Windows path escape in setuptools legacy wheel installation
Key facts
- Attack vector
- —
- Privileges required
- —
- User interaction
- —
- Attack complexity
- —
- Impact type
- —
- Confidentiality · integrity · availability
- —
- Fix status
- Fixed
No CVSS vector from any source. Fix status from GitHub.
Summary
setuptools versions from 38.2.2 before 84.1.0 are affected. On Windows, setuptools' legacy wheel installation path trusts the namespace_packages.txt file inside a wheel's .dist-info directory, so a crafted wheel can make setuptools create an __init__.py file outside the requested destination directory (CWE-22). An attacker who supplies a wheel can influence where the file is written, but not its contents, and the file is written with the permissions of the process performing the installation.
Written with Claude Haiku 5.5 from the sources below · 2026-10-28
Description from the GitHub advisory
### Summary On Windows, attacker-controlled content in a wheel's `.dist-info/namespace_packages.txt` can cause setuptools' legacy wheel installation path to create an `__init__.py` outside the requested `destination_eggdir`. The attacker can influence the destination path, but not the contents of the created file. ### Details Tested setuptools commit: `6ead555c5fb29bc57fe6105b1bffc163f56fd558` Relevant source: `setuptools/wheel.py` SHA-256: `359b7e0d5dd8c3135653abe08a8ed5dc0352eadfd7df22cb5e2b5303b7c3cafd` The relevant call path is: `Wheel(wheel_path).install_as_egg(destination)` → `_install_as_egg` → `_convert_metadata` → `_fix_namespace_packages` A wheel can provide attacker-controlled content in: `<distribution>.dist-info/namespace_packages.txt` The namespace value is eventually used approximately as: ```python mod_dir = os.path.join(destination_eggdir, *mod.split('.')) mod_init = os.path.join(mod_dir, '__init__.py') If mod is a Windows drive-qualified absolute path containing no dots, mod.split('.') preserves it as a single component. On Windows, os.path.join(destination_eggdir, absolute_component) discards the original base directory and resolves to the attacker-supplied absolute path. The existing wheel filename and ZIP traversal checks do not validate this metadata value before it is subsequently interpreted as a filesystem path. ```markdown ### PoC The reproduction was performed entirely inside a disposable temporary directory. Layout: ```text sandbox/ destination/ outside/ input/ canary.txt ``` A synthetic wheel was created containing: `demo_pkg-1.0.dist-info/namespace_packages.txt` whose content was the absolute path of: `sandbox\outside` The real acquired setuptools implementation was then invoked: ```python setuptools.wheel.Wheel(synthetic_wheel).install_as_egg( sandbox_destination ) ``` The operation completed without exception. Expected metadata was created under: `sandbox\destination\EGG-INFO` but setuptools also created: `sandbox\outside\__init__.py` The file did not exist before execution. Its contents were: ```python __import__('pkg_resources').declare_namespace(__name__) ``` The unrelated canary remained byte-identical. The sandbox was deleted after recording the evidence. ``` ### Impact A crafted wheel can cause setuptools to create a fixed-content `__init__.py` outside the intended installation directory at a filesystem path influenced by the wheel, with the permissions of the process performing the installation. The demonstrated reproduction required the target parent directory to already exist.
Why it matters now
SSVC decision points
Not available: CISA has not published SSVC decision points for this record.
Affected products
| Vendor | Product | Affected versions | Fixed in | Status | Source |
|---|---|---|---|---|---|
| — | setuptools (PyPI) | 38.2.2 to before 84.1.0 | 84.1.0 | Patch available | GHSA-xmj3-9gf7-h52w |
- setuptools (PyPI) Vendor— Affected 38.2.2 to before 84.1.0 Fixed in 84.1.0 Patch available GHSA-xmj3-9gf7-h52w
Remediation
Upgrade setuptools to version 84.1.0 or later.
Written with Claude Haiku 5.5 from the sources below · 2026-10-28
- Fix · GHSA-xmj3-9gf7-h52w setuptools (PyPI) Fixed in 84.1.0 Advisory →
Timeline
- 2026-10-23 Threatist record created from GHSA-xmj3-9gf7-h52w
References
Sources
This product uses the NVD API but is not endorsed or certified by the NVD. Sources and licences