Reputation: 1069
When i was unzip file.it's make file name like Movies\Hollywood\spider-Man. Actually Movies is a Folder,Hollywood is a Folder in Movies and spider-Man is file in Hollywood.
Upvotes: 0
Views: 1265
Reputation: 149
My app contains Kotlin version of A.B.'s answer fixing a Zip Path Traversal Vulnerability.
About the fix a Zip Path Traversal Vulnerability (from the website https://support.google.com/faqs/answer/9294009 ):
"Zip files can contain an entry (file or directory) having path traversal characters (“../”) in its name. If developers unzip such zip file entries without validating their name, it can potentially cause a path traversal attack, leading to writes in arbitrary directories or even overwriting the files in the app's private folders.
We recommend fixing this issue in your app by checking if canonical paths to unzipped files are underneath an expected directory. Specifically, before using a File object created using the return value of ZipEntry's getName() method, always check if the return value of File.GetCanonicalPath() belongs to the intended directory path."
/**
* unzip file with subdirectories.
*
* Refer to [stackoverflow](https://stackoverflow.com/questions/43672241/how-to-unzip-file-with-sub-directories-in-android)
* answer of [A.B.](https://stackoverflow.com/users/4914757/a-b)
* Refer to [support.google.com](https://support.google.com/faqs/answer/9294009)
* Refer to [stackoverflow](https://stackoverflow.com/questions/56303842/fixing-a-zip-path-traversal-vulnerability-in-android)
* answer of [Indra Kumar S](https://stackoverflow.com/users/3577946/indra-kumar-s)
*
* @param destination file
* @param zipFile file
* @return boolean
*/
@Throws(ZipException::class, IOException::class)
private fun extractFolder(destination: File, zipFile: File): Boolean {
val BUFFER = 8192
// File file = zipFile;
//This can throw ZipException if file is not valid zip archive
val zip = ZipFile(zipFile)
// String newPath = destination.getAbsolutePath() + File.separator + FilenameUtils.removeExtension(zipFile.getName());
val newPath = destination.absolutePath + File.separator + stripExtension(
zipFile.name.substring(zipFile.name.lastIndexOf("/") + 1)
)
//Create destination directory
File(newPath).mkdir()
val zipFileEntries: Enumeration<*> = zip.entries()
//Iterate overall zip file entries
while (zipFileEntries.hasMoreElements()) {
val entry = zipFileEntries.nextElement() as ZipEntry
val currentEntry = entry.name
val destFile = File(destination.absolutePath, currentEntry)
//
// String canonicalPath = destFile.getCanonicalPath();
try {
ensureZipPathSafety(destFile, destination)
} catch (e: Exception) {
// SecurityException
e.printStackTrace()
return false
}
// if (!canonicalPath.startsWith(destination.getAbsolutePath())) {
// SecurityException
// }
// Finish unzipping…
//
val destinationParent = destFile.parentFile
//If entry is directory create sub directory on file system
destinationParent!!.mkdirs()
if (!entry.isDirectory) {
//Copy over data into destination file
val `is` = BufferedInputStream(
zip
.getInputStream(entry)
)
var currentByte: Int
val data = ByteArray(BUFFER)
//orthodox way of copying file data using streams
val fos = FileOutputStream(destFile)
val dest = BufferedOutputStream(fos, BUFFER)
while (`is`.read(data, 0, BUFFER).also { currentByte = it } != -1) {
dest.write(data, 0, currentByte)
}
dest.flush()
dest.close()
`is`.close()
}
}
return true //some error codes etc.
}
/**
* ensure Zip Path Safety.
*
*
* Refer to [stackoverflow](https://stackoverflow.com/questions/56303842/fixing-a-zip-path-traversal-vulnerability-in-android)
* answer of [Indra Kumar S](https://stackoverflow.com/users/3577946/indra-kumar-s)
*
* @param destFile file
* @param destination directory
*/
@Throws(Exception::class)
private fun ensureZipPathSafety(destFile: File, destination: File) {
val destDirCanonicalPath = destination.canonicalPath
val destFileCanonicalPath = destFile.canonicalPath
if (!destFileCanonicalPath.startsWith(destDirCanonicalPath)) {
throw Exception(
String.format(
"Found Zip Path Traversal Vulnerability with %s",
destFileCanonicalPath
)
)
}
}
/**
* strip file name extension.
*
*
* Refer to [stackoverflow](https://stackoverflow.com/questions/7541550/remove-the-extension-of-a-file)
* answer of [palacsint](https://stackoverflow.com/users/843804/palacsint)
*
* @param s string file name
* @return string file name without extension
*/
fun stripExtension(s: String?): String {
return if (s != null && s.lastIndexOf(".") > 0) s.substring(
0,
s.lastIndexOf(".")
) else s!!
}
Upvotes: 0
Reputation: 1604
If Movies\Hollywood\spider-Man is a file while creating the zip it should be extracted as file, no matters whether it has extension or not (like *.mp4, *.flv)
You can rely on java APIs under namespace java.util.zip, the documentation link is here
Written some code which extracts only zip files, it should extract the file entry as file (no gzip, rar is supported).
private boolean extractFolder(File destination, File zipFile) throws ZipException, IOException
{
int BUFFER = 8192;
File file = zipFile;
//This can throw ZipException if file is not valid zip archive
ZipFile zip = new ZipFile(file);
String newPath = destination.getAbsolutePath() + File.separator + FilenameUtils.removeExtension(zipFile.getName());
//Create destination directory
new File(newPath).mkdir();
Enumeration zipFileEntries = zip.entries();
//Iterate overall zip file entries
while (zipFileEntries.hasMoreElements())
{
ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
String currentEntry = entry.getName();
File destFile = new File(newPath, currentEntry);
File destinationParent = destFile.getParentFile();
//If entry is directory create sub directory on file system
destinationParent.mkdirs();
if (!entry.isDirectory())
{
//Copy over data into destination file
BufferedInputStream is = new BufferedInputStream(zip
.getInputStream(entry));
int currentByte;
byte data[] = new byte[BUFFER];
//orthodox way of copying file data using streams
FileOutputStream fos = new FileOutputStream(destFile);
BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER);
while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
dest.write(data, 0, currentByte);
}
dest.flush();
dest.close();
is.close();
}
}
return true;//some error codes etc.
}
The routine does not perform any exception handling, please catch the ZipException and IOException inside driver code.
Upvotes: 3