Skip to content

Zip Slip via absolute-path entry names bypasses extraction path confinement (arbitrary file write) #1025

Description

@cryptcrack

Summary

When extracting a ZIP archive without specifying an explicit destination directory (mz_zip_reader_save_all() called with destination_dir == NULL, which is exactly what the minizip -x CLI does by default, i.e., when -d is not specified), an archive entry whose name is an absolute path (e.g., /etc/cron.d/evil or /home/user/.ssh/authorized_keys) is written to that absolute path instead of being confined to the intended extraction directory.

This is a variant of the classic "Zip Slip" path traversal vulnerability. However, it does not rely on the usual relative ../../ traversal pattern. The existing path-sanitization logic in mz_path_resolve() already handles ./.. components and duplicate path separators correctly. Instead, the issue is that a leading path separator, which makes the entry name an absolute path, is neither stripped nor rejected, allowing a fully qualified path to pass through unchanged.

Root Cause

  1. mz_path_resolve() (mz_os.c) normalizes paths by removing . and .. segments, but fails to check or reject leading / separators. Absolute paths remain absolute.

  2. mz_zip_reader_save_all() (mz_zip_rw.c) does not prepends a destination directory when destination_dir is NULL (the default minizip -x behavior), leaving entry paths unaltered.

Impact: Without validation for leading slashes, archive entries with absolute paths are written directly to absolute system locations, enabling Zip Slip via absolute path traversal.

Reproduction

Craft a zip with one stored entry whose name field is an absolute path pointing outside an empty, dedicated extraction directory. Run minizip -x -o with that empty directory as the working directory, without -d. The payload lands at the literal absolute path instead of under the extraction directory, confirming the entry name isn't sanitized when no destination directory is given.

PoC file: evil.zip

Command:

$ ./minizip -x ./evil.zip 

Log

$ ./minizip -x ./evil.zip 
minizip-ng 4.2.2 - https://github.com/zlib-ng/minizip-ng
---------------------------------------------------
-x ./evil.zip 
Archive ./evil.zip
Extracting /tmp/pwn.txt
$ cat /tmp/pwn.txt 
hello
$ ./minizip -l ./evil.zip 
minizip-ng 4.2.2 - https://github.com/zlib-ng/minizip-ng
---------------------------------------------------
-l ./evil.zip 
      Packed     Unpacked Ratio Method   Attribs Date     Time  CRC-32     Name
      ------     -------- ----- ------   ------- ----     ----  ------     ----
           5            5  100% stored  81a40000 11-30-79 00:00 3610a686   /tmp/pwn.txt

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions