当前位置: 首页 > 技术干货 > 对一道n1ctf赛题的详细分析

对一道n1ctf赛题的详细分析

发表于:2021-11-25 16:01 作者: rootkit 阅读数(1285人)

最近做了一道N1CTF2021的题目,学到了不少,分享给大家共同学习。

0x01 题目详情

题目如下:

1637574504174.png

源码如下:

<?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所在的文件中。

0x02 利用思路

整个过程如下:

1637570774492.png

我们可控的地方有path参数和time参数,time参数的内容最终会写入到name文件中。name文件会在每次请求的时候输出,我们不可控,但能够得知name文件的路径。path参数对应的文件名可控,但其文件内容不可控,而check文件名来源于path文件的内容,最终会读取并输出check文件的内容。

所以,我们要想读取/flag的内容,我们就要使check文件内容等于/flag,即可直接在网页中显示出/flag的内容。check文件名可通过path参数进行控制,而name文件内容我们可以通过time参数控制,所以,我们要想方法把/flag这个字符串写入name文件内容中,然后将check文件名设置为name文件即可。

0x03 利用过程

第一步,我们首先将/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,我在本机进行调试如下:

1637574303818.png

内容写入到time对应的txt文件中:

1637574352334.png

为什么写入的内容不是/flag?因为有date函数并结果urldecode,所以,改成如下即可:

1637574418885.png

查看对应的文件即为/flag:

1637574467842.png

而我们将path参数设置为time对应的txt文件名,那么check文件名就会被设置为/flag,最终读取输出flag。

按照本地测试的效果,首先传递time参数,将/flag写入文件,然后记下页面输出的txt文件路径:

1637573815817.png

然后再次请求时,指定path为txt文件路径/var/www/tmp/16375737971145120615.txt,得到flag内容:

1637573856778.png