GHSA-5fqc-mrg8-w798: Path Traversal
Summary
Dulwich's filterbranch.py CommitFilter.applyindexfilter() 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
applyindexfilter() at dulwich/filterbranch.py:212 calls buildindexfromtree(".", tmpindexpath, ...) 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(tmpindexpath)) — NOT the filesystem files written to CWD. When processcommit() processes parents recursively first (line 260), files materialized from ancestor commits persist and affect processing of descendant commits.
On dulwich 1.2.7, buildfilefromblob() has no symlink protection, and validatepathelement only validates name patterns, not filesystem state.
Impact
An attacker can craft a malicious repository where running filterbranch 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 → /targetdir) - Descendant commit: tree entry evil/payload (mode 100644, attacker content) 2. Victim clones repository and runs filterbranch with an index filter 3. processcommit() processes ancestor first → materializes evil as symlink to /targetdir in CWD 4. CWD is NOT cleaned between commits 5. Processing descendant: os.path.exists("./evil") → True (symlink exists). buildfilefromblob(blob, mode, "./evil/payload") → open("./evil/payload", "wb") follows intermediate symlink → writes to /targetdir/payload
Suggested Fix
Clean the CWD between commit iterations in applyindexfilter(), or verify that no intermediate path components are symlinks before writing files.
Reported by zx (Jace)
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/dulwichto a version that resolves this vulnerability.Fixed in 1.2.8 - Compensating control
In dulwich's filter_branch.py, clean the current working directory between commit iterations in _apply_index_filter(), or verify that no intermediate path components are symlinks before writing files.
Event History
Frequently Asked Questions
Who is exposed to this issue?
Users are exposed when they run Dulwich's filter_branch functionality with an index filter against a repository an attacker can craft or influence. The vulnerable processing materializes repository tree entries into the current working directory.
What does an attacker need to exploit it?
An attacker needs to provide a malicious repository history containing a symlink in an ancestor commit and content written in a descendant commit. They must also induce a user to run filter_branch with an index filter on that repository.
What can the attacker do if exploitation succeeds?
The persisted symlink can redirect writes from a later commit to arbitrary filesystem locations. This can result in attacker-controlled file contents being written outside the intended working directory, with high confidentiality, integrity, and availability impact.
How can I tell whether my workflow is affected?
Dulwich 1.2.7 is identified as lacking symlink protection in build_file_from_blob(). Review whether your automation or users invoke filter_branch with index filters, especially when handling untrusted repository histories.
What can I do before updating?
Do not run filter_branch index filters on untrusted repositories. If processing cannot be avoided, perform it in an isolated, disposable environment where filesystem writes cannot affect sensitive host files.