微信公众号:[念沉凡] 初学安全的小老弟写的,思路来自于https://xz.aliyun.com/t/12081提供的案例。
java zip slip漏洞
zip slip漏洞其实是目录遍历漏洞中的一种,通过服务器或应用程序解压压缩文件来进行攻击。恶意攻击者通过构造一个压缩文件条目路径中带有../的压缩文件,上传给目标服务器并进行解压,如果目标服务器在解压时没有对该压缩文件的文件名进行合法性校验,而是直接将文件名拼接在待解压目录之后,则会导致将该文件解压到正常压缩路径之外并覆盖可执行程序,从而等待系统或用户调用他们实现代码执行,或者是覆盖一些配置文件和敏感文件。
漏洞复现
[下载地址](https://gitee.com/RainyGao/DocSys/releases)
我首先下载了DocSystem的MxsDoc_V2.02.43版本,即最新版本,在后台管理系统的系统管理中进行系统升级操作,传入恶意压缩文件,提示
但文件已经被解压到默认目录下,打开目录发现文件位于正确的位置,甚至文件都没有被解压
怀疑在最新版本上该漏洞已经被修复,于是在室友安装的低版本系统MxsDoc_V2.02.36上进行实验
如果构造一个正常的压缩文件,该文件压缩后默认的储存位置为C:MxsDocdocsystomcatwebappsdocSys.ini
但如果上传一个故意构造的压缩文件其中的恶意代码xxx.jsp则会被解压到C:MxsDocdocsystomcatwebappsDocSystem
攻击思路
我们首先来分析一下这个恶意压缩文件
他的文件路径中出现了两个名为..的文件夹,导致与默认解压文件夹拼接后进行回跳,写入敏感的文件目录中
解压时可构造成异常的文件存储路径
../../DocSystem/XXX.jsp
代码审计
在一个JavaWeb项目中,Controller目录通常用于存放处理请求和响应的控制器类。这些控制器类负责 接收来自前端的请求,然后处理请求参数、调用业务逻辑层进行处理,并最终返回响应结果给前端。 因此要具体分析上述漏洞的原理,应该在Controller文件夹中寻找答案
文件路径如下:
DocSys-DocSys_V2.02.36srccomDocSystemcontroller
根据之前了解到的原理可知,问题出现在解压时对于文件名的合法性没有进行检测,导致路径拼接后将../路径拼入,使得文件解压到了规定路径以外的路径,因此我们在代码审计时着重注意解压和文件拼接的方法
可疑点如下:
在ManageController类中,找到了升级的部分(如下图 里面存在两个疑点,经过分析
疑点1-getOriginalFilename()
第一个红色箭头指向的的 getOriginalFilename()方法,我们需要先了解一下这个函数,getOriginalFilename()是MultiparrtFile类中的一个函数,用于换取上传文件的原始文件名
文件名限制 对于getOriginalFilename()方法,文件名的限制主要取决于操作系统,浏览器和服务器等因素。在一些操作系统中,文件名的长度最多为260个字符
总的来说,该方法只是用于获取压缩文件的名称,而没有获取压缩文件的内部文件的名称,因此与该漏洞无关
疑点2-unZip()
第二个红色箭头用到了unZip方法,涉及到解压,因此我们去BaseController文件中审计一下unZip代码,发现问题出在unzip方法
//BaseController
public static boolean unZip(String path, String savepath)
{
boolean ret = false;
int count = -1;
InputStream is = null;
FileOutputStream fos = null;
BufferedOutputStream bos = null;
File file = new File(savepath);
if(file.exists() == false)
{
file.mkdir(); //创建保存目录
}
ZipFile zipFile = null;
try
{
zipFile = new ZipFile(path,"gbk"); //解决中文乱码问题
Enumeration<?> entries = zipFile.getEntries();
while(entries.hasMoreElements())
{
byte buf[] = new byte[2048];
ZipEntry entry = (ZipEntry)entries.nextElement();
String filename = entry.getName();
boolean ismkdir = false;
if(filename.lastIndexOf("/") != -1){ //检查此文件是否带有文件夹
ismkdir = true;
}
filename = savepath + filename;
if(entry.isDirectory()){ //如果是文件夹先创建
file = new File(filename);
file.mkdirs();
continue;
}
file = new File(filename);
if(!file.exists()){ //如果是目录先创建
if(ismkdir){
new File(filename.substring(0, filename.lastIndexOf("/"))).mkdirs(); //目录先创建
}
}
file.createNewFile(); //创建文件
is = zipFile.getInputStream(entry);
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos, 2048);
while((count = is.read(buf)) > -1)
{
bos.write(buf, 0, count);
}
bos.flush();
bos.close();
fos.close();
is.close();
}
zipFile.close();
ret = true;
}catch(IOException ioe){
Log.info(ioe);
}finally{
try{
if(bos != null){
bos.close();
}
if(fos != null) {
fos.close();
}
if(is != null){
is.close();
}
if(zipFile != null){
zipFile.close();
}
}catch(Exception e) {
Log.info(e);
}
}
return ret;
}
该JAVA方法可以解压指定路径的压缩文件,并将其内容保存到指定文件中,代码的具体功能如下:
-
- 该方法有两个参数:要解压的压缩文件文件路径(**path**)和要保存提取文件的目录(savepath)
-
- 方法首先检查保存目录是否存在,如果不存在,则创建该目录 - 然后使用ZIP文件路径创建一个 ZipFile 对象。
-
- 接着使用getEntries()方法遍历ZIP文件中的每个条目。
-
- 对于每个条目,方法检查它是目录还是文件。如果是目录,则在保存目录中创建一个新目录。如果是文件,则在保存目录中创建一个新文件,并将条目的内容写入该文件
其中最重要的代码如下:
经过不断的while循环,filename的值为
/../
/../../
/../../DocSystem
/../../DocSystem/XXX.jsp
当filename为第四行的值时,由于没有过滤 ../ 这样的特殊符号,就会执行下面操作,回退到上级文件夹,再次回退到上级文件夹,创建DocSystem文件夹,将XXX.jsp 写入
补丁diff
而在最新版的MxsDoc_V2.02.43版本中,该漏洞已经被修复,我们再来查看一下最新版的代码进行一个比较,我们发现在该版本中的unZip方法已经被修改,增加了对..字符路径的规则限制
原文始发于微信公众号(念沉凡):java slip漏洞复现分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论