GHSA-5fqc-mrg8-w798

Suggest an improvement
Source
https://github.com/advisories/GHSA-5fqc-mrg8-w798
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-5fqc-mrg8-w798/GHSA-5fqc-mrg8-w798.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-5fqc-mrg8-w798
Downstream
CGA (1)
MINI (2)
Published
2026-10-02T18:53:05Z
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 filter-branch index_filter via cross-commit state persistence
Details

Summary

Dulwich's filter_branch.py CommitFilter._apply_index_filter() is vulnerable to symlink directory traversal. When processing commit history, materialized tree entries (including symlinks) persist in the working directory between commits, allowing a symlink from an ancestor commit to redirect file writes from a descendant commit to arbitrary filesystem locations.

Root Cause

_apply_index_filter() at dulwich/filter_branch.py:212 calls build_index_from_tree(".", tmp_index_path, ...) which materializes all tree entries to the current working directory. The finally block (line 229-230) only cleans up the temporary index file (os.unlink(tmp_index_path)) — NOT the filesystem files written to CWD. When process_commit() processes parents recursively first (line 260), files materialized from ancestor commits persist and affect processing of descendant commits.

On dulwich 1.2.7, build_file_from_blob() has no symlink protection, and validate_path_element only validates name patterns, not filesystem state.

Impact

An attacker can craft a malicious repository where running filter_branch with an index filter writes attacker-controlled content to arbitrary filesystem locations via symlink traversal. This achieves RCE if the write targets .git/hooks/.

Attack Scenario

  1. Attacker creates a repository where commit history (linearized) has:
    • Ancestor commit: tree entry evil (mode 120000, symlink → /target_dir)
    • Descendant commit: tree entry evil/payload (mode 100644, attacker content)
  2. Victim clones repository and runs filter_branch with an index filter
  3. process_commit() processes ancestor first → materializes evil as symlink to /target_dir in CWD
  4. CWD is NOT cleaned between commits
  5. Processing descendant: os.path.exists("./evil") → True (symlink exists). build_file_from_blob(blob, mode, "./evil/payload") → open("./evil/payload", "wb") follows intermediate symlink → writes to /target_dir/payload

Suggested Fix

Clean the CWD between commit iterations in _apply_index_filter(), or verify that no intermediate path components are symlinks before writing files.

Reported by zx (Jace)

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

Affected packages

PyPI / dulwich

Package

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0.23.1
Fixed
1.2.8

Affected versions

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-5fqc-mrg8-w798/GHSA-5fqc-mrg8-w798.json"