首页
社区
课程
招聘
[原创] 看雪 2022 KCTF 春季赛 第四题 飞蛾扑火
发表于: 2022-5-17 04:10 8419

[原创] 看雪 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.phpurl.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.phpurl.php 也在这个目录下
open_basedir 和 disable_functions 都是空,让人有强烈的获得 webshell 的冲动

(从题解来看,这些信息都没啥用)

关注点回到 url.php,第一个目标是先获取到源码内容以进一步分析。
印象里ssrf常用file协议读文件,先构造一下:

代码中有一行 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 改掉即可直接命令行启动。

失败的尝试(也是比较容易搜索到的方法):

总结下来,网上的技术文章虽然很多,但原创且具有时效性的着实难找。题目的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直播授课

最后于 2022-5-17 04:27 被mb_mgodlfyn编辑 ,原因:
收藏
免费 5
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//