XXE(外部实体注入)保姆级教程
原创
南棋子
南棋子
南棋子网络安全盟
2026年11月2日 10:14
北京
去阅读
漏洞原理
概括一下就是’攻击者通过向服务器注入指定的xml实体内容,从而让服务器按照指定的配置进行执行,导致问题\"。
也就是说服务端接收和解析了来自用户端的xml数据,而又没有做严格的安全控制,从而导致xml外部实体注入。
xml语法
XXE 的“语法”其实就围绕XML + DTD + 实体声明展开。渗透测试里看 XXE,重点不是背 payload,而是先判断:请求是否被当作 XML 解析、是否支持 DOCTYPE、是否允许外部实体加载。
1. 基础结构
一个普通 XML 长这样:
<?xml version=\"1.0\" encoding=\"UTF-8\"?><note> <to>admin</to> <body>test</body></note>
XXE 会额外插入DOCTYPE 和实体声明:
<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE root [ <!ENTITY name \"value\">]><root>&name;</root>
2. 内部实体
内部实体只是把值定义在 XML 内部:
<!ENTITY name \"test\">
引用时用:
&name;
解析后会替换成test。这个本身不是漏洞,但可以用来验证服务端是否真的解析了 XML。
3. 外部实体
外部实体是 XXE 的核心:
<!ENTITY xxe SYSTEM \"file:///etc/passwd\">
引用方式仍然是:
&xxe;
如果解析器没有禁用外部实体,&xxe;的位置就会被替换成外部资源内容。
常见协议:
| 协议 | 用途 | 典型场景 |
|---|---|---|
| file:// | 读取本地文件 | 敏感文件泄露 |
| http:// / https:// | 发起服务端请求 | SSRF / 内网探测 |
| php://filter | PHP 环境读取源码 | CTF / PHP 应用 |
| ftp:// | 远程资源加载 | 少数环境可用 |
4. 参数实体
参数实体只能在 DTD 内部引用,前面带%:
<!ENTITY % file SYSTEM \"file:///etc/passwd\">%file;
它常用于盲注 XXE / OOB 外带,因为普通实体在无回显场景下不一定能把数据带出来。
DTD基础
DTD 是Document Type Definition,文档类型定义,用来约束 XML 文档的结构。它规定 XML 里允许出现哪些元素、元素之间是什么顺序、元素可以有哪些属性,以及实体如何定义。
从 XXE 的角度看,DTD 最关键的部分就是DOCTYPE 声明和实体声明。
1. DTD 的两种写法
内部 DTD,直接写在 XML 里:
<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE note [ <!ELEMENT note (to, from, body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT body (#PCDATA)>]><note> <to>admin</to> <from>user</from> <body>test</body></note>
外部 DTD,通过 SYSTEM 引入:
<!DOCTYPE note SYSTEM \"note.dtd\">
内部 DTD 用[]包裹声明,外部 DTD 指向一个.dtd文件。
2. 元素声明:ELEMENT
<!ELEMENT 元素名 内容模型>
常见内容模型:
| 写法 | 含义 |
|---|---|
| (#PCDATA) | 只能包含文本 |
| EMPTY | 空元素 |
| ANY | 可包含任意内容 |
| (a, b) | a、b 按顺序出现 |
(a | b) |
|
| a\* | 出现 0 次或多次 |
| a+ | 出现 1 次或多次 |
| a? | 出现 0 次或 1 次 |
例如:
<!ELEMENT book (title, author+, price?)>
表示book必须有title,至少一个author,price可有可无。
3. 属性声明:ATTLIST
<!ATTLIST 元素名 属性名 属性类型 默认值>
常见属性类型:
CDATA
:普通字符串
ID
:唯一标识
IDREF
:引用某个 ID
枚举:(admin|user)
常见默认值:
REQUIRED
:必须提供
IMPLIED
:可选
FIXED \"值\"
:固定值
例如:
<!ATTLIST user role CDATA #REQUIRED>
表示user元素必须有role属性。
4. 实体声明:ENTITY
实体可以理解为 XML 里的“变量”。
内部实体:
<!ENTITY name \"test\">
引用:
&name;
外部实体:
<!ENTITY xxe SYSTEM \"file:///etc/passwd\">
引用:
&xxe;
如果解析器允许外部实体加载,就会把&xxe;替换成外部资源内容。
5. 参数实体:ENTITY %
参数实体只能在 DTD 内部使用,前面带%:
<!ENTITY % file SYSTEM \"file:///etc/passwd\">%file;
它和普通实体的区别是:
| 类型 | 定义 | 引用 | 使用范围 |
|---|---|---|---|
| 普通实体 | <!ENTITY name \"value\"> | &name; | DTD、XML 内容 |
| 参数实体 | <!ENTITY % name \"value\"> | %name; | 仅 DTD 内部 |
参数实体在盲注 XXE 里很关键,因为它常用于构造外部 DTD 加载和数据外带逻辑。
6. 和 XXE 的关系
XXE 之所以成立,本质上是因为:
应用接收了用户可控的 XML;
解析器允许DOCTYPE;
解析器允许加载外部实体或参数实体。
所以在漏洞分析时,看到<!DOCTYPE、<!ENTITY、SYSTEM、%实体名;,就要立刻意识到这里可能涉及 DTD 解析和外部实体加载。
防御
禁用外部实体:确保XML解析器配置为不解析外部实体。
输入验证:对所有输入进行严格的验证,确保它们符合预期的格式。
使用安全的解析器:使用那些默认禁用外部实体解析的XML解析器。
错误处理:不要在错误消息中暴露敏感信息,这可能会被攻击者用来构造攻击。
使用白名单:限制可接受的输入到一个明确定义的、安全的白名单。
更新和打补丁:定期更新XML解析器和相关库,以修复已知的XXE漏洞。
危害
任意文件读取
:攻击者可以读取服务器上的敏感文件,如配置文件、密码文件等,导致敏感信息泄露。
系统命令执行
:在某些环境下,攻击者可以利用XXE漏洞执行系统命令,从而获得服务器的控制权。
内网探测
:攻击者可以通过XXE漏洞探测内网中的其他系统和服务。
拒绝服务(DoS)攻击
:通过发送大量的异常请求,攻击者可以导致服务器资源耗尽,服务中断。
服务器沦陷
:攻击者可能利用XXE漏洞在目标服务器上执行恶意代码,实现对服务器的控制。
敏感数据泄露
:XXE攻击可以使攻击者读取服务器上的文件,包括配置文件、源代码、密码文件等,从而泄露敏感信息。
内部系统扫描
:攻击者可以利用XXE攻击探测内部网络,识别内部服务和应用,为进一步的攻击提供信息。
服务器端请求伪造(SSRF)
:通过XXE攻击,攻击者可能能够迫使服务器与内部系统或外部服务器进行交互,可能导致数据泄露或其他安全问题。
执行远程代码和命令
:在某些情况下,攻击者还可以利用XXE漏洞发起服务拒绝(DoS)攻击,甚至执行远程代码。
绕过防火墙
:攻击者可以利用XXE漏洞绕过直接从外部网络访问内部网络资源的限制。
植入恶意内容
:在某些情况下,XXE漏洞可以被用来植入恶意内容到系统中,影响数据的完整性。
绕过
XXE 绕过可以从三个层面看:绕过 WAF / 过滤、绕过无回显限制、绕过协议/解析器限制。只建议在授权测试、靶场、代码审计或面试学习场景下理解这些思路,不要对未授权系统做探测。
1. 绕过 WAF / 关键词过滤
常见防护会拦截DOCTYPE、ENTITY、SYSTEM、file://等关键字。
| 绕过方向 | 原理 | 注意点 |
|---|---|---|
| 编码混淆 | 用 UTF-7、UTF-16、XML 实体编码等方式改变关键字外观 | WAF 若只按 ASCII 关键字匹配,可能被绕过 |
| 外部 DTD | 把恶意 DTD 放在远程服务器,本地 XML 只保留引用 | WAF 不一定递归检测外部 DTD 内容 |
| XInclude | 使用 xi:include 代替 DOCTYPE/ENTITY | 需要解析器支持 XInclude |
| 畸形/混淆 | 插入多余空格、换行、注释、大小写变形 | 取决于 WAF 规则严谨程度 |
| 内容类型混淆 | 尝试修改 Content-Type 或测试后端是否强制按 JSON 解析 | 属于接口逻辑探测 |
| 超大请求体 | 让请求体超过 WAF 检测上限,使后端收到未检测内容 | 成功率取决于 WAF 配置 |
面试里可以概括成:WAF 通常基于关键字和规则匹配,XXE 绕过本质是让恶意结构绕过规则检测,但仍能被解析器正常解析。
2. 绕过无回显限制
无回显 XXE 不能直接在响应里看到结果,所以需要OOB 外带。
常见思路:
DNSLog / HTTPLog 探测
用外部实体请求自己的域名或服务器,判断是否触发解析。
参数实体 + 远程 DTD
先在远程服务器上放恶意 DTD,再通过参数实体加载它,让目标服务器把读取到的内容带出来。
报错回显
有些环境没有正常回显,但会把解析错误信息返回。可以构造错误路径,让文件内容出现在报错信息里。
Base64/编码外带
读取文件时先用php://filter等方式做 Base64 编码,避免特殊字符导致外带失败。
这里最核心的原理是:盲注 XXE 不是等服务器返回数据,而是让服务器主动把数据带出来。
3. 绕过协议/解析器限制
不同语言和解析器行为差异很大。
| 环境 | 常见可用点 | 限制 |
|---|---|---|
| PHP | php://filter 读取源码、Base64 编码外带 | 高版本 libxml 可能限制外部实体 |
| Java | 可尝试 netdoc、XInclude、本地 DTD 报错 | 通常需显式开启不安全配置才更容易触发 |
| Python lxml | 默认可能解析外部实体 | defusedxml 更安全 |
| .NET | 需关注 DtdProcessing、XmlResolver | 默认较新版本更安全 |
4. 本地 DTD 绕过
如果服务器不能出网,OOB 外带会失效。此时可以尝试利用服务器上已有的本地 DTD 文件,通过重新定义参数实体触发报错或解析异常,从而间接泄露信息。
这类场景在面试或代码审计里很适合展开:不出网时,XXE 利用会从“外带数据”转向“本地资源复用 + 报错侧信道”。
5. 面试/审计视角怎么答
可以这样组织:
先看应用是否接收并解析 XML;
再看是否允许 DOCTYPE 和外部实体;
有回显时直接读取文件;
无回显时用 OOB、报错回显或本地 DTD;
遇到 WAF 时考虑编码、外部 DTD、XInclude、内容类型混淆;
防御层面优先禁用 DOCTYPE、禁用外部实体、使用安全解析库、限制出站协议。
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论