前言
本文将深入研究 preg_replace /e 模式下的代码执行问题,其中包括 preg_replace 函数的执行过程分析、正则表达式分析、漏洞触发分析,当中的坑非常多,相信看完本文,你一定会有所收获。下面是 七月火 和 l1nk3r 的分析结果。
案例
下面先看一个案例,思考如何利用此处的 preg_replace /e 模式,执行代码(可以先不看下文分析,自己思考出 payload 试试)。
这个案例实际上很简单,就是 preg_replace 使用了 /e 模式,导致可以代码执行,而且该函数的第一个和第三个参数都是我们可以控制的。我们都知道, preg_replace 函数在匹配到符号正则的字符串时,会将替换字符串(也就是上图 preg_replace 函数的第二个参数)当做代码来执行,然而这里的第二个参数却固定为 'strtolower("//1")' 字符串,那这样要如何执行代码呢?
爬坑1
上面的命令执行,相当于 eval('strtolower("//1");')
结果,当中的 //1 实际上就是 /1 ,而 /1 在正则表达式中有自己的含义。我们来看看 W3Cschool 中对其的描述:
反向引用
对一个正则表达式模式或部分模式 两边添加圆括号 将导致相关 匹配存储到一个临时缓冲区 中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 '/n' 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。
所以这里的 /1 实际上指定的是第一个子匹配项,我们拿 ripstech 官方给的 payload 进行分析,方便大家理解。官方 payload 为: /?.*={${phpinfo()}}
,即 GET 方式传入的参数名为 /?.* ,值为 {${phpinfo()}}
。
原先的语句: preg_replace('/(' . $regex . ')/ei', 'strtolower("//1")', $value);
变成了语句: preg_replace('/(.*)/ei', 'strtolower("//1")', {${phpinfo()}});
爬坑2
上面的 preg_replace 语句如果直接写在程序里面,当然可以成功执行 phpinfo() ,然而我们的 .* 是通过 GET 方式传入,你会发现无法执行 phpinfo 函数,如下图:
我们 var_dump 一下 $_GET 数组,会发现我们传上去的 .* 变成了 _* ,如下图所示:
新闻热点
疑难解答