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 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.
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.
main containing link (symlink → ../../.git/hooks) and branch feature containing link/post-checkout (executable payload)main — symlink link exists in worktree)feature, makes changes, runs stash.push()main (restoring the link symlink)stash.pop(0) — stash contains link/post-checkoutos.path.exists("link") returns True (symlink to existing directory), os.makedirs skippedbuild_file_from_blob(blob, mode, "link/post-checkout") → open("link/post-checkout", "wb") follows the intermediate symlink → payload written to .git/hooks/post-checkoutBefore 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)
{
"cwe_ids": [
"CWE-22",
"CWE-59"
],
"github_reviewed": true,
"github_reviewed_at": "2026-10-02T18:52:53Z",
"nvd_published_at": null,
"severity": "HIGH"
}