XML 如今不像十年前那样到处当主数据格式,可它从没真正离开:办公文档、SOAP、SAML、一大堆企业集成、支付回调、日志采集、老 RPC、安全产品自己的规则文件......只要服务端还在"把用户可控的 XML 喂给解析器",XXE(XML External Entity,XML 外部实体) 就还可能从历史书里坐起来。
XXE 可怕的地方在于,它常常一次性打开好几扇门:读本地文件、打内网(SSRF 近亲)、在盲环境下靠 OOB(Out-of-Band,带外) 把数据带回家。很多开发只听说过"关掉外部实体",却说不清 DTD、实体、参数实体、盲 XXE 和带外各自在干什么------出了事只能干瞪眼。
一、先补一块 XML 基础(不然 XXE 永远像黑话)
1. 实体是什么
XML 允许定义"实体",有点像宏:解析时展开成别的内容。
内部实体在文档里定义;外部实体则可以指向外部资源(URI)------解析器去取这个资源,再塞回文档树。
XXE 的关键就在这里:若解析器允许外部实体,而 XML 文档内容又来自用户,攻击者就能写一个外部实体,让你的服务器 去读 file:///... 或请求 http://内部地址/...。
2. DTD 与 DOCTYPE
文档类型定义(DTD)里可以声明实体。很多 XXE 都出现在用户可控的 <!DOCTYPE ...> 区域。
现代有人用 XSD,但只要解析器仍处理 DOCTYPE/外部实体,风险就在。
3. 解析器默认值是魔鬼
安全问题很少出在"XML 不行",而出在"某语言某库的默认解析器太好心 "------自动加载外部 DTD、解析外部实体、跟随网络。
换安全默认值,往往比在业务里做字符串过滤靠谱一百倍。
二、XXE 能造成什么危害
1. 任意文件读取
外部实体指向本地文件 URI,解析结果若回显到响应里,攻击者可读到应用能读的文件:配置、密钥、云凭证路径、源代码等(权限取决于进程身份)。
2. SSRF 式请求
外部实体指向内网 HTTP 服务或云元数据地址,效果类似 SSRF:借服务器网络位置去探内网、碰元数据。
与本系列 SSRF 文同一类后果,入口换成了 XML 解析器。
3. 拒绝服务
经典"亿笑攻击"(Billion Laughs)用嵌套实体把内存撑爆------偏 DoS,不是读文件,但同属危险实体展开。生产要限实体展开深度与大小。
4. 在特定生态里的升级
历史上与某些组件结合可导致更严重后果(视平台而定)。评估时按"读文件 + SSRF + DoS"打底,再看组件版本。
5. 盲 XXE
解析出错或结果不回显时,仍可能通过错误信息差异、时间、或 OOB 带外 证实与利用。盲,不代表无害。
三、经典"有回显"XXE 的理解方式
教学靶场常见路径:
- 找接受 XML 的接口(上传 xml、SOAP、导入、回调);
- 提交带 DOCTYPE 与外部实体的文档;
- 观察响应是否出现文件内容或内网响应片段。
授权测试里,用读公开可预测文件或自有 echo 服务证明即可,避免大肆读取客户敏感文件进报告附件。
开发若看到报告,请不要纠结 payload 长什么样,而要问:我们哪个解析器开了外部实体?为什么用户输入能进 DOCTYPE?
四、盲 XXE 与 OOB 带外:深度实战里最常被问的部分
1. 什么叫盲
服务器解析了恶意 XML,但:
- 不把实体展开结果返回给你;或
- 只返回固定错误页;或
- 异步解析,响应与内容无关。
这时"看页面吃内容"失效,需要旁路信号。
2. OOB 是什么意思
带外 指:数据或信号不走原 HTTP 响应,而走另一通道------通常是服务器向攻击者控制的服务器发起 DNS/HTTP 请求。
若解析器为了解析外部实体去解析某个域名、或请求某个 URL,攻击者就能在自己的 DNS/HTTP 日志里看到"被打到了",甚至在精心设计的场景下把数据带出去。
对防守方,这意味着:
- 内网主机突然对奇怪域名做 DNS,可能是盲 XXE/SSRF;
- 只盯响应内容的 WAF,可能漏掉盲利用;
- 出站流量与 DNS 日志是检测盲 XXE 的重要传感器。
3. 参数实体与"更绕的 DTD 玩法"(概念级)
深度资料里会谈到参数实体(%name)、外部参数实体、多级 DTD 引用等,用于在严格环境下仍触发外部加载或数据外带。细节因解析器而异,且变化快。
学习与授权测试的正确姿势:
- 先确认解析器类型与版本;
- 在自建环境复现"是否会对外发起请求";
- 客户环境以证明风险为主,不把客户数据经 OOB 拖到个人 VPS 长期存放;
- 修复验证:关闭外部实体后,对外请求消失。
4. OOB 的授权与合规
即便有授权,把客户敏感文件内容经你的外带服务器传出,也要极谨慎:协议是否允许、数据是否加密、测完是否销毁、报告是否脱敏。
很多项目约定:只允许 DNS 探测证明(看是否查询了某唯一子域),不允许完整文件外带。 这是专业做法。
5. 检测盲 XXE 的防守视角
- 应用服务器异常 DNS(随机子域、罕见 TLD);
- 异常出站访问攻击者基础设施;
- XML 解析错误激增;
- 解析耗时异常(大实体/网络取实体)。
把这些进 SIEM,比在原响应里找文件内容更贴近真实攻击。
五、XXE 与 SSRF、文件读取的边界
| 能力 | 常见实现载体 |
|---|---|
| 读本地文件 | XXE file://、路径穿越读、LFI |
| 打内网 HTTP | XXE 外部实体 HTTP、SSRF URL 拉取 |
| 盲数据外带 | XXE OOB、部分 SSRF+DNS |
汇报时可写:XXE 导致任意文件读取与/或 SSRF 类影响;盲场景存在 OOB 风险。修复统一落在解析器安全配置,不要只做 URL 关键字过滤。
六、哪里会接上用户 XML
审计清单(很实用):
- 文件上传:
.xml、.xsl、.svg(SVG 也是 XML!)、Office(底层大量 XML)、PDF 里的某些路径; - SOAP / XML-RPC;
- SAML 断言解析(身份联邦,出事是大事故);
- 内容类型
application/xml/text/xml的 API; - 配置导入、规则导入、报表导入;
- 老的 Java 系统、
.NETXmlDocument 默认行为、PHPsimplexml等。
SVG 上传当图片却服务端解析 XML,是经典坑。图片处理管道也要问一句:会不会走 XML 解析器?
七、防御深度:关掉外部实体是第一义
1. 原则
- 禁止解析外部实体;
- 禁止外部 DTD 加载;
- 如需 DTD,仅用本地可信 DTD,且不可被用户指定;
- 限制实体展开规模,防 DoS;
- 业务能改 JSON 就少暴露 XML 入口。
2. Java 生态(高频)
Java 历史事故多,因为常用解析器默认值不友好。按所用库(DocumentBuilder、SAX、XMLReader、Transformer 等)查阅当前官方安全编码指南,显式设置:
- 禁止 DOCTYPE / 外部通用实体 / 外部参数实体等特性(名称随 API 变化);
- 使用经过安全封装的库或框架默认安全配置;
- 升级到仍维护的版本。
不要抄十年前的 Stack Overflow 片段就上生产。以 OWASP XXE Prevention Cheat Sheet 与厂商文档为准,做进统一 XmlUtils,禁止业务私自 new DocumentBuilderFactory()。
3. .NET
XmlResolver 相关默认值、XmlReaderSettings 禁止 DTD 等------同样按当前文档配置。老代码 XmlDocument.Load 用户输入要逐个审。
4. PHP
libxml_disable_entity_loader 在老版本讨论很多;PHP 8 行为有变化,需按版本查阅。simplexml_load_*、DOMDocument 加载用户 XML 前统一安全封装。
5. Python
lxml、标准库等要注意是否解析外部实体;优先安全默认的解析方式,避免让用户控制 DOCTYPE。
6. 网关与 WAF
可拦明显的 <!DOCTYPE、ENTITY、SYSTEM 关键字,辅防。编码、截断、换入口(SVG)能绕。主防永远在解析器。
7. 网络层纵深
即便 XXE 触发,若主机不能出网、不能碰元数据、文件权限极小,危害下降。这与 SSRF 防御同一套出站策略。
八、授权测试实战流程
1. 识别入口
抓包找 XML;改 Content-Type 试 XML;上传 SVG/XML;看 SOAP。
2. 判断解析器是否处理 DOCTYPE
在授权环境提交带外部实体的测试文档,观察:
- 是否报错提及 DTD/实体;
- 是否引发对外 DNS(用你控制的唯一域名,仅证明用);
- 是否回显文件内容。
3. 定级
能读敏感文件或打元数据 → 高危/严重;
仅 DoS → 看环境;
仅证明解析外部 DTD → 至少中高危(接近可利用)。
4. 修复验证
关外部实体后:同样请求应失败或忽略实体,且不再出现对外 DNS/HTTP 。
回归测试留样例进 CI(安全测试包)。
5. 报告写法
写入口、解析组件(若可知)、危害类型(读文件/SSRF/盲 OOB 风险)、修复为"统一安全解析配置"、附验证步骤。
不附带客户敏感文件全文。
九、常见误区
"我们不用 XML。"
间接用:Office、SVG、SOAP、第三方库。
"WAF 拦了 DOCTYPE。"
换编码或换文件格式入口可能绕过;且盲 OOB 不一定靠响应。
"只过滤 SYSTEM 关键字。"
黑名单游戏,解析器特性太多。
"内网服务所以没事。"
XXE 读的是服务器本地文件,内网一样 aggh。
"盲 XXE 不能利用。"
OOB 与错误差异都可能让风险落地。
十、收尾
XXE 的本质是:XML 解析器太勤快,用户文档太自由,服务器既当解析器又当客户端。
有回显时它像任意文件读取;无回显时它靠 OOB 带外证明自己还活着;再往前走就和 SSRF、云凭证事故握手。
防御不必神秘:
禁止外部实体与外部 DTD;
统一安全解析器;
限制出站;
少让用户直接喂 XML。
今晚若只做一件事:找出项目里所有解析 XML 的地方(含 SVG、SOAP),看是否统一关闭了外部实体;同时在测试环境打一个应触发外部 DNS 的样例,确认加固后 DNS 不再出现。
这两步做完,你对 XXE 与 OOB 的"深度实战",就从名词落到了可验证的工程状态。
附:入口检查清单
- API
application/xml - 文件导入 XML/XSL
- SVG 上传与渲染
- SOAP/SAML
- Office 文档处理管道