修复Android中的Zip路径遍历漏洞

时间:2019-05-25 10:11:09

标签: android android-security

我已在Google Play商店中上传了我的应用程序,并且Google发出了“ Android安全性”警告。

在Application中,我们下载了Zip文件夹并将此Zip文件夹保存在内部存储中,然后将该文件夹解压缩到设备的内部存储中。

这是解压缩文件夹代码:

public static void doUnzip(String inputZipFile, String 
   destinationDirectory, ZipProgressListener zipProgressListener) throws 
    IOException, RuntimeException {

 Log.e(TAG, "doUnzip:inputZipFile: " + inputZipFile);
 Log.e(TAG, "doUnzip:destinationDirectory: " + destinationDirectory);

int BUFFER = 6 * 1024;
List zipFiles = new ArrayList();
  File sourceZipFile = FileUtils.createValidFile(inputZipFile);
   File unzipDestinationDirectory = 
  FileUtils.createValidFile(destinationDirectory);
unzipDestinationDirectory.mkdir();
   String newPath = unzipDestinationDirectory.getAbsolutePath() + 
 File.separator + 
     FileUtils.getFileNameWithoutExtension(sourceZipFile.getName());
  new File(newPath).mkdir();

 ZipFile zipFile;
// Open Zip file for reading
zipFile = new ZipFile(sourceZipFile, ZipFile.OPEN_READ);
int entries = zipFile.size();
int total = 0;
Log.e(TAG, "doUnzip: entries Found !!" + entries);

// Create an enumeration of the entries in the zip file
Enumeration zipFileEntries = zipFile.entries();

if (zipProgressListener != null) {
    zipProgressListener.onZipStart();
}

// Process each entry
while (zipFileEntries.hasMoreElements()) {
    // grab a zip file entry
    ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();

    String currentEntry = entry.getName();
    Log.i(TAG, "[doUnzip] " + currentEntry);

    File file = new File(newPath);

    File destFile = new File(newPath, currentEntry);
    Log.i(TAG, "doUnzip getCanonicalPath : " + 
destFile.getCanonicalPath());

    if (Build.VERSION.SDK_INT <= VERSION_CODES.LOLLIPOP) {
        Log.i(TAG, "doUnzip: LOLLIPOP");
        if 
  (!destFile.getCanonicalPath().startsWith(destinationDirectory)) {
            throw new RuntimeException(destFile.getCanonicalPath() + 
  " is outside of targetDirectory: " + destinationDirectory);
        }
    } else {
        Log.i(TAG, "doUnzip: Above ");
        if(!destFile.getCanonicalPath().contains(file.getName()) && 
    !destFile.getCanonicalPath().contains("/")){
            throw new RuntimeException(destFile.getCanonicalPath() + 
     " is outside of targetDirectory: " + destinationDirectory);
          }
      }

      if (currentEntry.endsWith(".zip")) {
        zipFiles.add(destFile.getAbsolutePath());
    }

    // grab file's parent directory structure
    File destinationParent = destFile.getParentFile();
    // create the parent directory structure if needed
    destinationParent.mkdirs();

       try {
        // extract file if not a directory
        if (!entry.isDirectory()) {
            BufferedInputStream is = new 
       BufferedInputStream(zipFile.getInputStream(entry));
            int currentByte;
            // establish buffer for writing file
            byte data[] = new byte[BUFFER];

            // write the current file to disk
            FileOutputStream fos = new FileOutputStream(destFile);
            BufferedOutputStream dest = new BufferedOutputStream(fos, 
        BUFFER);

            // read and write until last byte is encountered
            while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
                dest.write(data, 0, currentByte);
            }

            Log.e(TAG, "unzip:outPath: =>" + 
        destFile.getAbsolutePath() + "\nFile size: " + destFile.length() 
         / 1024);
            dest.flush();
            dest.close();
            is.close();
        }

        int progress = 0;
        if (zipProgressListener != null) {
            progress = (total++ * 100 / entries);
            zipProgressListener.onZipProgressUpdate(progress);
        }
        Log.e(TAG, "unzip: PROGRESS::" + progress);
      } catch (IOException ioe) {
        ioe.printStackTrace();
      }
       }
       zipFile.close();

      for (Object zipFile1 : zipFiles) {
         String zipName = (String) zipFile1;
         Log.i(TAG, "doUnzip: ");
         doUnzip(zipName, destinationDirectory + File.separator + 
         zipName.substring(0, zipName.lastIndexOf(".zip")),
            zipProgressListener);
     }

   if (zipProgressListener != null) {
    Log.i(TAG, "doUnzip: " + sourceZipFile.getName());

    zipProgressListener.onZipCompleted(destinationDirectory +
            File.separatorChar + sourceZipFile.getName().substring(0, 
  sourceZipFile.getName().lastIndexOf(".zip")));
  }

   }

这是Google警告:

此信息适用于具有不安全解压缩模式的应用程序的开发人员,这可能会导致Zip Path Traversal攻击。包含不安全的解压缩模式的易受攻击的应用程序类别的位置可以在您的应用程序的Play控制台通知中找到。

其他详细信息

Zip文件可以包含名称中包含路径遍历字符(“ ../”)的条目(文件或目录)。如果开发人员在未验证其名称的情况下解压缩此类zip文件条目,则可能会引起路径遍历攻击,从而导致在任意目录中进行写入,甚至覆盖应用程序专用文件夹中的文件。

我们建议通过检查解压缩文件的规范路径是否在预期目录下来解决此问题。具体来说,在使用通过ZipEntry的getName()方法的返回值创建的File对象之前,请始终检查File.GetCanonicalPath()的返回值是否属于预期的目录路径。例如:

InputStream is = new InputStream(untrustedFileName);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is));
while((ZipEntry ze = zis.getNextEntry()) != null) {
  File f = new File(DIR, ze.getName());
  String canonicalPath = f.getCanonicalPath();
  if (!canonicalPath.startsWith(DIR)) {
    // SecurityException
  }
  // Finish unzipping…
}

如何在Android OS-6以上版本中解决此警告?

1 个答案:

答案 0 :(得分:1)

检查这种漏洞

MoreDerived