Unsafe Unzip with spring-integration-zip 分析-【CVE-2018-1261 与 CVE-2018-1263】

栏目: 后端 · 发布时间: 6年前

内容简介:关于 CVE-2018-1263 ,见补丁浅析部分。

漏洞公告

https://pivotal.io/security/cve-2018-1261

Unsafe Unzip with spring-integration-zip 分析-【CVE-2018-1261 与 CVE-2018-1263】

关于 CVE-2018-1263 ,见补丁浅析部分。

漏洞分析

Unsafe Unzip with spring-integration-zip 分析-【CVE-2018-1261 与 CVE-2018-1263】

从简单的测试代码开始:

public final class Main {

    private static ResourceLoader resourceLoader = new DefaultResourceLoader();
    private static File path =  new File("./here/");
    public static void main(final String... args) {
        final Resource evilResource = resourceLoader.getResource("classpath:zip-malicious-traversal.zip");
        try{
            InputStream evilIS = evilResource.getInputStream();
            Message<InputStream> evilMessage = MessageBuilder.withPayload(evilIS).build();
            UnZipTransformer unZipTransformer = new UnZipTransformer();
            unZipTransformer.setWorkDirectory(path);
            unZipTransformer.afterPropertiesSet();
            unZipTransformer.transform(evilMessage);
        }catch (Exception e){
            System.out.println(e);
        }
    }
}

其中 zip-malicious-traversal.zip 即恶意的压缩包,结构如下:

Unsafe Unzip with spring-integration-zip 分析-【CVE-2018-1261 与 CVE-2018-1263】

unZipTransformer.setWorkDirectory(path); 设置了正常情况下解压目录为当前目录下的here文件夹,如上gif所示,在here文件夹中生成了good.txt。而evil.txt却逃逸出了这个限制,在G://tmp下生成了。

环境相关源码见附件。为了复现漏洞,需要在硬盘根目录下先创建一个tmp目录, zip-malicious-traversal.zip 在CVE-2018-1261\src\main\resources中。

跟踪代码,在 unZipTransformer.transform(evilMessage); 处打上断点跟入。当控制流到达 org/springframework/integration/zip/transformer/UnZipTransformer.java:112

ZipUtil.iterate(inputStream, new ZipEntryCallback() { ... });

这里会将inputStream输入, ZipEntryCallback 作为回调函数。跟入 iterate 至org/zeroturnaround/zip/ZipUtil.java。

public static void iterate(InputStream is, ZipEntryCallback action, Charset charset) {
    try {
      ZipInputStream in = null;
      if (charset == null) {
          in = new ZipInputStream(new BufferedInputStream(is));
      }
      else { ... }
      ZipEntry entry;
      while ((entry = in.getNextEntry()) != null) {
        try {
          action.process(in, entry);
        }
        ...
      }
    }
    ...
  }

在iterate中,通过 in = new ZipInputStream(new BufferedInputStream(is)); 生成了ZipInputStream对象 in ,此后通过 in.getNextEntry() 来获取对象in中的一个个条目。对于 getNextEntry() 而已,它会直接把目录给打印出来,具体可以参见 stackoverflow: How does ZipInputStream.getNextEntry() work? 。所以对于 zip-malicious-traversal.zip 而言

Unsafe Unzip with spring-integration-zip 分析-【CVE-2018-1261 与 CVE-2018-1263】

回到UnZipTransformer.java:

Unsafe Unzip with spring-integration-zip 分析-【CVE-2018-1261 与 CVE-2018-1263】

可以看到 entry 的值即为 ../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../tmp/evil.txt

此后调用回调函数process:

public void process(InputStream zipEntryInputStream, ZipEntry zipEntry) throws IOException {

    final String zipEntryName = zipEntry.getName();
    ...
    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()) { ...   }
        else {
            SpringZipUtils.copy(zipEntryInputStream, destinationFile);
            uncompressedData.put(zipEntryName, destinationFile);
        }
    }
    ...
}

tempDir 是临时生成的文件夹,而 zipEntryName 通过 zipEntry.getName() 得到,即为 ../../../ 那一串。接着通过 final File destinationFile = new File(tempDir, zipEntryName); 确定解压目录,也正是这里造成了跨越目录漏洞。接着就是调用 copy 把数据写到destinationFile处。

Unsafe Unzip with spring-integration-zip 分析-【CVE-2018-1261 与 CVE-2018-1263】

究其原因,对于getNextEntry而言, ../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../tmp 仅仅是目录名字,而对于copy操作而言, ../../../ 等将被解释为目录穿越操作从而造成了任意解压。

补丁浅析

1.0.1.RELEASE中的补丁 Disallow traversal entity in zip ,主要是在进行copy操作前,对zipEntryName进行了检查

final File destinationFile = new File(tempDir, zipEntryName);

    if (zipEntryName.contains("..") && !destinationFile.getCanonicalPath().startsWith(workDirectory.getCanonicalPath())) {
        throw new ZipException("The file " + zipEntryName + " is trying to leave the target output directory of " + workDirectory);
    }

对于恶意的压缩包,在生成了 destinationFile 后,假设值为 .\here\e401f4b8-0ecb-3f3a-76ce-5318b14d6000\..\..\tmp\evil.txt 时,通过调用 destinationFile.getCanonicalPath()... 解析成对应的正确的路径,获得它规范化的绝对路径。之后再与工作目录 workDirectory.getCanonicalPath() 比较来确定是否存在目录穿越。

之后,2018年5月11日pivotal又再次放出公告:

Unsafe Unzip with spring-integration-zip 分析-【CVE-2018-1261 与 CVE-2018-1263】

原因在于:

While the framework itself now does not write such files, it does present the errant path to the user application, which could inadvertently write the file using that path.

也就是说,生成的 destinationFile 其实是错误的,尽管框架本身不会有问题不会出现目录遍历漏洞,但是对于应用而言,可能之后直接使用了 destinationFile 这个路径来进行操作从而导致错误。因此在1.0.2.RELEASE版本中的补丁中 [Dissallow traversal entry even for byte[] ,直接在生成 destinationFile 时做了检查:

final File destinationFile = checkPath(message, zipEntryName);

Unsafe Unzip with spring-integration-zip 分析-【CVE-2018-1261 与 CVE-2018-1263】

除此之外,在 Remove unnecessary check for the .. 中还将 zipEntryName.contains("..") 的判断删除,因为认为是不必要的。

漏洞考古

类似的压缩文件目录遍历漏洞以前也出现不少,列举几个。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

数据密集型应用系统设计

数据密集型应用系统设计

Martin Kleppmann / 赵军平、李三平、吕云松、耿煜 / 中国电力出版社 / 2018-9-1 / 128

全书分为三大部分: 第一部分,主要讨论有关增强数据密集型应用系统所需的若干基本原则。首先开篇第1章即瞄准目标:可靠性、可扩展性与可维护性,如何认识这些问题以及如何达成目标。第2章我们比较了多种不同的数据模型和查询语言,讨论各自的适用场景。接下来第3章主要针对存储引擎,即数据库是如何安排磁盘结构从而提高检索效率。第4章转向数据编码(序列化)方面,包括常见模式的演化历程。 第二部分,我们将......一起来看看 《数据密集型应用系统设计》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具