Обход почтового пути

Категория OWASP: MASVS-STORAGE: Хранилище

Обзор

Уязвимость Zip Path Traversal, также известная как ZipSlip, связана с обработкой сжатых архивов. На этой странице мы демонстрируем эту уязвимость на примере формата ZIP, но аналогичные проблемы могут возникнуть в библиотеках, работающих с другими форматами, такими как TAR, RAR или 7z.

Основная причина этой проблемы заключается в том, что внутри ZIP-архивов каждый упакованный файл хранится с полным именем, которое допускает использование специальных символов, таких как косая черта и точки. Библиотека по умолчанию из пакета java.util.zip не проверяет имена записей архива на наличие символов обхода каталога ( ../ ), поэтому необходимо проявлять особую осторожность при объединении имени, извлеченного из архива, с путем к целевому каталогу. .

Очень важно проверять любые фрагменты кода или библиотеки ZIP-распаковки из внешних источников. Многие такие библиотеки уязвимы для обхода Zip Path Traversal.

Влияние

Уязвимость Zip Path Traversal может использоваться для произвольной перезаписи файлов. В зависимости от условий влияние может различаться, но во многих случаях эта уязвимость может привести к серьезным проблемам безопасности, таким как выполнение кода.

Смягчения

Чтобы устранить эту проблему, перед извлечением каждой записи всегда следует проверять, что целевой путь является дочерним по отношению к целевому каталогу. В приведенном ниже коде предполагается, что каталог назначения безопасен — доступен для записи только вашему приложению и не находится под контролем злоумышленника — в противном случае ваше приложение может быть подвержено другим уязвимостям, таким как атаки по символическим ссылкам.

companion object {
   
@Throws(IOException::class)
   
fun newFile(targetPath: File, zipEntry: ZipEntry): File {
       
val name: String = zipEntry.name
       
val f = File(targetPath, name)
       
val canonicalPath = f.canonicalPath
       
if (!canonicalPath.startsWith(
                targetPath
.canonicalPath + File.separator)) {
           
throw ZipException("Illegal name: $name")
       
}
       
return f
   
}
}
public static File newFile(File targetPath, ZipEntry zipEntry) throws IOException {
   
String name = zipEntry.getName();
   
File f = new File(targetPath, name);
   
String canonicalPath = f.getCanonicalPath();
   
if (!canonicalPath.startsWith(targetPath.getCanonicalPath() + File.separator)) {
     
throw new ZipException("Illegal name: " + name);
   
}
   
return f;
 
}

Чтобы избежать случайной перезаписи существующих файлов, перед началом процесса извлечения также следует убедиться, что каталог назначения пуст. В противном случае вы рискуете привести к сбою приложения или, в крайних случаях, к его компрометации.

@Throws(IOException::class)
fun unzip(inputStream: InputStream?, destinationDir: File) {
   
if (!destinationDir.isDirectory) {
       
throw IOException("Destination is not a directory.")
   
}
   
val files = destinationDir.list()
   
if (files != null && files.isNotEmpty()) {
       
throw IOException("Destination directory is not empty.")
   
}
   
ZipInputStream(inputStream).use { zipInputStream ->
       
var zipEntry: ZipEntry
       
while (zipInputStream.nextEntry.also { zipEntry = it } != null) {
           
val targetFile = File(destinationDir, zipEntry.name)
           
// ...
       
}
   
}
}
void unzip(final InputStream inputStream, File destinationDir)
     
throws IOException {
 
if(!destinationDir.isDirectory()) {
   
throw IOException("Destination is not a directory.");
 
}

 
String[] files = destinationDir.list();
 
if(files != null && files.length != 0) {
   
throw IOException("Destination directory is not empty.");
 
}

 
try (ZipInputStream zipInputStream = new ZipInputStream(inputStream)) {
   
ZipEntry zipEntry;
   
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
     
final File targetFile = new File(destinationDir, zipEntry);
       

   
}
 
}
}

Ресурсы

{% дословно %}

Пока рекомендаций нет.

Попытайтесь в свой аккаунт Google.

{% дословно %}