Skip to main content
CVE-2026-73624VulnerabilityPublished Updated

shallow-backup · GitPython: Arbitrary file overwrite via git diff --output argument injection in Diffable.diff (key- and

Affects shallow-backupSource GCVE Database

0
Authoritative record:CVE-2026-73624· High 7.2

Not known to be exploited · CVSS 7.2 High : upgrade to 6.6_4 soon.

Data as of 26 Oct 2026, 17:16 UTC

CVSS 4.0 base score
7.2High
How severe the flaw is on the 0–10 CVSS scale.
EPSS
0.5% chance of exploitation in 30 days – higher than 45% of CVEsExploit Prediction Scoring System: the likelihood attackers will use this flaw soon.
Patch
Available – fixed in 6.6_4
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N

Exploitation evidence

  1. Disclosed: evidence found.

    CVE published 13 Aug 2026

  2. Detectable: no evidence on record.

    No public nuclei template indexed

  3. Weaponised: no evidence on record.

    No public exploit code indexed in Metasploit or Exploit-DB

  4. Exploited in the wild: no evidence on record.

    Not in CISA KEV and no source reports exploitation

  5. Observed by OffSeq Mirage: no evidence on record.

    No attack traffic recorded by OffSeq Mirage sensors

A missing step is not evidence of safety. Sources: cve.org (opens in a new tab), the Metasploit, Exploit-DB and nuclei-templates index as of 26 Oct 2026.

What to do

Upgrade shallow-backup to 6.6_4.

Affected software

ProductAffectedFixed in
Homebrewshallow-backuppkg:brew/shallow-backupOther threatsAffected
  • >=5.0.1 <6.6_4
Fixed in
  • 6.6_4

Check your own hosts for these packages with threat-finder (opens in a new tab), our free open-source scanner.

Description

## Summary `Diffable.diff()` forwards `**kwargs` straight into `diff`/`diff_tree` with **no** `check_unsafe_options` guard. `Diffable` is mixed into `Commit`, `Tree`, `IndexFile`, and `Submodule`, giving a broad surface. `git diff --output=<path>` writes real patch content to an attacker-chosen path, enabling arbitrary file overwrite. ## Root Cause `diff.py:188-283` builds and runs the diff command with no `check_unsafe_options` anywhere in the method (grep-confirmed). Additionally `diff.py:265` does `args.insert(0, other)`, placing the caller-supplied `other` ref BEFORE the `--` separator, so a value of `--output=/path` is parsed by git as an option — a value-only control path requiring no kwarg key. ## Impact Overwrite/corrupt any file at process privilege with attacker-chosen path (e.g. `~/.ssh/authorized_keys`, configs, lockfiles). Content is real diff/patch bytes (attacker-influenced). Per the skill's rule, controlling WHICH file is overwritten = I:H regardless of content constraints. ## Proof of Concept ```python # Key-control: commit.diff(other_commit, output='/home/app/.ssh/authorized_keys') # victim overwritten with diff (105 bytes verified) # Value-control (attacker controls only the ref string): commit.diff(other='--output=/home/app/.ssh/authorized_keys') # 14-byte victim -> 146 bytes of diff-tree output ``` ## Attack Chain 1. Entry (value-control): `commit.diff(other=<user ref>)` with `other = "--output=/home/app/.ssh/authorized_keys"`. Guard: none in `Diffable.diff`. Bypass proof: no `check_unsafe_options` in the method body (grep); `other` inserted pre-`--` at diff.py:265. 2. Sink: `git diff-tree <sha> --output=/home/app/.ssh/authorized_keys -r ...` -> git opens+truncates the target then writes diff content. Impact: overwrite/corrupt any file at process privilege (attacker chooses the path). Verified argv and victim overwrite live. ## Bypass Evidence Live-verified on HEAD (tag 3.1.53): both key-control (`output=`) and value-control (`other='--output=...'`) overwrote a victim file with real diff-tree content; argv confirmed `['git','diff-tree','<sha>','--output=/victim','-r',...]`. This is the same value-control model GHSA-956x deemed fix-worthy for `iter_commits(rev='--output=')` — but `diff` is a distinct, unguarded sink NOT touched by that fix. ## Affected Versions `<= 3.1.53` ## Suggested Fix Add `check_unsafe_options` to `Diffable.diff` (mirroring `iter_commits`/`archive`), and/or place `--end-of-options` before the `other` ref so it cannot be parsed as an option. --- Reported by **zx (Jace)** — GitHub: @manus-use

References

Community reviews

Loading reviews…