escapeshellarg绕过与参数注入漏洞
escapeshellarg绕过与参数注入漏洞
参数注入漏洞
在执行命令的时候,用户控制了命令中的某个参数,并通过一些危险的参数功能,达成攻击的目的。
举例
gitlist 0.6.0远程命令执行漏洞
在用户对仓库中代码进行搜索的时候,gitlist将调用git grep
命令
1 | <?php |
其中,$query
是搜索的关键字,$branch
是搜索的分支
如果用户输入的$query
的值是--open-files-in-pager=id;
,将可以执行id
命令
原因:
虽然–open-files-in-pager=id;被escapeshellarg处理过两边加上了单引号,但是它的位置在参数选项(option)的位置上,如果想要安全,不被命令执行,就应该被放在参数值的位置上
我们看一下漏洞里的指令是怎么执行的
但是如果换成
1 | git grep -i --line-number -e '--open-files-in-pager=id;' master |
不就无漏洞了吗
转到php的问题上
PHP特别准备了两个过滤函数:
- escapeshellcmd
- escapeshellarg
escapeshellcmd()
是用来处理整个命令字符串的,它会在特殊字符前添加反斜杠(\
)进行转义,让这些字符失去它们的命令意义。这些特殊字符包括;
,&
,|
等等
escapeshellarg()
主要是用来处理单个参数的,它会把参数包裹在单引号内并转义其中的单引号
二者分工不同,前者为了防止用户利用shell的一些技巧(如分号、反引号等),执行其他命令;
后者是为了防止用户的输入逃逸出“参数值”的位置,变成一个“参数选项”。
但是如果开发者在拼接命令的时候,将$传入参数
直接给拼接在“参数选项”的位置上,那用escapeshellarg也就没任何效果了
其他拓展
Java、Python等语言,执行命令的方法相对来说更加优雅:
1 | import subprocess |
默认情况下,python的subprocess接受的是一个列表。我们可以将用户输入的query放在列表的一项,这样也就避免了开发者手工转义query的工作,也能从根本上防御命令注入漏洞。但可惜的是,python帮开发者做的操作,也仅仅相当于是PHP中的escapeshellarg。我们可以试试令query等于--open-files-in-pager=id;
可见,仍然是存在参数注入漏洞的。原因还是例子中说的原因,把query放在了“参数选项”的位置上,无论怎么过滤,或者换成其他语言,都不可能解决问题。
举一反三(周四回来复现下)
参数注入的例子还比较多,因为大部分的开发者都能理解命令注入的原理,但处理了命令注入后,往往都会忽略参数注入的问题。
最典型是案例是Wordpress PwnScriptum漏洞,PHP mail函数的第五个参数,允许直接注入参数,用户通过注入-X
参数,导致写入任意文件,最终getshell。
另一个典型的例子是php-cgi CVE-2012-1823 ,在cgi模式中,用户传入的querystring将作为cgi的参数传给php-cgi命令。而php-cgi命令可以用-d参数指定配置项,我们通过指定auto_prepend_file=php://input
,最终导致任意代码执行。
客户端上也出现过类似的漏洞,比如Electron CVE-2018-1000006,我们通过注入参数--gpu-launcher=cmd.exe /c start calc
,来让electron内置的chromium执行任意命令。electron的最早给出的缓解措施也是在拼接点前面加上“–”。