对一道n1ctf赛题的详细分析
2021-11-26 16:17:06 Author: www.secpulse.com(查看原文) 阅读量:19 收藏

最近做了一道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,我在本机进行调试如下:

1.jpg

2.jpg

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

3.jpg

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

4.jpg

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

5.jpg

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

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

6.jpg

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

7get.jpg

本文作者:合天网安实验室

本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/170153.html


文章来源: https://www.secpulse.com/archives/170153.html
如有侵权请联系:admin#unsafe.sh