在本文中,我们将深入了解如何查找和利用 XML 外部实体注入漏洞。
XXE(XML External Entity)顾名思义,是一种与解析 XML 数据的应用程序相关的攻击。根据 XML 标准规范,实体可以被视为一种存储类型。在编程术语中,我们可以将实体视为具有某些值的变量。XML 规范中有两种类型的实体:
根据 XML 标准,内部实体是一个实体,其值被定义为字符串文字。例如,仅指向字符串值的实体可以称为内部实体。它可以定义如下:
“Internal Entity”> |
internal =变量名称
“Internal Entity” =字符串文字
如果实体不是内部实体,则它是外部实体。外部实体可以定义如下:
SYSTEM|PUBLIC “http://www.example.com/test.xml”> |
外部实体声明包括称为实体的系统标识符的SystemLiteral ( SYSTEM )。当 XML 处理器使用SystemLiteral解析实体时,它会解析 URI 引用 ( http://www.example.com/test.xml ) 以获取 XML 处理器的输入,以将值分配给“外部”变量或任何其他引用在 XML 数据中定义。我们将在 OOB(带外)XXE 开发部分中更详细地讨论这一点。
根据实体的声明方式,实体可以进一步分为通用实体和参数化实体两种类型。
一般实体是可以用“ & ”符号引用的实体。声明如下:
foo “this is foo”> ]> |
参数化实体也可用于将值分配给其他实体。参数化实体在声明期间在其名称前有一个百分号' % ',可以引用为%(name); . 参数化实体通常可以在 DTD 声明中找到。百分号“ % ”告诉 XML 处理器它是一个参数化实体,在它被引用的地方插入这个实体的替换值,并将实体的值作为 DTD 的一部分进行解析。在处理 OOB XXE 有效载荷的构造时,我们会看到这样的实体。参数化实体可以定义如下:
foo “this is foo”> %foo;‘> ]> |
由于 XXE 漏洞仅与解析 XML 数据的应用程序相关,因此在测试应用程序是否存在 XXE 漏洞时,主要攻击向量将是应用程序中接受 XML 格式输入的任何功能。查找和确认漏洞还取决于不同的情况,即呈现给我们的应用程序。
如果应用程序正在解析 XML 数据并在 HTTP 响应中显示解析 XML 的结果,则测试 XXE 漏洞的基本测试用例将发送使用内部实体的 XXE 有效负载,以确保应用程序是否具有实体。
将以下 PHP 代码另存为xxe.php在 webserver 根文件夹中:
向xxe.php文件发送 POST 请求,其中包含以下屏幕截图所示的 XML 数据:
观察应用程序在 HTTP 响应中显示用户名,确认它正在解析 XML 数据。
现在,让我们向 XML 数据添加一个内部实体,并使用&u;在
观察应用程序解析我们的内部实体,成功确认 XXE 漏洞。
要模拟在 HTTP 响应中不显示解析 XML 数据的结果的应用程序,我们可以简单地注释掉前面使用的 Php 代码中的echo 语句。注释掉 echo 语句后,我们的 PHP 代码将如下所示。
在这种情况下,我们无法仅使用内部实体来确认 XXE 漏洞,因为我们无法确认注入的实体是否正在被应用程序解析。让我们快速测试一下。
我们将简单地发送案例 1中所示的内部实体有效负载。
观察应用程序在 HTTP 响应正文中没有显示任何内容。
因此,对于这种情况,我们可以使用外部实体来解析我们(攻击者/漏洞测试者)控制的URL 。这样,如果我们收到从易受攻击的服务器到我们服务器的 HTTP 请求,我们就可以确认该漏洞。
我们将使用本地 python 服务器进行演示。但是,您也可以使用任何远程服务器。我们的请求将如下所示:
观察应用程序通过向我们在localhost监听端口81的服务器发送 HTTP 请求来解析我们的外部实体。这种将实体解析为已知 URL 的方法称为 OOB(带外)XXE。
在本节中,我们将了解如何使用外部实体将一些敏感数据(针对不同情况)从易受攻击的系统中提取出来,以了解该漏洞的整体影响。
使用外部实体,我们还可以在分配给易受攻击的应用程序/ XML 解析器的权限的上下文中读取系统文件。如果您可以在 HTTP 响应中看到解析的 XML 数据,这意味着您将能够在 HTTP 响应中看到系统文件的内容,这使得利用变得更加容易。我们将使用与“查找 XXE 漏洞”部分的案例 1中使用的相同的 PHP 代码。
我们使用外部实体读取系统文件的请求如下所示:
注意上面的有效载荷;我们在 URL 中滥用协议类型(使用 file:// 而不是 http://)来读取系统文件。
这是您在应用程序安全参与期间会遇到的最常见情况。在这种情况下,我们将使用参数化实体来构建我们的有效负载并在此过程中学习一些概念。这里的想法是将文件的内容存储在某个变量中,并在 HTTP 请求中解析该变量以将文件的内容发送到我们的服务器。
让我们逐步构建我们的有效载荷,并了解我们的有效载荷的方式和原因。我们简单地定义了一个文件变量来存储win.ini的内容和一个req变量来将内容发送到我们的服务器。
让我们尝试在“查找 XXE 漏洞”部分的案例 2中的易受攻击的 Php 文件中使用此有效负载。我们的请求负载将如下所示:
我们从 PHP 解释器收到以下错误,指出我们的 URL 无效。
为了克服上述错误,我们使用了一个与外部实体嵌套的内部实体并再次触发了请求。我们的请求负载将如下所示:
然而,这次我们得到了一个不同的错误,这仅仅意味着我们不能在内部文档类型声明中引用参数化实体。
我们可以通过使用外部 DTD 来克服上述限制。我们在监听localhost:81的服务器上创建了一个xxe.dtd文件,其内容如下:
我们使用 XML 有效负载的最终请求将如下所示。有效负载解析对外部 DTD ( %dtd ) 的引用以及 DTD 文件xxe.dtd ( %all;
%req; ) 中定义的引用。
观察应用程序解析我们的 DTD 文件以及请求中的所有引用,并发送回win.ini文件的 base64 编码内容,稍后可以对其进行解码以获得原始内容。