谈escapeshellarg绕过与参数注入漏洞

参数注入漏洞是指,在执行命令的时候,用户控制了命令中的某个参数,并通过一些危险的参数功能,达成攻击的目的。

0x01 从gitlist 0.6.0远程命令执行漏洞说起

我们从gitlist说起,gitlist是一款使用PHP开发的图形化git仓库查看工具。在其0.6.0版本中,存在一处命令参数注入问题,可以导致远程命令执行漏洞。

在用户对仓库中代码进行搜索的时候,gitlist将调用git grep命令:

<?php
public function searchTree($query, $branch)
{if (empty($query)) {return null;}$query = escapeshellarg($query);try {$results = $this->getClient()->run($this, "grep -i --line-number {$query} $branch");} catch (\\RuntimeException $e) {return false;}
​

其中,$query是搜索的关键字,$branch是搜索的分支。

如果用户输入的$query的值是--open-files-in-pager=id;,将可以执行id命令:

[<img src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/5/3/16325a2ccfd14c8d~tplv-t2oaga2asx-zoom-in-crop-mark:4536:0:0:0.image)](跳转提示-稀土掘金 "https://cdn.ioin.in/media/attachment/2018/04/29/dd89dc70-2446-48e1-a728-5693d1fa868a.png"" style="margin: auto" />

0x02 escapeshellarg为什么没有奏效?

导致这个漏洞的原因,有几点:

1.开发者对于escapeshellarg函数的误解,造成参数注入 2.git grep的参数--open-files-in-pager的值,将被直接执行

理论上,在经过$query = escapeshellarg($query);处理后,$query将变成一个由单引号包裹的字符串。但不出漏洞的前提是,这个字符串应该出现在“参数值”的位置,而不是出现在参数选项(option)中。

我们可以试一下如下命令:

git grep -i --line-number -e '--open-files-in-pager=id;' master
​

[<img src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/5/3/16325a2cce81c632~tplv-t2oaga2asx-zoom-in-crop-mark:4536:0:0:0.image)](跳转提示-稀土掘金 "https://cdn.ioin.in/media/attachment/2018/04/29/840cca46-5780-4713-bf2a-319f296368b7.png"" style="margin: auto" />

如上图,我将$query放在了-e参数的值的位置,此时它就仅仅是一个字符串而已,并不会被当成参数--open-files-in-pager

这应该作为本漏洞的最佳修复方法,也是git官方对pattern可能是用户输入的情况的一种解决方案(以下说明来自man-page):

-e> The next parameter is the pattern. This option has to be used for patterns starting with - and should be used in scripts passing user input to grep. Multiple patterns are combined by> or.

当然,gitlist的开发者用了另一种修复方案:

<?php
public function searchTree($query, $branch)
{if (empty($query)) {return null;}$query = preg\_replace('/(--?\[A-Za-z0-9\\-\]+)/', '', $query);$query = escapeshellarg($query);try {$results = $this->getClient()->run($this, "grep -i --line-number -- {$query} $branch");} catch (\\RuntimeException $e) {return false;}
​

首先用preg_replace-开头的非法字符移除,然后将$query拼接在--的后面。

在命令行解析器中,--的意思是,此后的部分不会再包含参数选项(option):

A -- signals the end of options and disables further option processing. Any arguments after the -- are treated as filenames and arguments. An argument of - is equivalent to --.

If arguments remain after option processing, and neither the -c nor the -s option has been supplied, the first argument is assumed to be the name of a file containing shell commands. If bash is invoked in this fashion, $0 is set to the name of the file, and the positional parameters are set to the remaining arguments. Bash reads and executes commands from this file, then exits. Bash's exit status is the exit status of the last command executed in the script. If no commands are executed, the exit status is 0. An attempt is first made to open the file in the current directory, and, if no file is found, then the shell searches the directories in PATH for the script.

举个简单的例子,如果我们需要查看一个文件名是--name的文件,我们就不能用cat --name来读取,也不能用cat '--name',而必须要用cat -- --name。从这个例子也能看出,单引号并不是区分一个字符串是“参数值”或“选项”的标准。

[<img src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/5/3/16325a2cce0d9cf6~tplv-t2oaga2asx-zoom-in-crop-mark:4536:0:0:0.image)](跳转提示-稀土掘金 "https://cdn.ioin.in/media/attachment/2018/04/29/d70ec825-c4fc-4874-88b7-d4c8bbebfd70.png"" style="margin: auto" />

所以官方这个修复方案也是可以接受的,只不过第一步的preg_replace有点影响正常搜索功能。

0x03 这不是PHP的专利

熟悉PHP语言的同学一定对PHP执行命令的方法感受深刻,PHP内置的命令执行函数(如shell_execsystem),都只接受一个“字符串”作为参数。而在内核中,这个字符串将被直接作为一条shell命令来调用,这种情况下就极为容易出现命令注入漏洞。

由于这个特点,PHP特别准备了两个过滤函数:

  • escapeshellcmd

  • escapeshellarg

二者分工不同,前者为了防止用户利用shell的一些技巧(如分号、反引号等),执行其他命令;后者是为了防止用户的输入逃逸出“参数值”的位置,变成一个“参数选项”。

但我在0x02中也已经说清楚了,如果开发者在拼接命令的时候,将$query直接给拼接在“参数选项”的位置上,那用escapeshellarg也就没任何效果了。

Java、Python等语言,执行命令的方法相对来说更加优雅:

import subprocess
​
query = 'id'
r = subprocess.run(\['git', 'grep', '-i', '--line-number', query, 'master'\], cwd='/tmp/vulhub')
​

默认情况下,python的subprocess接受的是一个列表。我们可以将用户输入的query放在列表的一项,这样也就避免了开发者手工转义query的工作,也能从根本上防御命令注入漏洞。但可惜的是,python帮开发者做的操作,也仅仅相当于是PHP中的escapeshellarg。我们可以试试令query等于--open-files-in-pager=id;

[<img src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/5/3/16325a2cd013ff4b~tplv-t2oaga2asx-zoom-in-crop-mark:4536:0:0:0.image)](跳转提示-稀土掘金 "https://cdn.ioin.in/media/attachment/2018/04/29/8296bbd4-40cd-481e-9147-6f11e80967d8.png"" style="margin: auto" />

可见,仍然是存在参数注入漏洞的。原因还是0x02中说的原因,你把query放在了“参数选项”的位置上,无论怎么过滤,或者换成其他语言,都不可能解决问题。

0x04 举一反三

参数注入的例子还比较多,因为大部分的开发者都能理解命令注入的原理,但处理了命令注入后,往往都会忽略参数注入的问题。

最典型是案例是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的最早给出的缓解措施也是在拼接点前面加上“--”。

学习计划安排

我一共划分了六个阶段,但并不是说你得学完全部才能上手工作,对于一些初级岗位,学到第三四个阶段就足矣~

这里我整合并且整理成了一份【282G】的网络安全从零基础入门到进阶资料包,需要的小伙伴可以扫描下方CSDN官方合作二维码免费领取哦,无偿分享!!!

①网络安全学习路线 ②上百份渗透测试电子书 ③安全攻防357页笔记 ④50份安全攻防面试指南 ⑤安全红队渗透工具包 ⑥HW护网行动经验总结 ⑦100个漏洞实战案例 ⑧安全大厂内部视频资源 ⑨历年CTF夺旗赛题解析

点击链接即可免费领取:网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值