-
-
[原创] 看雪 2022 KCTF 春季赛 第四题 飞蛾扑火
-
发表于: 2022-5-17 04:10 8419
-
访问 http://121.36.145.157:8044/
,查看源代码:
url.php?url=
一眼ssrf,file协议读文件:http://121.36.145.157:8044/url.php?url=file://127.0.0.1/var/www/html/url.php
直接访问 http://123.57.254.42/flag.php
提示 error ip
,需要从服务端去访问。
参考 https://toutiao.io/posts/kuvr3yy/preview 绕 parse_url
过滤:http://121.36.145.157:8044/url.php?url=123.57.254.42://127.0.0.1/../flag.php
得到flag:
(表面Web,实际Misc)
属于那种见过秒杀(真·可以按秒计数的),没见过打死也做不出来的题
(逆向题至少还可以花时间死磕,这类题真的无从下手)
从一血战队竟然花了47min之久来看,比起相信他们是花了几十分钟才现场搜索到相关资料,更倾向于相信他们12点时在吃饭没空看题,然后12点40分吃完回来,看了眼题然后秒杀
Web题,查看源代码是基本操作。本题的首页除了图片,额外的提示就是phpinfo.php
和url.php
的存在。
(回想起来,自己的第一步操作是把图片拉下来 binwalk
走一遍看有没有隐写……纯粹浪费时间)
首页用url.php?url=
加载图片的方式直白的点出了SSRF(Server-Side Request Forgery,服务端请求伪造),通过控制 url
参数肯定能获得一些重要的内部信息。
保守起见还是先看了一眼phpinfo:http://121.36.145.157:8044/phpinfo.php
,几个引起注意的地方:
Apache、PHP、cURL的版本都不算高,大概率存在一些 CVE(当然,没必要现在就考虑这些)
Server API 表明 PHP 以 mod_php 模式作为一个动态库直接加载进 Apache 进程中,那么 SSRF 打 PHP-FPM 获得 RCE 的套路在本题中不存在
Web的根目录是默认的/var/www/html
,所以 phpinfo.php
和 url.php
也在这个目录下
open_basedir 和 disable_functions 都是空,让人有强烈的获得 webshell 的冲动
(从题解来看,这些信息都没啥用)
关注点回到 url.php
,第一个目标是先获取到源码内容以进一步分析。
印象里ssrf常用file协议读文件,先构造一下:
代码中有一行 http://123.57.254.42/flag.php
,直接访问返回了 error ip
,结合ssrf能够猜到需要设法让服务器去访问此页面。(顺手试了一发XFF伪造发现不行)
现在思考的是如何绕过 parse_url
对 url
的过滤检查。搜索资料,很多文章都提到了 php parse_url
和 curl 对 url 的解析规则不同。
(自己构造需要相当的创造力,亦或者对二者的源码非常熟练;从做题角度还是寻找前人的工作最快捷)
绝大多数文章给出的都是用两个 @
的方法:http://u:p@123.57.254.42@127.0.0.1/flag.php
,然而却没有得到flag
(到目前为止是12点30分,题目放出后只过了30分钟就摸到了最后一步,没想到却被临门一脚卡了两天)
本地测试不需要配置web服务器,只需要装好php环境,把代码里的 _GET
改掉即可直接命令行启动。
失败的尝试(也是比较容易搜索到的方法):
总结下来,网上的技术文章虽然很多,但原创且具有时效性的着实难找。题目的php版本很低,但是curl的版本却不低,上面的尝试大都失败在curl_exec
中,因为新版的curl对url的解析更严谨了。
开始走偏路,因为 open_basedir 为空,用 file://
读了服务器的 /proc/net/tcp
文件找监听的端口(寄希望找到 redis
之类的服务打 RCE),发现只有web服务在监听,基本杜绝了获得 shell 的可能
php 7.3.11 版本过旧,能找到很多公开的 CVE,但在题目的苛刻条件下也无法利用
眼见提交人数达到两位数,基本可以确定题目的破解方式一定是最原始的简单ssrf,而且是可以公开搜索到 POC 的。
思路转回,开始不断变换关键字搜索各种可能相关的文章(Misc题的既视感),直到在搜索结果不起眼的位置发现了 浅谈 URL 协议 这篇,作者 香草
。
(既然是出题人写的文章,那答案几乎一定就在里面)
(另一启示:搜索解法时带上出题人的名字,第一个结果就是这篇文章)
3.2部分给出了一种其他多数文章没有提到的构造方法:
按照这种方法构造出 123.57.254.42://127.0.0.1/../flag.php
,然后作为url
参数请求url.php
:
终于获得的flag
(p.s. 本地 curl 命令行测试失败,不清楚具体原因。既然题已经做出来了,
那安心等待结束,学习其他人的writeup吧)
<html>
<head>
<meta charset
=
"utf-8"
>
<title>欢迎挑战 Design by 香草<
/
title>
<
/
head>
<body>
<!
-
-
phpinfo.php
-
-
>
<img src
=
"url.php?url=https://ctf.pediy.com/upload/team/762/team236762.png"
>
<
/
body>
<
/
html>
<html>
<head>
<meta charset
=
"utf-8"
>
<title>欢迎挑战 Design by 香草<
/
title>
<
/
head>
<body>
<!
-
-
phpinfo.php
-
-
>
<img src
=
"url.php?url=https://ctf.pediy.com/upload/team/762/team236762.png"
>
<
/
body>
<
/
html>
<?php
function curl_request($url, $data
=
null, $method
=
'get'
, $header
=
array(
"content-type: application/json"
), $https
=
true, $timeout
=
5
){
$method
=
strtoupper($method);
$ch
=
curl_init();
/
/
初始化
curl_setopt($ch, CURLOPT_URL, $url);
/
/
访问的URL
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
/
/
只获取页面内容,但不输出
if
($https){
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
/
/
https请求 不验证证书
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
/
/
https请求 不验证HOST
}
if
($method !
=
"GET"
) {
if
($method
=
=
'POST'
){
curl_setopt($ch, CURLOPT_POST, true);
/
/
请求方式为post请求
}
if
($method
=
=
'PUT'
|| strtoupper($method)
=
=
'DELETE'
) {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
/
/
设置请求方式
}
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
/
/
请求数据
}
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
/
/
模拟的header头
/
/
curl_setopt($ch, CURLOPT_HEADER, false);
/
/
设置不需要头信息
$result
=
curl_exec($ch);
/
/
执行请求
curl_close($ch);
/
/
关闭curl,释放资源
return
$result;
}
$url
=
$_GET[
"url"
];
$uu
=
parse_url($url);
$host
=
isset($uu[
"host"
])?$uu[
"host"
]:"";
$scheme
=
isset($uu[
"scheme"
])?$uu[
"scheme"
]:"";
if
(empty($host)){
die(
"host is null"
);
}
if
(empty($scheme)){
die(
"scheme is null"
);
}
/
/
https:
/
/
ctf.pediy.com
/
upload
/
team
/
762
/
team236762.png?
if
($host
=
=
"ctf.pediy.com"
||$host
=
=
"127.0.0.1"
||$host
=
=
"localhost"
){
/
/
echo curl_request(
"http://123.57.254.42/flag.php"
,
"get"
,[],true,
5
);
/
/
get flag
echo curl_request($url,'',
"get"
,[],true,
5
);
}
else
{
die(
"host not allow"
);
}
?>
<?php
function curl_request($url, $data
=
null, $method
=
'get'
, $header
=
array(
"content-type: application/json"
), $https
=
true, $timeout
=
5
){
$method
=
strtoupper($method);
$ch
=
curl_init();
/
/
初始化
curl_setopt($ch, CURLOPT_URL, $url);
/
/
访问的URL
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
/
/
只获取页面内容,但不输出
if
($https){
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
/
/
https请求 不验证证书
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
/
/
https请求 不验证HOST
}
if
($method !
=
"GET"
) {
if
($method
=
=
'POST'
){
curl_setopt($ch, CURLOPT_POST, true);
/
/
请求方式为post请求
}
if
($method
=
=
'PUT'
|| strtoupper($method)
=
=
'DELETE'
) {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
/
/
设置请求方式
}
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
/
/
请求数据
}
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
/
/
模拟的header头
/
/
curl_setopt($ch, CURLOPT_HEADER, false);
/
/
设置不需要头信息
$result
=
curl_exec($ch);
/
/
执行请求
curl_close($ch);
/
/
关闭curl,释放资源
return
$result;
}
$url
=
$_GET[
"url"
];
$uu
=
parse_url($url);
$host
=
isset($uu[
"host"
])?$uu[
"host"
]:"";
$scheme
=
isset($uu[
"scheme"
])?$uu[
"scheme"
]:"";
if
(empty($host)){
die(
"host is null"
);
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课