8000 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
[go: up one dir, main page]

Skip to content

Path traversal in mz_path_combine_safe(): entry names whose second character is ':' are stripped as drive letters (regression introduced by fcc8858) #1044

Closed

Description

@SunQingyuan0

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)
  • pre-fcc8858 build (a6c8664): same, 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).

poc-b003.zip

Activity

  1. ShraddhaZoman commented on Sep 6, 2026

    @ShraddhaZoman
    Contributor

    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.

  2. SunQingyuan0 commented on Sep 6, 2026

    @SunQingyuan0
    Author

    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.

  3. nmoinvaz commented on Sep 6, 2026

    @nmoinvaz
    Member

    @SunQingyuan0 send me the money.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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

      0