# Root Cause Analysis and Patch Verification - GHSA-5rjg-fvgr-3xxf / CVE-2025-47273

Summary
- Vulnerability: Path traversal / arbitrary file write in setuptools PackageIndex._download_url
- Affected: setuptools < 78.1.1 (confirmed on 78.1.0)
- Fixed: 78.1.1 and later
- Impact: Arbitrary file write at an attacker-chosen absolute path when a crafted URL is processed

Mechanism (What is broken)
- In setuptools/package_index.py: PackageIndex._download_url(url, tmpdir) derives a download filename from the URL via egg_info_for_url(url). It unquotes the last URL path segment into variable name, performs only a weak sanitization of '..' and backslashes, then computes filename = os.path.join(tmpdir, name).
- On POSIX, if name is an absolute path like /tmp/pwn.txt (for example by using an encoded segment %2Ftmp%2Fpwn.txt), os.path.join(tmpdir, name) discards tmpdir and returns the absolute name. The subsequent download writes content to that absolute path.

Proof of Vulnerability
- On setuptools 78.1.0, calling PackageIndex._download_url with a URL such as http://127.0.0.1:<port>/%2Ftmp%2Fsetuptools_pwned.txt leads to a write to /tmp/setuptools_pwned.txt instead of the intended tmpdir.
- Evidence captured in logs/poc_vulnerable.jsonl:
  {"setuptools_version":"78.1.0", "downloaded_to":"/tmp/setuptools_pwned.txt", "abs_file_exists":true, "abs_file_content_matches":true}

Patched Behavior (Latest)
- Installing setuptools 78.1.1 and then upgrading to latest (per policy) removes/deprecates package_index in recent releases. In our environment, importing setuptools.package_index fails (module removed), so the vulnerable code path is unavailable, effectively blocking exploitation through this API.
- Our script therefore records patched_all_blocked = true with error import_failed in all 10 bypass attempts.

Bypass Exploration (10+ distinct attempts)
We tested the following URL last-segment payloads on a patched environment to evaluate whether any path tricks still lead to absolute writes:
1. %2Ftmp%2Fsetuptools_pwned_p1.txt (absolute /tmp)
2. //tmp/setuptools_pwned_p3.txt (double slash)
3. /tmp//setuptools_pwned_p4.txt (redundant slash)
4. /tmp/./setuptools_pwned_p7.txt (dot segment)
5. %2Fvar%2Ftmp%2Fsetuptools_pwned_p5.txt (alternate dir)
6. %2Ftmp%2F..%2Fetc%2Fsetuptools_pwned_p6.txt (traversal attempt)
7. %2Ftmp%2Fsetuptools_pwned_p2.txt (absolute repeat)
8. %2Ftmp%2Fspace%20in%20name_p11.txt (space)
9. %255Ctmp%255Cwinsep_p12.txt (encoded backslashes)
10. C:%5Ctmp%5Cwin_drive_p13.txt (Windows drive)

Result: All attempts were blocked because the vulnerable module is no longer importable in the latest setuptools. No files were written to absolute paths via this code path. patched_all_blocked=true.

Threat Model Considerations
- Access: An attacker able to influence URLs consumed by PackageIndex (e.g., via a malicious HTML page on a package index, or any code path that feeds untrusted URLs to PackageIndex._download_url).
- Gain: Arbitrary file write with process privileges; can be escalated to RCE if writing to import paths, configuration, or authorized_keys, etc.
- In the patched/latest environment, access to this code path is not available; threat mitigated by removal/deprecation.

Patch Diffing Notes
- The fix described by upstream states additional sanitization around name and/or ensuring that absolute names do not escape tmpdir. Upstream commit reference: 250a6d1.
- In the latest release lines, setuptools.package_index is removed/absent, making this particular API not reachable. Latest fixes therefore fully mitigate this vector.

How to Interpret Our Artifacts
- logs/poc_vulnerable.jsonl: Shows writing to /tmp/setuptools_pwned.txt on 78.1.0.
- logs/poc_patched.jsonl: Shows import_failed for setuptools.package_index on latest setuptools (module removed), hence exploitation blocked.
- logs/result.json: JSON summary with reproduced=true, patched_all_blocked=true, counts of attempts and observed versions.

Environment and Idempotency
- The reproduction_steps.sh script is self-contained and idempotent: it creates its own virtualenv, installs specific setuptools versions, and writes all artifacts to logs/. Running twice yields consistent results with exit code 0.

Conclusion
- Vulnerability reproduced on affected version (78.1.0) with concrete file-write evidence.
- Latest patched environment blocks the vulnerable path; our bypass attempts (10) confirm no observable absolute writes through this API.
