Unc1e
Unc1e
SSRF服务端请求伪造漏洞原理及修复

SSRF服务端请求伪造漏洞原理及修复

本文站在安全工程师的角度,分析了PHP中SSRF的成因及修复方案。通过在vps上作实验,介绍不同成因导致的SSRF漏洞的危害和限制,并在最后给出安全编码的示例。

https://cdn.nlark.com/yuque/0/2020/png/166008/1581432420756-90e01855-46d9-4621-ba1d-e196bdfa3bc8.png#align=left&display=inline&height=513&margin=%5Bobject%20Object%5D&originHeight=513&originWidth=847&size=0&status=done&style=none&width=847#align=left&display=inline&height=513&margin=%5Bobject%20Object%5D&originHeight=513&originWidth=847&status=done&style=shadow&width=847

cURL的配置项

此处记录了cURL的各项配置和SSRF漏洞类型之间的关系.

  • CURLOPT_HEADER 绝大多数情况下都是0,否则会连同HTTP响应头一起返回(如图
图片.png
  • CURLOPT_NOBODY 启用时将不对HTML中的BODY部分进行输出,若关闭则有回显决定是否为无回显布尔型SSRF的因素
image.png
  • CURLOPT_PORT 用来指定默认连接端口。决定SSRF是否被限制端口的因素。多个端口:用-表示端口范围,用逗号,指定多个端口

  • CURLOPT_PROTOCOLS 限定在传输过程中可使用的协议。这将允许你在编译libcurl时支持众多协议,默认将会使用全部它支持的协议。**决定SSRF是否被限制协议的因素. **示例代码为curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP);, 多个协议用竖线|分隔. 下面是一些常用的协议, 如需完整的协议列表, 可访问https://curl.haxx.se/libcurl/c/CURLOPT_PROTOCOLS.html

需要注意的是, 如果CURLOPT_PROTOCOLS配置只允许HTTP协议的话, 那么是无法通过302跳转来bypass的!
也就是说302跳转只适用于关键词过滤时的绕过. 参考本文例3中的重定向型SSRF

  • CURLOPT_RETURNTRANSFERcurl_exec获取的信息以文件流的形式返回,而不是直接输出。ps: 有网友说这项配置会导致ssrf无回显,经测试并非如此——置0置1对有无输出均无影响,有无输出由CURLOPT_NOBODY决定 ;实际上,这个配置项主要跟CURLOPT_BINARYTRANSFER一起使用)
  • CURLOPT_FOLLOWLOCATION 启用时会将服务器服务器返回的”Location: “放在header中递归的返回给服务器,使用CURLOPT_MAXREDIRS可以限定递归返回的数量。是否允许SSRF跳转的决定因素. 默认为False即不支持跳转
  • CURLOPT_TIMEOUT 设置cURL允许执行的最长秒数。
  • CURLOPT_TIMEOUT_MS 设置cURL允许执行的最长毫秒数。
  • CURLOPT_CONNECTTIMEOUT 在发起连接前等待的时间,如果设置为0,则无限等待。
  • CURLOPT_CONNECTTIMEOUT_MS 尝试连接等待的时间,以毫秒为单位。如果设置为0,则无限等待
  • CURLOPT_CUSTOMREQUEST 使用一个自定义的请求信息来代替”GET”或”HEAD”作为HTTP请求。常用值如”GET”,”POST”,”CONNECT”等等。

本文的代码可在

成因1—cURL

image.png

某天审一套野生cms的时候看到如上的PHP代码,curl的配置项比较多,怀疑是否存在ssrf漏洞,也正好想研究ssrf,所以对什么样的curl配置会导致ssrf这一点很感兴趣,于是展开了本次研究

0x01    回显型SSRF

实例代码

**

0x02    布尔型SSRF

示例代码

这就是bool型SSRF, 返回值永远只有True or False.
True, 即有响应内容时: 返回1
False, 即无响应内容时:返回空
不妨试试, 在上面实例代码的情况下, 使用file协议仍然可以读取文件。

图片.png

这是由于CURLOPT_NOBODY选项真正的作用是: 采用**HEAD****方法来请求网络资源. **就像下面cURL文档里说的一样
图片.png

