https://github.com/snyk/zip-slip-vulnerability
學(xué)習(xí)鏈接1:http://blog.nsfocus.net/zip-slip-2/
學(xué)習(xí)鏈接2:https://www.freebuf.com/vuls/180383.html
就學(xué)習(xí)做下筆記
本質(zhì):么有對壓縮包中的文件名進(jìn)行合法性校驗(yàn),直接將文件名拼接到待解壓目錄中惰爬,導(dǎo)致存在路徑遍歷風(fēng)險蜒谤。
Enumeration<ZipEntry> entries = zip.getEntries();
while (entries.hasMoreElements()) {
ZipEntry e = entries.nextElement();
//主要是這一步著觉,毫無防備的拼接
File f = new File(destinationDir, e.getName());
InputStream input = zip.getInputStream(e);
IOUtils.copy(input, write(f));
}
解決方法
String canonicalDestinationDirPath = destinationDir.getCanonicalPath();
File destinationfile = new File(destinationDir, e.getName());
//關(guān)鍵在這一步驟
String canonicalDestinationFile = destinationfile.getCanonicalPath();
if (!canonicalDestinationFile.startsWith(canonicalDestinationDirPath)) {
throw new ArchiverException("Entry is outside of the target dir: " + e.getName());
}
解釋看這里:
File 類的 getPath()、getAbsolutePath()、getCanonicalPath() 的區(qū)別
總結(jié)來說就是,getCanonicalPath() 方法返回絕對路徑坷牛,會把../
、./
這樣的符號解析掉
關(guān)于復(fù)現(xiàn)
網(wǎng)上比較常見的例子是ant和zt-zip的很澄,不過對于ant那個例子稍稍有點(diǎn)疑問京闰,比如librebuff:Zip Slip任意文件覆蓋漏洞分析這篇里面提到的unzip
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");//設(shè)置編碼格式
} 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();
}
}
這里面用到的ZipEntry是Java.util.zip.ZipEntry
,雖然ZipFile()
方法屬于ant但是本質(zhì)來說其實(shí)是路徑拼接的問題甩苛,也就是File f = new File(unZipAddress + zipEntry.getName());
這步蹂楣,所以這段代碼即使在有過補(bǔ)丁的ant包中,也是可以執(zhí)行成功的讯蒲。實(shí)際上痊土,這個漏洞在哪修復(fù)的呢:https://github.com/apache/ant/commit/e56e54565804991c62ec76dad385d2bdda8972a7#diff-32b057b8e95fa2b3f7d644552643010aR11
修復(fù)位置是自己封裝的一個解壓縮的一個文件,所以官方推薦的寫法是這樣的:
/**
* 解壓縮文件和文件夾
*
* @param zipFilepath 需要被解壓的zip文件路徑
* @param destDir 將要被解壓到哪個文件夾
* @throws BuildException
* @throws RuntimeException
*/
public static void unzip(String zipFilepath, String destDir) throws BuildException, RuntimeException {
if (!new File(zipFilepath).exists()) {
throw new RuntimeException("zip file " + zipFilepath + " does not exist.");
}
Project proj = new Project();
Expand expand = new Expand();
expand.setProject(proj);
expand.setTaskType("unzip");
expand.setTaskName("unzip");
expand.setEncoding("GBK");
expand.setSrc(new File(zipFilepath));
expand.setDest(new File(destDir));
expand.execute();
System.out.println("uncompress successed.");
}
追蹤一下就知道這個setSrc后面的exactFile()調(diào)用了補(bǔ)丁函數(shù)墨林。
如果是封裝好了赁酝,可以認(rèn)為是個組件漏洞, 不然就應(yīng)該說這個漏洞是開發(fā)者自己寫代碼不小心旭等,未經(jīng)驗(yàn)證就拼接了路徑酌呆。