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
-
mz_path_resolve() (mz_os.c) normalizes paths by removing . and .. segments, but fails to check or reject leading / separators. Absolute paths remain absolute.
-
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
Summary
When extracting a ZIP archive without specifying an explicit destination directory (
mz_zip_reader_save_all()called withdestination_dir == NULL, which is exactly what theminizip -xCLI does by default, i.e., when-dis not specified), an archive entry whose name is an absolute path (e.g.,/etc/cron.d/evilor/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 inmz_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
mz_path_resolve()(mz_os.c) normalizes paths by removing.and..segments, but fails to check or reject leading/separators. Absolute paths remain absolute.mz_zip_reader_save_all()(mz_zip_rw.c) does not prepends a destination directory whendestination_dirisNULL(the defaultminizip -xbehavior), 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:
Log