谈escapeshellarg绕过与参数注入漏洞
字数 1182 2025-08-29 08:32:18
参数注入漏洞与escapeshellarg绕过详解
1. 参数注入漏洞概述
参数注入漏洞是指当执行命令时,用户控制了命令中的某个参数,并通过危险的参数功能达成攻击目的。与传统的命令注入不同,参数注入更侧重于利用合法命令的参数特性来实现攻击。
2. Gitlist 0.6.0漏洞案例分析
漏洞代码分析
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;
}
}
漏洞利用方式
攻击者可以输入--open-files-in-pager=id;作为$query的值,导致id命令被执行:
git grep -i --line-number --open-files-in-pager=id; master
escapeshellarg为何失效
escapeshellarg只是将输入用单引号包裹,确保其作为参数值而非命令的一部分。但当用户输入被直接拼接为参数选项时,过滤无效:
- 有效用法:
git grep -e '--open-files-in-pager=id;' master - 无效用法:
git grep '--open-files-in-pager=id;' master
3. 修复方案分析
方案一:使用-e参数(推荐)
"grep -i --line-number -e {$query} $branch"
这是git官方推荐的方案,-e参数明确表示下一个参数是模式(pattern),特别适用于用户输入可能以-开头的情况。
方案二:使用--参数分隔符
$query = preg_replace('/(--?[A-Za-z0-9\-]+)/', '', $query);
$query = escapeshellarg($query);
"grep -i --line-number -- {$query} $branch"
--表示选项结束,之后的内容都被视为参数而非选项。虽然有效,但preg_replace可能影响正常搜索功能。
4. 参数注入的普遍性
跨语言问题
不仅PHP存在此问题,其他语言如Python同样可能遭遇:
import subprocess
query = '--open-files-in-pager=id;'
r = subprocess.run(['git', 'grep', '-i', '--line-number', query, 'master'], cwd='/tmp/vulhub')
即使使用参数列表而非字符串拼接,仍然存在参数注入风险。
其他典型案例
- WordPress PwnScriptum漏洞:PHP mail函数的第五个参数允许注入参数,通过
-X参数实现任意文件写入 - php-cgi CVE-2012-1823:querystring作为CGI参数传递,可通过
-d指定配置项导致代码执行 - Electron CVE-2018-1000006:注入
--gpu-launcher=cmd.exe /c start calc参数导致命令执行
5. 防御措施
- 严格参数位置:确保用户输入只出现在参数值位置,而非选项位置
- 使用专用参数:如git的
-e参数明确标识用户输入 - 参数分隔符:在命令中使用
--分隔选项和参数 - 输入过滤:移除或转义输入中的潜在危险字符(如开头的
-) - 最小权限原则:降低执行命令的权限
- 使用更安全的API:如PHP的
proc_open配合参数数组
6. 总结
参数注入漏洞的核心问题是开发者错误地将用户输入放在了命令的选项位置而非参数值位置。防御的关键在于:
- 理解命令参数的结构和语义
- 正确使用语言提供的安全机制
- 遵循最小化、明确化的参数传递原则
通过本案例的分析,开发者应当提高对参数注入的认识,避免仅依赖简单的过滤函数而忽视命令构造的语义安全性。