SSRF漏洞
SSRF(Server Side Request Forgery,服务端请求伪造)是一种攻击者通过构造数据进而伪造服务器端发起请求的漏洞。因为请求是由内部发起的,所以一般情况下,SSRF漏洞攻击的目标往往是从外网无法访问的内部系统。
SSRF漏洞形成的原因多是服务端提供了从外部服务获取数据的功能,但没有对目标地址、协议等重要参数进行过滤和限制,从而导致攻击者可以自由构造参数,而发起预期外的请求。
SSRF的原理解析URL的结构如下:
URI = scheme:[//authority]path[?query][#fragment]
authority组件又分为以下3部分
scheme:由一串大小写不敏感的字符组成,表示获取资源所需要的协议。
authority:认证,userinfo遇到得比较少,这是一个可选项,一般HTTP使用匿名形式来获取数据,如果需要进行身份验证,格式为username:password,以@结尾。
host:表示在哪个服务器上获取资源,一般所见的是以域名形式呈现的,如baidu.com,也有以IPv4、IPv6地址形式呈现的。
port:服务器端口。各协议都有默认端口,如HTTP的为80、FTP的为21。使用默认端口时,可以将端口省略。
path:指向资源的路径,一般使用“/”进行分层。
query:查询字符串,用户将用户输入数据传递给服务端,以“?”作为表示。例如,向服务端传递用户名密码为?username=admin&password=admin123
。
fragment:片段ID,与query不同的是,其内容不会被传递到服务端,一般用于表示页面的锚点。
SSRF漏洞的寻找和测试
SSRF漏洞一般出现在有调用外部资源的场景中,如社交服务分享功能、图片识别服务、网站采集服务、远程资源请求(如wordpress xmlrpc.php)、文件处理服务(如XML解析)等。
在对存在SSRF漏洞的应用进行测试的时候,可以尝试是否能控制、支持常见的协议,包括但不限于以下协议。
❖ file://:从文件系统中获取文件内容,如file:///etc/passwd。
❖ dict://:字典服务器协议,让客户端能够访问更多字典源。在SSRF中可以获取目标服务器上运行的服务版本等信息。
❖ gopher://:分布式的文档传递服务,在SSRF漏洞攻击中发挥的作用非常大。使用Gopher协议时,通过控制访问的URL可实现向指定的服务器发送任意内容,如HTTP请求、MySQL请求等,所以其攻击面非常广。
SSRF漏洞攻击方式
内部服务资产探测
SSRF漏洞可以直接探测网站所在服务器端口的开放情况甚至内网资产情况,如确定该处存在SSRF漏洞,则可以通过确定请求成功与失败的返回信息进行判断服务开放情况。例如,使用Python语言写一个简单的利用程序。
# encoding: utf-8
import requests as req
import time
ports = ['80','3306','6379','8080','8000']
session = req.Session()
for i in range(255):
ip = '10.160.32.%d' % i
for port in ports:
url = 'http://example.com/?url=http://%s:%s' % (ip,port)
try:
res = session.get(url,timeout=3)
if len(res.content) > 0:
print(ip,port,'is open')
except Exception as e:
continue
print('DONE')
使用Gopher协议扩展攻击面
1.攻击Redis
Redis一般运行在内网,使用者大多将其绑定于127.0.0.1:6379,且一般是空口令。攻击者通过SSRF漏洞未授权访问内网Redis,可能导致任意增、查、删、改其中的内容,甚至利用导出功能写入Crontab、Webshell和SSH公钥(使用导出功能写入的文件所有者为redis的启动用户,一般启动用户为root,如果启动用户权限较低,将无法完成攻击)。Redis是一条指令执行一个行为,如果其中一条指令是错误的,那么会继续读取下一条,所以如果发送的报文中可以控制其中一行,就可以将其修改为Redis指令,分批执行指令,完成攻击。如果可以控制多行报文,那么可以在一次连接中完成攻击。
2.攻击MySQL
攻击内网中的MySQL,我们需要先了解其通信协议。MySQL分为客户端和服务端,由客户端连接服务端有4种方式:UNIX套接字、内存共享、命名管道、TCP/IP套接字。我们进行攻击依靠第4种方式,MySQL客户端连接时会出现两种情况,即是否需要密码认证。当需要进行密码认证时,服务器先发送salt,然后客户端使用salt加密密码再验证。当不需进行密码认证时,将直接使用第4种方式发送数据包。所以,在非交互模式下登录操作MySQL数据库只能在空密码未授权的情况下进行。
3.PHP-FPM
攻击利用条件如下:Libcurl,版本高于7.45.0;PHP-FPM,监听端口,版本高于5.3.3;知道服务器上任意一个PHP文件的绝对路径。首先,FastCGI本质上是一个协议,在CGI的基础上进行了优化。
PHP-FPM是实现和管理FastCGI的进程。在PHP-FPM下如果通过FastCGI模式,通信还可分为两种:TCP和UNIX套接字(socket)。TCP模式是在本机上监听一个端口,默认端口号为9000,Nginx会把客户端数据通过FastCGI协议传给9000端口,PHP-FPM拿到数据后会调用CGI进程解析。
4.攻击内网中的脆弱
Web应用内网中的Web应用因为无法被外网的攻击者访问到,所以往往会忽视其安全威胁。假设内网中存在一个任意命令执行漏洞的Web应用,在本地监听任意端口,然后对此端口发起一次POST请求,以抓取请求数据包,将其改成Gopher协议的URL,改变规则同上。
自动组装Gopher
目前已经有人总结出多种协议并写出自动转化的脚本,所以大部分情况下不需要再手动进行抓包与转换。
推荐工具 https://github.com/tarunkant/Gopherus
SSRF的绕过
IP的限制
使用Enclosed alphanumerics代替IP中的数字或网址中的字母,或者使用句号代替点。
如果服务端过滤方式使用正则表达式过滤属于内网的IP地址,那么可以尝试将IP地址转换为进制的方式进行绕过,如将127.0.0.1转换为十六进制后进行请求。
可以将IP地址转换为十进制、八进制、十六进制,分别为2130706433、17700000001、7F000001。在转换后进行请求时,十六进制前需加0x,八进制前需加0,转换为八进制后开头所加的0可以为多个。
另外,IP地址有一些特殊的写法,如在Windows下,0代表0.0.0.0,而在Linux下,0代表127.0.0.1,见图2-1-23。所以,某些情况下可以用http://0进行请求127.0.0.1。类似127.0.0.1这种中间部分含有0的地址,可以将0省略。
302跳转
网络上存在一个名叫xip.io的服务,当访问这个服务的任意子域名时,都会重定向到这个子域名,如127.0.0.1.xip.io
,这种方式可能存在一个问题,即在传入的URL中存在关键字127.0.0.1,一般会被过滤,那么,我们可以使用短网址将其重定向到指定的IP地址,如短网址http://dwz.cn/11SMa
。
有时服务端可能过滤了很多协议,如传入的URL中只允许出现“http”或“https”,那么可以在自己的服务器上写一个302跳转,利用Gopher协议攻击内网的Redis。
URL的解析问题
如果传入的URL为http://a@127.0.0.1:80@baidu.com
,那么进入safe_request_url后,parse_url取到的host其实是baidu.com
,而curl取到的是127.0.0.1:80,所以实现了检测IP时是正常的一个网站域名而实际curl请求时却是构造的127.0.0.1,以此实现了SSRF攻击。
除了PHP,不同语言对URL的解析方式各不相同,进一步了解可以参考:https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf。
DNS Rebinding
在某些情况下,针对SSRF的过滤可能出现下述情况:通过传入的URL提取出host,随即进行DNS解析,获取IP地址,对此IP地址进行检验,判断是否合法,如果检测通过,则再使用curl进行请求。那么,这里再使用curl请求的时候会做第二次请求,即对DNS服务器重新请求,如果在第一次请求时其DNS解析返回正常地址,第二次请求时的DNS解析却返回了恶意地址,那么就完成了DNS Rebinding攻击。
DNS重绑定的攻击首先需要攻击者自己有一个域名,通常有两种方式。
第一种是绑定两条记录。这时解析是随机的,但不一定会交替返回。所以,这种方式需要一定的概率才能成功。
第二种方式则比较稳定,自己搭建一个DNS Server,在上面运行自编的解析服务,使其每次返回的都不同。
Redis的主从模式
Redis为了应对读写量较大的问题,提供了一种主从模式,使用一个Redis实例作为主机只负责写,其余实例都为从机,只负责读,主从机间数据相同,其次在Redis 4.x后新增加了模块的功能,通过外部的扩展可以实现一条新的Redis命令,因为此时已经完全控制了Redis,所以可以通过将此机设置为自己VPS的从机,在主机上通过FULLSYNC同步备份一个恶意扩展到从机上加载。
在Github上可以搜到关于该攻击的exp,如https://github.com/n0b0dyCN/redis-rogue-server。