Skip to content

shutil.unpack_archive() on Windows writes outside extract_dir for ZIP entries with drive-prefixed names #146581

Description

@PuH4ck3rX

Bug report

Bug description:

Summary

I found a Windows-specific issue in shutil.unpack_archive() when extracting ZIP files.

In Lib/shutil.py, the private helper _unpack_zipfile() skips names that start with / or contain .., but it does not reject or sanitize Windows drive-prefixed names such as D:/path/file.

On Windows, such names are joined into a drive-qualified path and can escape the intended extraction directory. As a result, a crafted ZIP archive can cause files to be written outside extract_dir.

Affected component

  • Lib/shutil.py
  • _unpack_zipfile()

Impact

A crafted ZIP archive can cause an arbitrary file write outside extract_dir on Windows.

Trigger condition

  • Platform: Windows
  • Archive format: ZIP
  • A ZIP entry name contains a drive prefix such as D:/...
  • extract_dir is on a different drive

Tested environment

I reproduced this on Windows with Python 3.12.8.

Minimal reproduction

I attached a minimal repro script:

  • repro_shutil_unpack_zip_windows_drive_path_min.py

The repro creates a ZIP archive containing an entry like:

D:/shutil_outside_min.txt

and then calls:

shutil.unpack_archive(str(zip_path), str(extract_dir))
With extract_dir on another drive, the file is written outside the intended extraction directory.

Root cause
The validation in _unpack_zipfile() appears incomplete for Windows path semantics. Checking only for leading / and .. is not sufficient, because drive-prefixed paths such as D:/... are still treated as rooted or drive-qualified paths by Windows path handling.

For comparison, zipfile's own extraction logic strips drive information before extraction, but shutil._unpack_zipfile() does not.

Expected behavior
ZIP entries that are absolute, drive-prefixed, or otherwise resolve outside extract_dir on Windows should be rejected or normalized so that extraction always remains within extract_dir.

Actual behavior
A crafted ZIP entry with a drive prefix can escape extract_dir and be written to another location.

Additional context
I previously reported this privately to the Python Security Response Team on March 28, 2026, but I have not received a response yet, so I am opening this issue for tracking and triage.

I also attached a short write-up:

vuln_shutil_unpack_archive_zip_windows_drive_path.md

shutil_unpack_archive_zip_windows_drive_path_min.zip

CPython versions tested on:

CPython main branch, 3.14

Operating systems tested on:

Windows

Linked PRs

Activity

  1. Shrey-N commented on Mar 29, 2026

    @Shrey-N
    Contributor

    Hiya @PuH4ck3rX I have investigated this and can confirm I was able to reproduce the bug locally on Windows.

    The current sanitization in shutil._unpack_zipfile correctly catches Posix absolute paths and .., but it completely misses Windows drive prefixed paths for example D:/file.txt, which allows the directory traversal.

    I am working on a fix now and will try to submit a Pull Request ASAP!

    Thank you for the bug report! :)

  2. added
    stdlibStandard Library Python modules in the Lib/ directory
    on Mar 29, 2026
  3. serhiy-storchaka commented on Mar 29, 2026

    @serhiy-storchaka
    Member

    Sorry, this was discussed privately, and solution was created a month ago. Three days ago I asked for opening a public issue, so the solution also can be made public. See #146591.

  4. added
    3.11only security fixes
    3.12only security fixes
    3.13only security fixes
    3.14bugs and security fixes
    3.15bugs and security fixes
    and removed
    type-bugAn unexpected behavior, bug, or error
    on Mar 29, 2026
  5. 8 remaining items

  6. added 3 commits that reference this issue on May 19, 2026
  7. StanFromIreland commented on May 19, 2026

    @StanFromIreland
    Member

    @sepastian, you're now a commit co-author :-)

  8. sepastian commented on May 20, 2026

    @sepastian
    Contributor

    Thank you very much @StanFromIreland and @serhiy-storchaka and everyone else involved!

    Sorry for the noise and thanks again for all your great work on this!

    🙏🏼

  9. added 2 commits that reference this issue on Jun 3, 2026
  10. added a commit that references this issue on Jun 7, 2026
  11. added a commit that references this issue on Jun 17, 2026
  12. added a commit that references this issue on Aug 4, 2026
  13. added 2 commits that reference this issue on Aug 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

3.10 (EOL)end of life3.11only security fixes3.12only security fixes3.13only security fixes3.14bugs and security fixes3.15bugs and security fixesOS-windowsstdlibStandard Library Python modules in the Lib/ directorytype-securityA security issue

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions