GHSA-cm62-gvxx-vmxx

Suggest an improvement
Source
https://github.com/advisories/GHSA-cm62-gvxx-vmxx
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-cm62-gvxx-vmxx/GHSA-cm62-gvxx-vmxx.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-cm62-gvxx-vmxx
Downstream
CGA (1)
MINI (2)
Published
2026-10-02T18:52:53Z
Modified
2026-10-02T19:00:05Z
Severity
  • 8.6 (High) CVSS_V3 - CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H CVSS Calculator
Summary
Dulwich: Symlink directory traversal in stash pop allows arbitrary file write via intermediate directory symlinks
Details

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)

Database specific
{
    "cwe_ids":  [
        "CWE-22",
        "CWE-59"
    ],
    "github_reviewed":  true,
    "github_reviewed_at":  "2026-10-02T18:52:53Z",
    "nvd_published_at":  null,
    "severity":  "HIGH"
}
References

Affected packages

PyPI / dulwich

Package

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0.22.5
Fixed
1.2.8

Affected versions

0.*
0.22.5
0.22.6
0.22.7
0.22.8
0.23.0
0.23.1
0.23.2
0.24.0
0.24.1
0.24.2
0.24.3
0.24.4
0.24.5
0.24.6
0.24.7
0.24.8
0.24.9
0.24.10
0.25.0
0.25.1
0.25.2
1.*
1.0.0
1.1.0
1.2.0
1.2.1
1.2.2
1.2.3
1.2.4
1.2.5
1.2.6
1.2.7

Database specific

last_known_affected_version_range
"<= 1.2.7"
source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-cm62-gvxx-vmxx/GHSA-cm62-gvxx-vmxx.json"