XXE漏洞本质是解析器配置不当而非XML本身危险,真正RCE需特定协议与组件组合,如PHP expect://或Java XSLT注入;修复须全面禁用外部实体并检查所有XML解析入口。

XML 文件本身不会执行系统命令
XML 是纯数据格式,解析器默认只读取结构、不执行代码。所谓“XXE 执行命令”,本质是服务端用了不安全的 XML 解析器(比如老版本 libxml2、javax.xml.parsers.DocumentBuilder),且开启了外部实体加载(DOCTYPE 解析)——漏洞不在 XML,而在解析它的程序。
常见错误现象:java.lang.SecurityException: Attempt to access external entity 反而说明防护到位;真正危险的是没报错、却悄悄发起了 DNS 请求或读取了 /etc/passwd。
- 使用场景:目标用 Java/Spring 的
SAXReader、PHP 的simplexml_load_string()、Python 的xml.etree.ElementTree.parse()(未禁用外部实体)处理用户提交的 XML - 关键参数差异:Java 中
DocumentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)才算真正禁用;设成false或漏掉这行,等于开门 - PHP 7.4+ 默认禁用
expect://协议,但php://filter仍可读文件,别以为升级就万事大吉
XXE 利用链里哪些协议真能触发命令执行
绝大多数 XXE 场景只能读文件或打内网探测,所谓“执行命令”需要极特殊条件:解析器支持特定协议 + 后端环境存在可利用组件。不是所有 XXE 都能 RCE。
真实可用的协议组合有限,且高度依赖后端技术栈:
-
expect://id:仅 PHP 且expect扩展启用时有效,现在默认不装,报错Warning: simplexml_load_string(): Unable to load source data就说明走不通 -
php://filter/convert.base64-encode/resource=/etc/passwd:安全,只编码不执行,但常被误当成“命令执行” -
file:///proc/self/environ:Linux 下读进程环境变量,可能泄露密钥或配置路径,为后续攻击铺路,但不是命令执行本身 - 真正 RCE 的少见路径:Java 中若用了
javax.xml.transform.Transformer+ XSLT 且允许外部样式表,才可能通过xsl:include引入恶意 XSL 触发Runtime.getRuntime().exec()—— 这已超出 XXE 范畴,属于 XSLT 注入
为什么本地复现总失败
本地测试时解析器行为和线上环境常不一致:JDK 版本、XML 库编译选项、容器安全策略都影响结果。别在自己电脑上跑通一个 payload 就认为线上也行。
容易踩的坑:
- Java 项目用 Spring Boot 2.2+,默认禁用外部 DTD,
setFeature("http://xml.org/sax/features/external-general-entities", true)会直接抛UnsupportedOperationException - Python 的
defusedxml库若没显式替换原生xml.etree.ElementTree,照样有风险;只 pip install 不 import 等于没装 - DNSLog 回显失败?检查目标是否出网、DNS 服务器是否过滤
_http._tcp.类型请求,换用http://xxx.ceye.io/123更可靠 - 报错信息被日志脱敏,看不到
java.io.FileNotFoundException: /etc/shadow (Permission denied),不代表没读成功——盲注才是常态
修复时最容易被忽略的点
关掉 DOCTYPE 声明只是第一步。很多团队修完 XXE 还留着隐患,因为没意识到解析器不止一种入口。
必须同步检查:
- 所有接受 XML 的接口,包括 SOAP、WebDAV、配置上传、CI/CD 中的 Jenkins 插件配置解析
- 第三方 SDK 是否封装了 XML 解析逻辑(比如某些国产加密库、电子签章组件),它们可能自带不安全的
DocumentBuilder - Spring 中
@RequestBody接收 XML 时,若用了Jaxb2RootElementHttpMessageConverter,需确认其底层JAXBContext是否禁用外部实体——这个细节文档极少提 - WAF 规则只拦截
SYSTEM关键字?绕过方法太多了:]]>、实体嵌套、大小写混淆都能逃逸
复杂点在于:同一个应用里,不同模块可能用不同解析器,有的修了,有的还在用老版 dom4j 1.6.1。










