V5338. OWASP. Possible Zip Slip vulnerability. Potentially tainted data might be used to extract the file.

Анализатор обнаружил, что имя файла, полученное из архива, используется в качестве пути к файлам или папкам без предварительной проверки. Если в имени файла присутствуют "dot-dot-slash" последовательности, операции с файлом приведут к возникновению уязвимости Zip Slip в приложении.

Атаки этого типа выделены в отдельные категории рисков в OWASP Top 10 Application Security Risks:

Zip Slip осуществляется через передачу в приложение архива с вредоносными файлами внутри: они содержат в имени "dot-dot-slash" последовательности (../../evil.csx). Спровоцировав распаковку такого архива, злоумышленник может переписать любые файлы, к которым у приложения есть доступ.

В большинстве архиваторов и операционных систем создать файл с названием вида ../../evil.csx не получится из-за встроенных ограничений, тем не менее, существуют инструменты, допускающие эту операцию. Из-за этого атака Zip Slip и становится возможной.

Рассмотрим пример:

public void extractArchive(String destinationDir, ZipFile zip) {
    Enumeration<? extends ZipEntry> entries = zip.entries();
    while (entries.hasMoreElements()) {
        ZipEntry entry = entries.nextElement();

        File file = new File(destinationDir, entry.getName());

        InputStream input = zip.getInputStream(entry);
        IOUtils.copy(input, new WriterOutputStream(new FileWriter(file)));
    }
}

Данный метод принимает архив и распаковывает его в destinationDir. Но путь до места распаковки файла создаётся из имени записи в архиве, из-за чего данный код подвержен уязвимости Zip Slip. Если в приложение попадёт вредоносный архив, то он может быть распакован в любом месте системы, к которому приложение имеет доступ.

Для защиты от Zip Slip можно проверять, находится ли целевой путь внутри исходной директории. В примере выше рекомендуется использовать метод getCanonicalPath() для приведения пути к канонической форме (путь без относительных переходов .., . и избыточных разделителей) с последующей проверкой через startsWith(destinationDir). Это предотвратит доступ к файлам за пределами разрешённой директории, даже если путь содержит замаскированные относительные переходы, например, ../../evil.csx.

Ожидается, что процесс распаковки любого архива будет защищён. Диагностическое правило считает все архивы внешними сущностями и потенциально подозрительными.

Исправленный пример:

public void extractArchive(String destinationDir, ZipFile zip) {
    Enumeration<? extends ZipEntry> entries = zip.entries();
    while (entries.hasMoreElements()) {
        ZipEntry entry = entries.nextElement();

        File file = new File(destinationDir, entry.getName());

        if (file.getCanonicalPath().startsWith(destinationDir)) {
            InputStream input = zip.getInputStream(entry);
            IOUtils.copy(input, new WriterOutputStream(new FileWriter(file)));
        }
    }
}

Выявляемые диагностикой ошибки классифицируются согласно ГОСТ Р 71207–2024 как критические и относятся к типу: Ошибки непроверенного использования чувствительных данных (ввода пользователя, файлов, сети и пр.).

Данная диагностика классифицируется как: