绝大多数老版本或配置宽松的xml解析器默认开启外部实体解析,这是dtd的历史设计特性而非漏洞;关键判断依据是是否显式禁用外部实体加载。

XML解析器默认开启外部实体(XXE)吗?
绝大多数老版本或配置宽松的XML解析器——比如Java的DocumentBuilder、Python的xml.etree.ElementTree(不加防护)、PHP的simplexml_load_string()——默认会解析外部实体。这不是“漏洞”,而是历史设计:DTD本就支持SYSTEM和ENTITY声明,解析器照做而已。
关键判断点:只要没显式禁用外部实体加载,就极可能被利用。
- Java中必须调用
setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)或关闭http://xml.org/sax/features/external-general-entities - Python的
xml.etree.ElementTree完全不安全,改用defusedxml.ElementTree;lxml需设resolve_entities=False - PHP 8.0+默认禁用
libxml_disable_entity_loader(true)已废弃,应改用libxml_set_external_entity_loader(null)
怎么验证目标是否存在XXE?用什么Payload最稳?
别一上来就打file:///etc/passwd——很多环境根本没这个路径,或解析器限制了协议。先确认基础回显能力,再逐步升级。
推荐分三步试:
- 用
触发DNS外带,看域名是否被解析(绕过HTTP拦截,最通用) - 若支持
file://且无过滤,用(Linux当前目录更可靠) - 对JSON/XML混合接口,尝试在注释里藏实体:
<!-- <!ENTITY xxe SYSTEM "file:///etc/hosts"> -->,部分解析器仍会处理
注意:php://filter在PHP环境常比file://更有效,比如php://filter/read=convert.base64-encode/resource=/etc/passwd可绕过二进制截断。
为什么有些Payload执行了却没回显?常见堵点在哪?
不是Payload写错了,大概率是解析器做了半吊子防护——比如只禁了file://但放行http://,或只关了外部DTD但留着内部实体。
- 服务端用了WAF?检查是否拦截
SYSTEM关键字,可尝试大小写混淆:SyStEm或用CDATA包裹 - 报错信息被吞?加
xxe SYSTEM "http://xxx"后观察响应时间是否明显变长,延迟说明请求发出去了 - XML被二次解析?比如先用
DOMParser转成JS对象再拼回XML,此时实体已在前端被展开,后端收不到原始声明
一个容易忽略的事实:很多现代框架(如Spring Boot 2.3+)默认用Jackson而非javax.xml,这时XXE根本不会触发——你得先确认底层用的是哪个解析器。
测试时如何避免把自己搞进黑名单?
频繁发file://或http://请求很容易触发WAF规则或日志告警。真实测试要克制,优先走低风险路径。
- 用
gopher://或ftp://替代http://做DNS探测(某些解析器支持,且更难被WAF识别) - 本地构造好Payload再发,避免在Burp Repeater里反复修改DOCTYPE导致大量异常日志
- 如果目标明确禁了外部实体,别硬刚——试试
XXE via SVG upload或DTD reuse in XInclude,这些属于同源但不同入口的变种
真正卡住的往往不是Payload多复杂,而是没弄清对方用的是哪一层解析器、有没有中间件提前吃掉了DOCTYPE声明。抓包看原始请求体,比盲目换Payload有用得多。