0x03    支持重定向的SSRF

为了演示302跳转脚本的作用, 我将cURL限制为只能请求以http://开始的地址, 实例代码如下

此时: 可以通过302.php脚本来实现协议的”转换”, 示例脚本如下。

注意:使用header()函数时,在<?php标签前不能有内容,否则会提示报错Warning: Cannot modify header information - headers already sent by ..., 响应头也不能正常发送。

最后,经测试,302跳转可实现HTTP=>DICT, HTTP=>GOPHER,但不支持转成FILE协议。

图片.png

图片.png

成因2— file_get_contents

来看看官网对这个函数的说明,支持读取以下形式的内容

  • /path/to/file.ext
  • relative/path/to/file.ext
  • fileInCwd.ext
  • C:/path/to/winfile.ext
  • C:\path\to\winfile.ext
  • \smbserver\share\path\to\winfile.ext
  • file:///path/to/file.ext

且在开启allow_url_fopen的情况下(默认开启),支持ftphttp协议
这里也是给出示例代码:

当请求内网的redis服务时,报错开启的话会显示banner

image.png

当请求内网的SSH服务时也是
image.png

不知为啥,请求内网的MySQL就不行。。
相较于curl,感觉file_get_contents要收敛很多,不支持dict / gopher协议,更不支持302跳转
image.png

主要功能点在于内网的http服务扫描、本地文件的读取以及有限制的内网服务扫描(开启报错情况下的banner回显),总体来说更偏向于读取文件一些,例如下面读读PHP代码之类的。
image.png

说到读取PHP源代码,不得不提到一个东西叫做php伪协议:简单来说就是用php过滤器来将输出的内容编码,常用的编码方式之一是base64

下面这张图,就是采用base64编码之后的结果。在过滤了回显关键字的时候有奇效

image.png

成因3— fopen/fsockopen

fopen

示例代码

支持file://协议,默认支持通外网,因此用来内网web服务探测也是妥妥的。

图片.png

可惜的是:不支持POST请求、不支持DICTGOPHER协议。当然配合上传点,phar反序列化还是很香的。

fsockopen

image.png

乏善可陈…

常见服务支持的协议

| 服务类型 | 支持的协议 | 实例命令 | 响应内容 |
| — | — | — | — |
| Redis | dict | dict://redis:6379/ | -ERR Syntax error, try CLIENT (LIST | KILL | GETNAME | SETNAME | PAUSE | REPLY) +OK string(0) "" 1 |
| | http | http://redis:6379/ | -ERR wrong number of arguments for 'get' command string(0) "" 1 |
| | gopher | gopher://redis:6379/_info | 返回redis执行info命令后的内容 |
| SSH | dict
http
gopher | http://172.17.0.1:22
… | SSH-2.0-OpenSSH_7.4 Protocol mismatch. |
| MySQL | dict
http
gopher
telnet | dict://mysql:3306
… | J5.7.32q@Y60l����W{_lfD.5mysql_native_password!��#08S01Got packets out of order1 |
| HTTP | dict
http | 略 | 略 |

注意:
1. 使用dict协议, 可以直接在redis上执行命令, 如dict://redis:6379/info, 就是执行了info命令

图片.png

修复建议

对于不同的需求,有不同的配置建议,但总的还是采用【白名单】的思路。

如果是为了防止SSRF进内网的话,可以通过限制协议+端口+白名单地址范围来彻底杜绝SSRF,例如下面的实例代码就是较为安全的(限制白名单地址+不输出http头+限制http(s)协议).

附录: cURL 7.61.1支持的协议

可用于buzz

首页      WEB安全      SSRF服务端请求伪造漏洞原理及修复

发表评论

textsms
account_circle
email

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

Unc1e

SSRF服务端请求伪造漏洞原理及修复
某天审一套野生cms的时候看到如上的PHP代码,curl的配置项比较多,怀疑是否存在ssrf漏洞,也正好想研究ssrf,所以对**什么样的curl配置会导致ssrf**这一点很感兴趣,于是展开了本次研究
扫描二维码继续阅读
2020-11-16
%d 博主赞过: