Summary
When extracting a ZIP archive, mz_zip_reader_entry_save_file() (mz_zip_rw.c) applies the entry's Unix mode bits from external_fa directly via chmod() without masking S_ISUID, S_ISGID, or sticky bits. As a result, a crafted archive entry can cause the extracted file to be created with setuid (or setgid) permissions regardless of the extracting process's umask. If the archive is extracted by a privileged process, the resulting file may become a setuid-root binary that an unprivileged local user can execute to gain root privileges. This issue may lead to local privilege escalation.
Root Cause
mz_zip_attrib_convert() (mz_zip.c) copies the Unix mode bits stored in external_fa verbatim (*target_attrib = src_attrib) when both the source and target host systems are Unix-like, without filtering special permission bits.
mz_os_set_file_attribs() (mz_os_posix.c) passes the resulting mode directly to chmod(path, (mode_t)attributes).
mz_zip_reader_entry_save_file() invokes both functions unconditionally for every extracted entry, with no option to disable this behavior via the minizip CLI.
Reproduction
Craft a ZIP archive containing an entry whose central directory record sets the version_madeby host-system byte to Unix and external_fa to (S_ISUID | 0755) << 16, using any ordinary relative entry name. Extract the archive with minizip -x -o -d <dest>. No special flags or destination are required, as this behavior occurs during every extraction. The extracted file will have mode 4755 regardless of the current umask.
PoC : evil.zip
Command:
$ ./minizip -x -o -d ./dest ./evil.zip
Log
$ minizip -x -o -d /tmp/poc /tmp/evil.zip
minizip-ng 4.2.2 - https://github.com/zlib-ng/minizip-ng
---------------------------------------------------
-x -o -d /tmp/poc /tmp/evil.zip
Archive /tmp/evil.zip
Extracting evil_setuid
$ ls -lah /tmp/poc
total 12K
drwxr-xr-x 2 root root 4.0K Jul 24 07:57 .
drwxrwxrwt 1 root root 4.0K Jul 24 07:57 ..
-rwsr-xr-x 1 root root 21 Nov 30 1979 evil_setuid
$ minizip -l /tmp/evil.zip
minizip-ng 4.2.2 - https://github.com/zlib-ng/minizip-ng
---------------------------------------------------
-l /tmp/evil.zip
Packed Unpacked Ratio Method Attribs Date Time CRC-32 Name
------ -------- ----- ------ ------- ---- ---- ------ ----
21 21 100% stored 9ed0000 11-30-79 00:00 a4b6baa2 evil_setuid
Ref
tar option :
--no-same-owner
(x mode only) Do not extract owner and group IDs. This is the reverse of --same-owner and the default behavior if tar is run as non-root.
--no-same-permissions
(x mode only) Do not extract full permissions (SGID, SUID, sticky bit, file attributes or file flags, extended file attributes and ACLs). This is the reverse of -p and the default behavior if tar is run as non-root.
Summary
When extracting a ZIP archive,
mz_zip_reader_entry_save_file()(mz_zip_rw.c) applies the entry's Unix mode bits fromexternal_fadirectly viachmod()without maskingS_ISUID,S_ISGID, or sticky bits. As a result, a crafted archive entry can cause the extracted file to be created with setuid (or setgid) permissions regardless of the extracting process'sumask. If the archive is extracted by a privileged process, the resulting file may become a setuid-root binary that an unprivileged local user can execute to gain root privileges. This issue may lead to local privilege escalation.Root Cause
mz_zip_attrib_convert()(mz_zip.c) copies the Unix mode bits stored inexternal_faverbatim (*target_attrib = src_attrib) when both the source and target host systems are Unix-like, without filtering special permission bits.mz_os_set_file_attribs()(mz_os_posix.c) passes the resulting mode directly tochmod(path, (mode_t)attributes).mz_zip_reader_entry_save_file()invokes both functions unconditionally for every extracted entry, with no option to disable this behavior via the minizip CLI.Reproduction
Craft a ZIP archive containing an entry whose central directory record sets the
version_madebyhost-system byte to Unix andexternal_fato(S_ISUID | 0755) << 16, using any ordinary relative entry name. Extract the archive withminizip -x -o -d <dest>. No special flags or destination are required, as this behavior occurs during every extraction. The extracted file will have mode4755regardless of the currentumask.PoC : evil.zip
Command:
Log
Ref