# Dulwich: Symlink directory traversal in filter-branch index_filter via cross-commit state persistence

Dulwich filter_branch materializes symlinks from ancestor commits into CWD without cleanup, letting a crafted repo redirect descendant file writes to arbitrary paths, RCE via .git/hooks.

- Payout: Research
- Severity: high
- Program: Dulwich
- Found by: manus-use
- Disclosed: 2026-10-02
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-5fqc-mrg8-w798
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-5fqc-mrg8-w798
- Tags: area:oss domain:oss impact:integrity impact:rce kind:research platform:ghsa sev:high tech:python technique:chained technique:source-review vuln:path-traversal vuln:rce

**Target:** filter_branch index_filter / build_index_from_tree in dulwich Python library
**Root cause:** _apply_index_filter() materializes tree entries (including symlinks) into CWD via build_index_from_tree but only unlinks the temp index in finally, leaving filesystem files. Since process_commit() recurses parents first, an ancestor's symlink persists and redirects descendant writes; build_file_from_blob has no symlink protection.
**Steps:**
1. Craft repo: ancestor commit has tree entry `evil` mode 120000 symlink -> `/target_dir`
2. Descendant commit has tree entry `evil/payload` mode 100644 with attacker content
3. Victim clones and runs `filter_branch` with an index filter
4. process_commit() processes ancestor first, materializing `evil` symlink in CWD
5. CWD not cleaned; descendant processing sees `os.path.exists("./evil")` True and `open("./evil/payload", "wb")` follows symlink, writing to `/target_dir/payload`
**How found:** Code review of _apply_index_filter(): noticed finally block only removes tmp_index_path, not materialized files, and that process_commit recurses parents first, so filesystem state leaks across commits.
**Exploitation:** Attacker-controlled repo history causes filter_branch to write attacker content to arbitrary filesystem paths; targeting `.git/hooks/` yields RCE when hooks execute.
**Escalation:** Point symlink at `.git/hooks/` to achieve code execution.
**Insight:** When a tool materializes VCS tree entries to disk, check whether cleanup covers the working files (not just temp indexes) and whether recursive parent-first processing lets symlinks from earlier commits persist into later writes.

