正文
参数注入漏洞是指,在执行命令的时候,用户控制了命令中的某个参数,并通过一些危险的参数功能,达成攻击的目的。
我们从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
命令:
导致这个漏洞的原因,有几点:
-
开发者对于
escapeshellarg
函数的误解,造成参数注入
-
git grep
的参数
--open-files-in-pager
的值,将被直接执行
理论上,在经过
$query = escapeshellarg($query);
处理后,
$query
将变成一个由单引号包裹的字符串。但不出漏洞的前提是,这个字符串应该出现在“参数值”的位置,而不是出现在参数选项(option)中。
我们可以试一下如下命令:
git grep -i --line-number -e '--open-files-in-pager=id;' master
如上图,我将
$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.