Path traversal in mz_path_combine_safe(): entry names whose second character is ':' are stripped as drive letters (regression introduced by fcc8858) · Issue #1044 · zlib-ng/minizip-ng · GitHub
You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Path traversal in mz_path_combine_safe(): entry names whose second character is ':' are stripped as drive letters (regression introduced by fcc8858) #1044
Affected: current develop (commit fcc8858 and later). NOT affected: release 4.2.2 (verified contained) and pre-fcc8858 master (a6c8664, verified contained). This is a regression introduced by the fix for #1025/#1038 itself, so it should be fixed before the next release ships.
Summary
mz_path_combine_safe() (mz_os.c, added by fcc8858) treats any path component whose second character is : as a DOS drive letter and strips the first two characters — without checking that the first character is a drive letter, and without regard to the host platform. On POSIX, filenames like /:../pwned.txt and W:../pwned.txt are perfectly legal; after the bogus strip they become ../pwned.txt, and extraction writes one level above the destination directory. An explicit -d <dir> does not protect against this.
Reproduction (minizip CLI built from fcc8858 with ASan; zip contains a single stored entry named /:../pwned-b003.txt):
$ minizip -x -o -d cli-dest poc-b003.zip
Archive poc-b003.zip
Extracting cli-dest/../pwned-b003.txt <-- the tool itself prints the escaped path
$ find cli-dest .. -name 'pwned-b003.txt'
pwned-b003.txt <-- created in the PARENT of cli-dest; cli-dest is empty
Control runs with the same zip:
4.2.2 release build: file stays inside the destination as cli-dest/:../pwned-b003.txt (contained)
An independent invariant fuzzer over the same extract path found the same family within 150 seconds and minimized it to 4 bytes (/:..); a second input shape W:../x triggers with no leading separator at all. On the pre-fix tree, 55M executions of resolve-only invariant fuzzing produced zero violations, so the combine step (not resolve) is the culprit.
(PoC attached: poc-b003.zip)
Root cause
/* mz_os.c, mz_path_combine_safe() — added by fcc8858 */if (*join!=0&&join[1] ==':') /* only looks at the 2nd character */join+=2; /* strips 2 chars unconditionally */
mz_path_resolve() correctly keeps /:../x as a literal (relative, no .. at segment start issues since : is not a separator), so the strip in combine_safe is what turns a legal name into a traversal.
Suggested fix
if (*join!=0&&join[1] ==':'&&isalpha((unsigned char)*join))
join+=2;
ideally gated to hosts with DOS drive-letter semantics. Regression tests worth adding: /:../x, W:../x, names ending /:../, //://../x, and the same shapes with the UTF-8 flag off (CP437).
Hi @SunQingyuan0, thanks for the thorough writeup and PoC — the root cause and suggested fix are very clear. If you haven't already opened a PR for this, would it be okay if I submit one with your suggested fix (checking isalpha on the first character before stripping the drive-letter prefix, gated to DOS-drive-letter hosts), plus the regression tests you listed (/:../x, W:../x, trailing /:../, //://../x, and the CP437/non-UTF-8 variants)? Happy to credit your analysis in the PR description. Let me know either way.
Thanks @ShraddhaZoman — please go ahead with the PR using the fix and test list from the issue, and I'd appreciate credit for the vulnerability discovery and analysis.
Please tag me for review when it's up — I'll re-run the PoC against your branch to confirm the escape is gone before it merges.
Activity
ShraddhaZoman commentedon Sep 6, 2026
Hi @SunQingyuan0, thanks for the thorough writeup and PoC — the root cause and suggested fix are very clear. If you haven't already opened a PR for this, would it be okay if I submit one with your suggested fix (checking
isalphaon the first character before stripping the drive-letter prefix, gated to DOS-drive-letter hosts), plus the regression tests you listed (/:../x,W:../x, trailing/:../,//://../x, and the CP437/non-UTF-8 variants)? Happy to credit your analysis in the PR description. Let me know either way.SunQingyuan0 commentedon Sep 6, 2026
Thanks @ShraddhaZoman — please go ahead with the PR using the fix and test list from the issue, and I'd appreciate credit for the vulnerability discovery and analysis.
Please tag me for review when it's up — I'll re-run the PoC against your branch to confirm the escape is gone before it merges.
nmoinvaz commentedon Sep 6, 2026
@SunQingyuan0 send me the money.
Fix path traversal in mz_path_combine_safe() (#1044) (#1061)