GHSA-8w8g-wq8h-fq33: High severity pip/dulwich vulnerability
Summary
Dulwich's porcelain.checkout(paths=[...]) code path writes files using raw os.open(filepath, OWRONLY|OCREAT|OTRUNC, mode) followed by f.write(obj.data). This code path does NOT call buildfilefromblob() at all, completely bypassing any symlink protections (including the unreleased d09f8af fix). os.open without ONOFOLLOW follows symlinks at both the target file and intermediate directories, allowing arbitrary file writes.
Root Cause
At dulwich/porcelain/init.py:5661-5675, the checkout(paths=[...]) implementation:
python filepath = checkedworktreepath(r, path) os.makedirs(os.path.dirname(filepath), existok=True) flags = os.OWRONLY | os.OCREAT | os.OTRUNC with os.fdopen(os.open(filepath, flags, mode), "wb") as f: f.write(obj.data)
checkedworktreepath() (line 601-631) only performs name validation — checking that the path doesn't start with / or \\ and that components pass INVALIDDOTNAMES checks. It performs zero filesystem symlink detection.
Impact
An attacker can craft a malicious repository that, when a victim clones it and runs checkout(paths=[...]), writes attacker-controlled content (with attacker-controlled permissions) to any filesystem location accessible to the user. Writing to .git/hooks/post-checkout achieves RCE on the next git checkout.
Attack Scenario
1. Attacker creates a repository where HEAD has trigger as a symlink (mode 120000, content ../../.git/hooks/post-checkout), and tag v1.0 has trigger as an executable file (mode 100755, content #!/bin/sh\nmaliciouspayload) 2. Victim clones the repository — worktree has trigger → ../../.git/hooks/post-checkout (a symlink) 3. Victim runs porcelain.checkout(repo, target="v1.0", paths=["trigger"]) to restore a specific file from a tag 4. checkedworktreepath(r, "trigger") passes — name validation only, no symlink check 5. os.open("trigger", OWRONLY|OCREAT|OTRUNC, 0o755) follows the symlink → opens .git/hooks/post-checkout for writing 6. f.write(obj.data) writes the malicious payload to the hook 7. Next checkout operation triggers the hook → RCE
Suggested Fix
Replace the raw os.open path with a call to buildfilefromblob (once that function is hardened against intermediate symlinks), or add explicit symlink detection: resolve the path with os.path.realpath() and verify it stays within the worktree root before opening.
Reported by zx (Jace)
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/dulwichto a version that resolves this vulnerability.Fixed in 1.2.8 - Compensating control
Harden dulwich porcelain checkout path handling by replacing the raw os.open() write path with build_file_from_blob() after that function is hardened against intermediate symlinks, or explicitly resolve the target with os.path.realpath() and verify it remains within the worktree root before opening it.
Event History
Frequently Asked Questions
Who is exposed to this issue?
Users of Dulwich who clone or otherwise use a malicious repository and then invoke porcelain.checkout with the paths argument are exposed. The vulnerable path is specifically the checkout(paths=[...]) code path.
What does an attacker need to exploit it?
An attacker needs to craft a repository that causes checkout(paths=[...]) to encounter symlinks in a target path or an intermediate directory. Victim interaction is required: the victim must use the malicious repository and run that checkout operation.
Are path-name validation checks sufficient to prevent exploitation?
No. _checked_worktree_path() validates path names, such as rejecting absolute paths and invalid dot-name components, but it does not detect filesystem symlinks. The subsequent os.open call follows symlinks because it is not opened with O_NOFOLLOW.
What is the impact if exploitation succeeds?
The checkout operation can write attacker-controlled content to an arbitrary file reachable through the followed symlink. The reported severity is high, with impacts to confidentiality, integrity, and availability.
Is there a referenced fixed release?
Yes. The references include the Dulwich 1.2.8 release and commit 9389fcb5cb9113adfc7f207d8be86a56904db3e8.