GCVE Workshop - 22 September 2026 (14:00-18:00), Luxembourg Before The Vulnopticon Conference - Registration

GHSA-6P8H-3WGX-97GF

Vulnerability from github – Published: 2026-07-24 16:42 – Updated: 2026-07-24 16:42
VLAI
Summary
GitPython: Incomplete unsafe_git_clone_options denylist omits --template enabling arbitrary command execution via clone hooks
Details

Summary

GitPython's unsafe_git_clone_options denylist omits --template. git clone --template=<dir> copies <dir>/hooks/ into the new repository and runs them (post-checkout fires during clone), so a caller who can influence clone options can achieve arbitrary command execution in the default allow_unsafe_options=False configuration.

Root Cause

base.py:145-152 defines unsafe_git_clone_options = ["--upload-pack","-u","--config","-c"]--template is absent. The guard candidate ['--template'] passes check_unsafe_options (verified). git copies the hook directory and executes post-checkout at checkout time. git's protocol.allow/GIT_ALLOW_PROTOCOL do not gate --template; the incomplete denylist is the only defense.

Impact

Arbitrary OS command execution during clone (default config). Requires an attacker-readable directory containing an executable hook — a genuine second precondition (realistic via shared filesystems, upload dirs, /tmp, or attacker-writable network paths), reflected as AC:H.

Proof of Concept

# attacker stages <dir>/hooks/post-checkout (chmod +x)
from git import Repo
Repo.clone_from(src, dst, template='<dir>')   # post-checkout hook executes -> marker created (verified)

Attack Chain

  1. Setup: attacker stages <dir>/hooks/post-checkout (chmod +x). Guard: n/a (filesystem).
  2. Entry: Repo.clone_from(url, path, template='<dir>'). Guard: check_unsafe_options(candidates=['--template'], unsafe=unsafe_git_clone_options). Bypass proof: --template not on the denylist -> passes (verified candidate ['--template'], no error).
  3. Sink: git copies the hook and executes post-checkout at checkout. Impact: ACE, default config (verified marker created).

Bypass Evidence

Live-verified on HEAD (tag 3.1.53): guard candidate ['--template'] passed with no error; staged post-checkout hook executed during clone_from, creating the marker. Independent of the value-smuggle bypass (--template is a legitimate long option that survives any single-char-value fix). Not covered by any existing advisory.

Affected Versions

<= 3.1.53

Suggested Fix

Add --template (and audit for other hook/exec-influencing options) to unsafe_git_clone_options.


Reported by zx (Jace) — GitHub: @manus-use

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 3.1.53"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "GitPython"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "3.1.54"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-184",
      "CWE-78"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-24T16:42:09Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "## Summary\nGitPython\u0027s `unsafe_git_clone_options` denylist omits `--template`. `git clone --template=\u003cdir\u003e` copies `\u003cdir\u003e/hooks/` into the new repository and runs them (`post-checkout` fires during clone), so a caller who can influence clone options can achieve arbitrary command execution in the default `allow_unsafe_options=False` configuration.\n\n## Root Cause\n`base.py:145-152` defines `unsafe_git_clone_options = [\"--upload-pack\",\"-u\",\"--config\",\"-c\"]` \u2014 `--template` is absent. The guard candidate `[\u0027--template\u0027]` passes `check_unsafe_options` (verified). git copies the hook directory and executes `post-checkout` at checkout time. git\u0027s `protocol.allow`/`GIT_ALLOW_PROTOCOL` do not gate `--template`; the incomplete denylist is the only defense.\n\n## Impact\nArbitrary OS command execution during clone (default config). Requires an attacker-readable directory containing an executable hook \u2014 a genuine second precondition (realistic via shared filesystems, upload dirs, `/tmp`, or attacker-writable network paths), reflected as AC:H.\n\n## Proof of Concept\n```python\n# attacker stages \u003cdir\u003e/hooks/post-checkout (chmod +x)\nfrom git import Repo\nRepo.clone_from(src, dst, template=\u0027\u003cdir\u003e\u0027)   # post-checkout hook executes -\u003e marker created (verified)\n```\n\n## Attack Chain\n1. Setup: attacker stages `\u003cdir\u003e/hooks/post-checkout` (chmod +x). Guard: n/a (filesystem).\n2. Entry: `Repo.clone_from(url, path, template=\u0027\u003cdir\u003e\u0027)`. Guard: `check_unsafe_options(candidates=[\u0027--template\u0027], unsafe=unsafe_git_clone_options)`. Bypass proof: `--template` not on the denylist -\u003e passes (verified candidate `[\u0027--template\u0027]`, no error).\n3. Sink: git copies the hook and executes `post-checkout` at checkout. Impact: ACE, default config (verified marker created).\n\n## Bypass Evidence\nLive-verified on HEAD (tag 3.1.53): guard candidate `[\u0027--template\u0027]` passed with no error; staged `post-checkout` hook executed during `clone_from`, creating the marker. Independent of the value-smuggle bypass (`--template` is a legitimate long option that survives any single-char-value fix). Not covered by any existing advisory.\n\n## Affected Versions\n`\u003c= 3.1.53`\n\n## Suggested Fix\nAdd `--template` (and audit for other hook/exec-influencing options) to `unsafe_git_clone_options`.\n\n---\nReported by **zx (Jace)** \u2014 GitHub: @manus-use",
  "id": "GHSA-6p8h-3wgx-97gf",
  "modified": "2026-07-24T16:42:09Z",
  "published": "2026-07-24T16:42:09Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-6p8h-3wgx-97gf"
    },
    {
      "type": "WEB",
      "url": "https://github.com/gitpython-developers/GitPython/pull/2180"
    },
    {
      "type": "WEB",
      "url": "https://github.com/gitpython-developers/GitPython/commit/ffcb5359e87619f4fe4a70a4aff5f08c5580ba97"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/gitpython-developers/GitPython"
    },
    {
      "type": "WEB",
      "url": "https://github.com/gitpython-developers/GitPython/releases/tag/3.1.54"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "GitPython: Incomplete unsafe_git_clone_options denylist omits --template enabling arbitrary command execution via clone hooks"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…