Unsafe Unzip with spring-integration-zip 分析-【CVE-2018-1261 与 CVE-2018-1263】
漏洞公告
https://pivotal.io/security/cve-2018-1261
关于 CVE-2018-1263 ,见补丁浅析部分。
漏洞分析
从简单的测试代码开始:
其中zip-malicious-traversal.zip
即恶意的压缩包,结构如下:
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
这里会将inputStream输入,ZipEntryCallback
作为回调函数。跟入iterate
至org/zeroturnaround/zip/ZipUtil.java。
在iterate中,通过in = new ZipInputStream(new BufferedInputStream(is));
生成了ZipInputStream对象in
,此后通过in.getNextEntry()
来获取对象in中的一个个条目。对于getNextEntry()
而已,它会直接把目录给打印出来,具体可以参见stackoverflow: How does ZipInputStream.getNextEntry() work?。所以对于zip-malicious-traversal.zip
而言
回到UnZipTransformer.java:
可以看到entry
的值即为../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../tmp/evil.txt
。
此后调用回调函数process:
tempDir
是临时生成的文件夹,而zipEntryName
通过zipEntry.getName()
得到,即为../../../
那一串。接着通过final File destinationFile = new File(tempDir, zipEntryName);
确定解压目录,也正是这里造成了跨越目录漏洞。接着就是调用copy
把数据写到destinationFile处。
究其原因,对于getNextEntry而言,../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../tmp
仅仅是目录名字,而对于copy操作而言,../../../
等将被解释为目录穿越操作从而造成了任意解压。
补丁浅析
1.0.1.RELEASE中的补丁 Disallow traversal entity in zip,主要是在进行copy操作前,对zipEntryName进行了检查
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.
除此之外,在 Remove unnecessary check for the ..
中还将zipEntryName.contains("..")
的判断删除,因为认为是不必要的。
漏洞考古
类似的压缩文件目录遍历漏洞以前也出现不少,列举几个。