Skip to content

Back to the catalog

Plugin4Shell - Zero Click RCE Vulnerability found in top 4 most popular coding agents, millions of agents affected
air.securitywriteup

Plugin4Shell - Zero Click RCE Vulnerability found in top 4 most popular coding agents, millions of agents affected

Air Security researchers found that AI coding agents (Claude Code, Codex, GitHub Copilot, Gemini CLI) that SHA-pin plugins from marketplaces never verify the checked-out commit actually matches the pin, letting an attacker who controls a plugin's upstream repo create a branch named after the 40-hex SHA (or, for Gemini CLI, named FETCH_HEAD) so git resolves the ref to attacker-controlled code instead of the pinned commit. Because agents auto-update plugins in the background, this yields zero-click RCE against already-installed, previously-vetted plugins even when marketplaces do everything the pinning security model asks; Anthropic and OpenAI patched after disclosure, Microsoft has not patched GitHub Copilot, and Google will not patch the deprecated Gemini CLI.

TL;DR SHA-pinning in Claude Code, Codex, GitHub Copilot and Gemini CLI can be silently bypassed via git ref ambiguity (a branch named like the pinned SHA, or FETCH_HEAD), enabling zero-click RCE via malicious plugin auto-updates.
Document

What to do

The summary states who patched but not the version boundaries or the fix. Update to at least:

  • Claude Code — 2.1.179 (Anthropic patched after disclosure)
  • Codex — 0.146.0 (OpenAI patched after disclosure)
  • GitHub Copilot — Microsoft has shipped no fix; no patch available.
  • Gemini CLI — deprecated, will not be patched; migrate to Antigravity, which has no marketplace plugin SHA pinning and so is not reachable by this attack.

The page gives the one-line agent-side assertion that closes both variants — it must run after checkout and compare the resolved HEAD, not the requested ref:

test "$(git rev-parse HEAD)" = "<pinned-sha>" || abort

How it works (detail the summary omits)

The vulnerable install sequence is a plain clone plus checkout:

git clone <plugin repo> ./
git checkout aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

When a name is both a valid ref and an object id, git prefers the ref, emitting only a refname is ambiguous warning — so a default branch named as the 40-hex SHA wins over the actual commit. The branch-name variant only works where a host allows 40-hex branch names: git check-ref-format accepts them, Bitbucket and self-hosted git allow them, GitHub rejects them outright. Anthropic's own docs list Bitbucket and self-hosted git as valid marketplace backends, so this is a supported configuration, not an edge case.

Gemini CLI's variant differs — it fetches the right commit into .git/FETCH_HEAD but git checkout FETCH_HEAD resolves to a default branch literally named FETCH_HEAD if one exists, discarding the fetched commit.

Preconditions

The victim only needs a previously-vetted, marketplace-pinned plugin already installed. Background auto-update — the default in Claude Code and Codex — re-runs the same checkout when the marketplace bumps the pin, making it zero-click. The attacker needs control of the plugin's upstream repo (via Method 1: publish-then-poison, or Method 2: repo takeover), not the marketplace.

Caveats

A marketplace cannot fully close this — the pin resolves client-side. A marketplace can blunt the branch-name variant by allowing GitHub-only hosts, but that bans officially-supported backends and does nothing for the Gemini FETCH_HEAD variant. (The "Air Marketplace/Air Filter customers unaffected" and demo pitch are vendor framing.)

Organisations named here

  • Plugin4Shell
  • RepoJacking
  • The Story of Skills

Technologies named here

  • Air Marketplace
  • Claude Code
  • OpenAI Codex
  • Air Filter
  • GitHub Copilot
  • git
  • Gemini CLI
  • Google Antigravity
  • Codex

Techniques named here

  • Plugin4Shell - FETCH_HEAD checkout bypass
  • Zero-click compromise via plugin auto-update
  • Method 1
  • Method 2

Threat actors named here

  • SkillJacking