GHSA-cm62-gvxx-vmxx
Dulwich: Symlink directory traversal in stash pop allows arbitrary file write via intermediate directory symlinks
- Risk score
- 57/100
- CVSS
- 8.6 v3.1
- Published
- 2 Oct 2026
Open in the live forecast Today's forecast
What to know
- What it is
- Dulwich's stash.py:pop() function is vulnerable to symlink directory traversal, allowing an attacker to write arbitrary files outside the repository worktree when a victim pops a stash in a malicious repository. The pop() function at dulwich/stash.py:236 uses os.path.exists(parent_dir) to check if a parent directory e…
- Affected
- dulwich (pip) >= 0.22.5, <= 1.2.7
- Fix
- Upgrade dulwich to 1.2.8 or later.
- Exploitation
- No known exploitation
Why it is Yellow
- CVSS 8.6 high
- On your watchlist: PyPI
Affected packages
| Package | Ecosystem | Vulnerable | Fixed in |
|---|---|---|---|
| dulwich | pip | >= 0.22.5, <= 1.2.7 | 1.2.8 |
References
- https://github.com/advisories/GHSA-cm62-gvxx-vmxx
- https://github.com/jelmer/dulwich/security/advisories/GHSA-cm62-gvxx-vmxx
- https://github.com/jelmer/dulwich/commit/40a542cb02f9ac39a9eeac1193472903098698f9
- https://github.com/jelmer/dulwich/releases/tag/dulwich-1.2.8
- https://github.com/advisories/GHSA-cm62-gvxx-vmxx
Original description
## Summary Dulwich's `stash.py:pop()` function is vulnerable to symlink directory traversal, allowing an attacker to write arbitrary files outside the repository worktree when a victim pops a stash in a malicious repository. ## Root Cause The `pop()` function at `dulwich/stash.py:236` uses `os.path.exists(parent_dir)` to check if a parent directory exists before writing stashed files. `os.path.exists()` follows symlinks, so when an intermediate directory in the path is a symlink pointing outside the worktree (e.g., `link → ../../.git/hooks`), the check passes and subsequent file writes resolve through the symlink. The `validate_path()` function (line 228) only validates path component names against `INVALID_DOTNAMES` — it performs zero filesystem symlink detection. On dulwich 1.2.7 (latest release), `build_file_from_blob()` has no symlink protection whatsoever. ## Impact An attacker can craft a malicious repository that, when a victim clones it and performs a stash pop operation, writes attacker-controlled content to arbitrary filesystem locations. Writing to `.git/hooks/post-checkout` achieves Remote Code Execution on the victim's machine on the next git checkout operation. ## Attack Scenario 1. Attacker creates a repository with branch `main` containing `link` (symlink → `../../.git/hooks`) and branch `feature` containing `link/post-checkout` (executable payload) 2. Victim clones the repository (landing on `main` — symlink `link` exists in worktree) 3. Victim checks out `feature`, makes changes, runs `stash.push()` 4. Victim checks out `main` (restoring the `link` symlink) 5. Victim runs `stash.pop(0)` — stash contains `link/post-checkout` 6. `os.path.exists("link")` returns True (symlink to existing directory), `os.makedirs` skipped 7. `build_file_from_blob(blob, mode, "link/post-checkout")` → `open("link/post-checkout", "wb")` follows the intermediate symlink → payload written to `.git/hooks/post-checkout` 8. Next checkout operation triggers the hook → RCE ## Suggested Fix Before writing any file, verify that no component of the target path resolves through a symlink outside the worktree. Use `os.path.realpath(parent_dir)` and confirm it stays within the repository root. Alternatively, use `os.open()` with `O_NOFOLLOW` on each path component. Reported by **zx (Jace)**
Last activity 2 Oct 2026. Patch Forecast updates every hour.