SSRF的成因看具体的后端实现TCP请求的库:
比如curl、php里的file_get_contents、fsockopen、Java里的openStream,HttpClient类,URLConnection等
https://drops.org.cn/PENETRATION/SSRF-Gopher-Attack-NoteBook.html
发起请求的库不同,决定了可以发起什么样类型的请求。
orange发现的Github Enterprise的SSRF到RCE
试用地址:
https://enterprise.github.com/trial
利用在linux下0代表localhost的特性,
http://0/
攻击本地的Elasticsearch服务,关闭导致其拒绝服务。
curl http://0:9200/_shutdown/
然后找到了一个8000端口的Graphite服务,有一个直接信任用户提供的url然后访问的特性,利用这一点将之前的POST的SSRF转换成了GET型的SSRF。
然后又通过审计Graphite的代码发现其使用了httplib.HTTPConnection
来获取web资源,然后找到了这个功能的CR-LF注入。
然后现在的poc长这样:
http://0:8000/composer/send_email?
to=orange@nogg&
url=http://127.0.0.1:12345/%0D%0Ai_am_payload%0D%0AFoo:
然后实际发起的TCP请求是这样的:
GET /
i_am_payload
Foo: HTTP/1.1
Host: 127.0.0.1:12345
Accept-Encoding: identity
通过这个CR-LF注入,我们可以在普通的HTTP协议中走私其他TCP请求了!
最后利用了Memcached协议注入了一个序列化后的对象,等待Memcached进行反序列化执行任意命令!
总结来说就是:
在poc的url里体现就是:
Github的防御措施。这里的几个步骤都有防御,但是我学习到的是通过iptables防火墙过滤掉某种webhook的UA:
User-Agent: GitHub-Hookshot
演示视频:
https://www.youtube.com/watch?v=GoO7_lCOfic
利用gopher发起任意TCP请求(依赖curl)
gopher://127.0.0.1:70/_ + TCP/IP数据
默认是70端口,可以指定为其他端口
可以攻击Redis、抓包改包之后可以攻击mysql。
利用GoPher协议进行内网Redis和mysql的未授权操作(在服务端设置了未授权访问的情况下)
SSRF可以结合DNS重绑定
http://www.bendawang.site/2017/05/31/%E5%85%B3%E4%BA%8EDNS-rebinding%E7%9A%84%E6%80%BB%E7%BB%93/
对Redis进行DoS:
curl -v gopher://127.0.0.1:6379/_FLUSHALL --max-time 1
通过向Redis持续发送flushall命令,让它不能存储数据。
利用Gopher进行SSRF的局限性以及利用方式:
主要是利用发一次任意基于TCP协议的请求,进行利用,不能交互。
依赖后端实现TCP请求的库;
可能不支持302跳转;
可能不支持URLencode;
参考:
https://maxchadwick.xyz/blog/ssrf-exploits-against-redis
https://xz.aliyun.com/t/5844
https://drops.org.cn/PENETRATION/SSRF-Gopher-Attack-NoteBook.html
https://blog.szfszf.top/tech/%E5%88%A9%E7%94%A8gopher%E5%8D%8F%E8%AE%AE%E6%94%BB%E5%87%BB%E7%94%9F%E6%88%90gopher%E5%8D%8F%E8%AE%AEpayload/
https://k-ring.github.io/2019/05/31/%E5%AF%B9%E4%B8%87%E9%87%91%E6%B2%B9gopher%E5%8D%8F%E8%AE%AE%E7%9A%84%E7%90%86%E8%A7%A3%E4%B8%8E%E5%BA%94%E7%94%A8/
https://blog.chaitin.cn/gopher-attack-surfaces/
https://en.wikipedia.org/wiki/Gopher_(protocol)
攻防
使用java.net.URI#getHost,getPort等方法拿到实际要访问的host和port,若是内网IP或者域名,则禁止访问。
参考:
- https://www.leavesongs.com/PYTHON/defend-ssrf-vulnerable-in-python.html
- https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf
利用短网站服务等的302/301跳转。
解决方案:
- 设置allow_redirects=False,不允许目标进行跳转
- 每跳转一次,就检查一次新的Host是否是内网IP,直到抵达最后的网址
第一种情况明显是会影响业务的,只是规避问题而未解决问题。当业务上需要目标URL能够跳转的情况下,只能使用第二种方法了。
所以,归纳一下,完美解决SSRF漏洞的过程如下:
- 解析目标URL,获取其Host
- 解析Host,获取Host指向的IP地址
- 检查IP地址是否为内网IP
- 请求URL
- 如果有跳转,拿出跳转URL,执行1
通过网路层面的ACL,目标服务器仅能访问白名单中的主机,以及常用特定端口80,443.
SSRF的绕过技巧
参考:
https://portswigger.net/web-security/ssrf
- 攻击网络上其他主机或端口的(burp intruder扫描各种IP和port)
- 基于黑名单的:使用特殊IP格式(127.1,spoofed.burpcollaborator.net等解析到127的域名)
- 基于白名单的:使用#和@(及其两次编码形式)https://expected-host@evil-host,https://evil-host#expected-host
- 字符串大小写/编码两次
- 通过open redirection绕过SSRF防御
基于黑名单的绕过demo:
基于白名单的绕过demo:
结合open redirection(302跳转)
比如在“下一个产品”这种页面
找到path
这样的参数,存在302跳转(open redirection)
整个过程:
先找到stockApi这个传url参数的地方,但是发现不能指定其他IP或域名:
于是只能从本身应用找,能不能找到一个302跳转(open redirection),
这样就可以把本该在客户端重定向的请求,转换成在服务端重定向的请求了。
注意一定要url编码这个参数,保证这个参数是stockApi
字段的值,避免引起歧义:
SSRF利用工具
https://github.com/samhaxr/XXRF-Shots
SSRF 漏洞原理与实际案例介绍
对于 SSRF 的审计可以从 http 请求函数入手,这里提供一些审计函数,如下:
HttpClient.execute
HttpClient.executeMethod
HttpURLConnection.connect
HttpURLConnection.getInputStream
URL.openStream
HttpServletRequest
getParameter
URL
HttpClient
Request
HttpURLConnection
URLConnection
okhttp
BasicHttpEntityEnclosingRequest
DefaultBHttpClientConnection
BasicHttpRequest
URI
参考:
https://xz.aliyun.com/t/7186#toc-12