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…