
XML external entity (XXE) injection学习笔记
最近刷题的时候碰到了xxe漏洞,我之前没了解过,现在就用bp的实验室来学习这个漏洞吧,写个笔记记录一下。
什么是XML XML实体 DTD?
XML 代表 “可扩展标记语言”。XML 是一种用于存储和传输数据的语言。和html一样使用标签,但是xml可以用自定义的标签。
XML 实体是一种在 XML 文档中表示数据项的方式,而不是使用数据本身。XML 语言规范中内置了各种实体。例如,实体 < 和 > 表示字符 < 和 > 。这些是用于表示 XML 标签的元字符,因此当它们出现在数据中时,通常必须使用其实体来表示。
XML 文档类型定义 (DTD) 包含声明,这些声明可以定义 XML 文档的结构、文档中可以包含的数据值类型以及其他项目。DTD 在 XML 文档开头的可选 DOCTYPE 元素中声明。DTD 可以完全包含在文档本身中(称为 “内部 DTD”),也可以从其他地方加载(称为 “外部 DTD”),也可以是两者的混合。
DTD可以类比html中的css,为了让xml的格式统一,便于传输
XML自定义实体:
XML 允许在 DTD 中定义自定义实体。
1 |
此定义意味着 XML 文档中任何对实体引用 &myentity; 的使用都将被替换为定义的值:“ my entity value ”。
XML自定义实体:
XML 外部实体是一种自定义实体,其定义位于声明它们的 DTD 之外。
外部实体的声明使用 SYSTEM 关键字,并且必须指定应从中加载实体值的 URL。URL也可以用php伪协议,file协议等等,如
1 |
什么是xxe漏洞 如何出现
允许攻击者干扰应用程序对 XML 数据的处理。它通常允许攻击者在应用程序服务器文件系统上查看文件,并与应用程序本身可以访问的任何后端或外部系统进行交互。
一些应用程序使用 XML 格式在浏览器和服务器之间传输数据。这样做的应用程序实际上始终使用标准库或平台 API 来处理服务器上的 XML 数据。 XXE 漏洞之所以出现,是因为 XML 规范包含各种潜在危险的功能,并且标准解析器也支持这些功能,即使应用程序通常不使用它们。
上面也有说到xml的外部实体可以运行其他的协议,所以可以读取本地文件,在一些情况还会出现ssrf(服务器端请求伪造)
xxe攻击类型
利用 xxe 检索文件
这种攻击需要两步提交xml
1 创建一个DOCTYPE 然后定义一个实体
2 发送数据请求,引用到这个实体
比如购物申请通过将以下 XML 提交到服务器:
1 |
|
因为服务器没有xxe的防御措施,我们可以这样利用
1 |
|
这样子发送数据后,系统就回读取到/etc/passwd中的文件,然后返回给我们
利用 xxe 执行SSRF
这会让程序的服务器访问任意的URL提出HTTP请求
1 |
比如这里ssrf攻击,访问内部资源,然后根据返回得到一个id,然后可以根据这些敏感信息继续得到一些信息
盲xxe
很多xxe漏洞是不会在响应中返回定义的外部实体,但是可以触发xml解析错误或者和外部的网络交互,从而导致敏感数据泄漏
1 让外部实体访问攻击者设置的url,这样在攻击机上可以监听http请求,从而确定攻击是否成功,如
1 |
有时程序会加强解析器,一般的xml实体攻击会被阻止,这时候就要用XML 参数实体。 XML 参数实体是一种特殊的 XML 实体,只能在 DTD 中的其他地方引用。
首先,XML 参数实体的声明在实体名称前包含百分号:
1 |
其次,使用百分号而不是通常的 “与” 符号来引用参数实体:
1 | %myparameterentity; |
1 |
要注意xml参数实体和普通实体的区别,多了个% 而且在最后要在DOCTYPE里面引用。
利用盲xxe访问敏感数据
上面知识说了什么是盲xxe,具体应该如何使用它呢。
它需要攻击者在他们控制的系统上托管恶意 DTD,然后从带内 XXE 负载中调用外部 DTD。
用于窃取 /etc/passwd 文件内容的恶意 DTD 示例如下:
1 |
|
定义一个名为 file 的 XML 参数实体,其中包含 /etc/passwd 文件的内容。
然后又定义了一个内部实体eval 包含了另一个exfiltrate实体的内容,%
是百分号,由于 % 在实体定义中有特殊含义,所以需要使用这种转义方式来表示普通的百分号。
最后用%eval
来调用这个实体,当解析器遇到这行代码的时候,会插入这个实体定义的内容
notes
1 | 此技术可能不适用于某些文件内容,包括 /etc/passwd 文件中的换行符。这是因为某些 XML 解析器使用 API 获取外部实体定义中的 URL,该 API 会验证 URL 中允许出现的字符。在这种情况下,可以使用 FTP 协议而不是 HTTP。有时,无法窃取包含换行符的数据,因此可以改为针对 /etc/hostname 等文件。 |
示例
在服务器上先写入恶意dtd文件
然后用内部实体给服务器的恶意dtd地址发送请求,
就可以在服务器上的accesslog上面看到hostname了
说完了用外部服务器利用盲xxe,现在再来看看用报错利用xxe
1 |
|
总体的结构和刚才很像,这里是通过访问一个不存在的文件,让xml报错,这个名称就是%file
的内容了
也是现在服务器上先放上这个恶意dtd文件
当我们去访问我们服务器上的这个dtd文件的时候,因为没有这个文件地址,所以有报错,根据报错信息,就能得到/etc/passwd了
通过重新利用本地 DTD 来利用盲 XXE
上述技术适用于外部 DTD,但通常不适用于 DOCTYPE 元素中完全指定的内部 DTD。这是因为该技术涉及在另一个参数实体的定义中使用 XML 参数实体。根据 XML 规范,这在外部 DTD 中是允许的,但在内部 DTD 中是不允许的。(有些解析器可能会容忍这种情况,但许多解析器则不会。)
那么,当带外交互被阻止时,盲 XXE 漏洞会怎样呢?无法通过带外连接泄露数据,也无法从远程服务器加载外部 DTD。
如果文档的 DTD 使用内部和外部 DTD 声明的混合,则内部 DTD 可以重新定义在外部 DTD 中声明的实体。发生这种情况时,在另一个参数实体的定义中使用 XML 参数实体的限制会放宽。
本质上,攻击涉及调用恰好存在于本地文件系统上的 DTD 文件并重新利用它以触发包含敏感数据的解析错误的方式重新定义现有实体
例如,假设服务器文件系统上 /usr/local/app/schema.dtd 位置有一个 DTD 文件,并且此 DTD 文件定义了一个名为 custom_entity 的实体。攻击者可以通过提交如下混合 DTD 来触发包含 /etc/passwd 文件内容的 XML 解析错误消息:
1 |
|
定义一个名为 local_dtd 的 XML 参数实体,其中包含服务器文件系统上存在的外部 DTD 文件的内容。
重新定义名为 custom_entity 的 XML 参数实体,该实体已在外部 DTD 文件中定义。该实体被重新定义为包含已描述的基于错误的 XXE 漏洞,用于触发包含 /etc/passwd 文件内容的错误消息。
使用 local_dtd 实体,以便解释外部 DTD,包括 custom_entity 实体的重新定义值。这会产生所需的错误消息。
转义后的真实含义:
转义写法 | 对应字符 | 解释 |
---|---|---|
% |
% | 用于定义参数实体(如 %file) |
& |
& | 转义后的 &,用于后续嵌套实体定义 |
' |
‘ | 单引号,用于包裹字符串避免与外围引号冲突 |
&#x25;
是双重转义,因为这一行代码里%
已经被使用,所以先转移出一个&
再和后面拼接,转义出一个%
由于此 XXE 攻击涉及重新利用服务器文件系统上的现有 DTD,因此关键要求是找到合适的文件。这实际上非常简单。由于应用程序会返回 XML 解析器抛出的任何错误消息,因此只需尝试从内部 DTD 中加载它们即可轻松枚举本地 DTD 文件。
例如,使用 GNOME 桌面环境的 Linux 系统通常有一个 DTD 文件 /usr/share/yelp/dtd/docbookx.dtd 可以通过提交以下 XXE payload 来测试此文件是否存在,如果文件缺失,将导致错误:
1 |
|
测试了常见 DTD 文件列表以找到存在的文件后,需要获取该文件的副本并查看它以找到可以重新定义的实体。由于许多包含 DTD 文件的常见系统都是开源的,因此通常可以通过互联网搜索快速获取文件副本。
比如使用GNOME 桌面环境的系统通常有一个 DTD /usr/share/yelp/dtd/docbookx.dtd 包含一个名为 ISOamso.
寻找 XXE 注入的隐藏攻击面
在许多情况下,XXE 注入漏洞的攻击面很明显,因为应用程序的正常 HTTP 流量包括包含 XML 格式数据的请求。在其他情况下,攻击面不太明显。但是,如果你在正确的地方寻找,你会在不包含任何 XML 的请求中找到 XXE 攻击面。
XInclude attacks XInclude 攻击
一些应用程序接收客户端提交的数据,在服务器端将其嵌入 XML 文档,然后解析该文档。例如,客户端提交的数据被放入后端 SOAP 请求中,然后由后端 SOAP 服务进行处理。
在这种情况下, 无法进行经典的 XXE 攻击,因为 无法控制整个 XML 文档, XInclude 无法定义或修改 DOCTYPE 元素。但是, 可能能够改用 XInclude 是 XML 规范的一部分,允许从子文档构建 XML 文档。 可以在 XML 文档中的任何数据值中放置 XInclude 攻击,因此可以在 仅控制放置在服务器端 XML 文档中的单个数据项的情况下执行攻击。
要执行 XInclude 攻击, 需要引用 XInclude 命名空间并提供要包含的文件的路径。例如:
1 | <foo xmlns:xi="http://www.w3.org/2001/XInclude"> |
XInclude 是 W3C 标准,允许 XML 文档包含其他资源(本地文件、远程 URL 等)的内容。
parse=”text”:将目标资源内容视为纯文本(而非 XML),直接嵌入当前文档。
href:指定要包含的资源路径(如 file:///etc/passwd)。
比如这里用户可以输入的是productId,就在这里插入XInclude,默认情况下, XInclude 会尝试将包含的文档解析为 XML。由于 /etc/passwd 不是有效的 XML,因此需要向 XInclude 指令添加额外的属性来更改此行为。这里就是指定parse="text"
将目标文件视为纯文本而非 XML
通过文件上传进行 XXE 攻击
一些应用程序允许用户上传文件,然后在服务器端进行处理。一些常见的文件格式使用 XML 或包含 XML 子组件。基于 XML 的格式的示例包括办公文档格式(如 DOCX)和图像格式(如 SVG)。
例如,应用程序可能允许用户上传图片,并在上传后在服务器上处理或验证这些图片。即使应用程序希望接收 PNG 或 JPEG 等格式,所使用的图像处理库也可能支持 SVG 图像。由于 SVG 格式使用 XML,攻击者可以提交恶意的 SVG 图像,从而达到 XXE 漏洞的隐藏攻击面。
payload:
1 | <svg width="128px" height="128px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"><text font-size="16" x="0" y="16">&xxe;</text></svg> |
这里就是在上传头像这里支持svg文件,传入payload之后,就能在评论区看到自己的头像了,头像丽的内容就是/etc/hostname了
通过修改内容类型进行 XXE 攻击
大多数 POST 请求使用由 HTML 表单生成的默认内容类型,例如 application/x-www-form-urlencoded 。一些网站希望接收这种格式的请求,但也能容忍其他内容类型,包括 XML。
正常请求的内容
1 | POST /action |
可能能够提交以下请求,并获得相同的结果:
1 | POST /action |
如果应用程序容忍消息正文中包含 XML 的请求,并将正文内容解析为 XML,那么只需重新格式化请求以使用 XML 格式即可到达隐藏的 XXE 攻击面。
以上就是所有关于xxe漏洞的内容了,更多技巧还需要在做题或者实战中学习。