最近做了一道N1CTF2021的题目,学到了不少,分享给大家共同学习。
题目如下:
源码如下:
<?php //flag is /flag $path=$_POST['path']; $time=(isset($_GET['time'])) ? urldecode(date(file_get_contents('php://input'))) : date("Y/m/d H:i:s"); $name="/var/www/tmp/".time().rand().'.txt'; $black="f|ht|ba|z|ro|;|,|=|c|g|da|_"; $blist=explode("|",$black); foreach($blist as $b){ if(strpos($path,$b) !== false){ • die(); } } if(file_put_contents($name, $time)){ • echo "<pre class='language-html'><code class='language-html'>logpath:$name</code></pre>"; } $check=preg_replace('/((\s)*(\n)+(\s)*)/i','',file_get_contents($path)); if(is_file($check)){ • echo "<pre class='language-html'><code class='language-html'>".file_get_contents($check). "</code></pre>"; }
页面下方输出的是日志文件。要拿到flag,我首先关注到下方的两个if语句,先看最下面这个:
if(is_file($check)){ echo "<pre class='language-html'><code class='language-html'>".file_get_contents($check)." </code></pre>"; }
如果$check是一个文件,那么读取他的内容,并输出。如果$check的值刚好是/flag,那这道题就解出来了。而$check是由这行代码来的:
$check=preg_replace('/((\s)*(\n)+(\s)*)/i','',file_get_contents($path));
此处使用了preg_replace函数,将$path文件内容里的换行空格等内容删除。而$path最初是通过POST请求提交的,之后进行了过滤,不能包含一些字符:
$black="f|ht|ba|z|ro|;|,|=|c|g|da|_"; $blist=explode("|",$black); foreach($blist as $b){ if(strpos($path,$b) !== false){ die(); }
除了POST请求提交path参数我们可以控制,另外一个参数time通过GET方式和伪协议的方式我们也可以控制:
$time=(isset($_GET['time'])) ? urldecode(date(file_get_contents('php://input'))) : date("Y/m/d H:i:s"); $name="/var/www/tmp/".time().rand().'.txt'; if(file_put_contents($name, $time)){ echo "<pre class='language-html'><code class='language-html'>logpath:$name</code></pre>";
而$time的内容会被写入$name所在的文件中。
整个过程如下:
我们可控的地方有path参数和time参数,time参数的内容最终会写入到name文件中。name文件会在每次请求的时候输出,我们不可控,但能够得知name文件的路径。path参数对应的文件名可控,但其文件内容不可控,而check文件名来源于path文件的内容,最终会读取并输出check文件的内容。
所以,我们要想读取/flag的内容,我们就要使check文件内容等于/flag,即可直接在网页中显示出/flag的内容。check文件名可通过path参数进行控制,而name文件内容我们可以通过time参数控制,所以,我们要想方法把/flag这个字符串写入name文件内容中,然后将check文件名设置为name文件即可。
第一步,我们首先将/flag字符串写入time文件。
程序通过如下代码获得time的值:
$time=(isset($_GET['time'])) ? urldecode(date(file_get_contents('php://input'))) : date("Y/m/d H:i:s");
其中涉及到php的伪协议php://input,它可以读取我们POST请求的内容,time参数如果被设置,同时有POST数据,POST数据将会赋值给$time,我在本机进行调试如下:
内容写入到time对应的txt文件中:
为什么写入的内容不是/flag?因为有date函数并结果urldecode,所以,改成如下即可:
查看对应的文件即为/flag:
而我们将path参数设置为time对应的txt文件名,那么check文件名就会被设置为/flag,最终读取输出flag。
按照本地测试的效果,首先传递time参数,将/flag写入文件,然后记下页面输出的txt文件路径:
然后再次请求时,指定path为txt文件路径/var/www/tmp/16375737971145120615.txt,得到flag内容:
本文作者:合天网安实验室
本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/170153.html