Plugin4Shell — Multi-Vendor AI Coding Agent Plugin SHA-Pinning Bypass
Executive Summary
Plugin4Shell is a zero-click remote-code-execution flaw affecting the plugin/marketplace systems of all four major AI coding agents — Anthropic Claude Code, OpenAI Codex, GitHub Copilot, and Google Gemini CLI — disclosed by Air Security researchers Or Nevo, Dor Granat, and Niv Hoffman on 2026-09-17/18. It breaks SHA pinning, the mechanism a marketplace uses to lock an installed plugin to one reviewed commit forever after. Every affected agent checks out the pinned commit but never verifies the checkout actually landed there. An attacker who controls a plugin's upstream repository can make that checkout resolve to a malicious tree — via a branch named identically to the 40-hex pinned SHA (Claude Code, Codex, GitHub Copilot) or via a default branch literally named FETCH_HEAD (Gemini CLI) — while the pin still displays as honored.
What makes it zero-click is plugin auto-update, on by default in Claude Code and Codex: the same checkout logic re-runs in the background whenever the marketplace bumps the pinned commit, silently swapping code inside a plugin the victim already trusts and already installed. No install step, no prompt, nothing to notice. The victim only needs one plugin, installed from a marketplace they trust, that was reviewed and pinned exactly as the security model intends — doing everything right does not protect you.
Patch status: fixed in Claude Code 2.1.179 and OpenAI Codex 0.146.0. GitHub Copilot remains unpatched — Microsoft has not shipped a fix, and exposure persists wherever Copilot supports marketplaces hosted on Bitbucket or self-hosted git (GitHub.com itself structurally blocks SHA-shaped branch names). Google has deprecated Gemini CLI and will not patch it; installs under Gemini Code Assist Standard/Enterprise licensing keep running and stay exposed indefinitely, with migration to Antigravity as the only mitigation.
There is no public report of in-the-wild exploitation and no published malware indicators for Plugin4Shell as of this pack's build. This is deliberately a behavioral + exposure hunt: find vulnerable installs, watch the plugin-update/checkout mechanic itself for the anomaly the attack requires, and catch the post-exploitation behaviors — an unexpected shell spawned from git or from inside a plugin directory, credential-store access, anomalous egress — that a real exploitation attempt would produce on an EDR-visible endpoint even though the exploit's actual "flip" happens server-side, off the endpoint.
Defender priority: patch Claude Code and Codex fleet-wide today (§12, Immediate tier) — that closes two of four vendors completely. For Copilot and Gemini CLI, since no patch exists, the durable mitigation is marketplace-host restriction (GitHub-only) plus the behavioral detections in §8 (Q5/Q6/Q7 are Custom IOA candidates).Source Review & Web Hunter Notes
| Tier | Source | Key Finding | Carry Forward |
|---|---|---|---|
| 1 · Primary research | Air Security — "Plugin4Shell" | Full technical writeup: attack chain, both exploitation variants, code-level checkout commands, timeline, per-vendor patch status | Yes |
| 2 · Trade press | Help Net Security | Corroborates core mechanism; researcher quotes; independent framing of the two attack variants | Yes |
| 2 · Trade press | The Register | On-record GitHub spokesperson statement disputing exploitability on GitHub.com; Air's rebuttal re: Bitbucket/self-hosted exposure; "~90% of Fortune 500 use Copilot" (Microsoft stat); Microsoft did not respond | Yes |
| 3 · Aggregator | Mallory.ai | Cross-check of vendor patch table, timeline, and researcher names | Partial (cross-check only) |
| 1 · Vendor changelog | Claude Code official changelog | Confirms version 2.1.179 exists, dated June 16, 2026; no explicit security callout in the public entry (silent-fix pattern — common for supply-chain fixes) | Yes |
| 1 · Vendor changelog | Codex GitHub release rust-v0.146.0 | Confirms version 0.146.0 and the specific merged fix, commit-titled "Verify Git plugin SHA checkouts" (PR #34644) — direct engineering-level corroboration of the AIR report | Yes |
| 1 · Vendor blog | Google Developers Blog | Confirms Gemini CLI deprecation for free/Pro/Ultra tiers; confirms Gemini Code Assist Standard/Enterprise licenses keep Gemini CLI running unchanged, with continued model access | Yes |
| 1 · Vendor docs | Claude Code — Discover & install plugins | Confirms marketplace Git URLs include "GitLab, Bitbucket, and self-hosted servers"; confirms plugin cache path ~/.claude/plugins/cache | Yes |
| 3 · Vendor docs | OpenAI Codex — Plugins docs | General plugin-directory context; did not independently confirm an exact Codex plugin cache path | Partial (justifies a placeholder rather than a claimed path) |
Web Hunter Notes
- No CVE ID was identified for Plugin4Shell in any source surveyed as of 2026-09-18.
- A targeted search for coverage on thehackernews.com returned zero matching results at research time — noted as a coverage gap in that specific outlet, not as evidence against the vulnerability (four independent sources above corroborate it).
- GitHub's on-record position (via The Register) is that Plugin4Shell cannot be exploited on GitHub.com because it rejects 40-hex-shaped branch/tag names. Air Security's countered position — that Bitbucket-hosted and self-hosted marketplaces remain exploitable, and that this is exactly why GitHub Copilot (which supports those hosts) is still vulnerable — is carried forward as the operative risk statement in this pack, and is independently supported by Claude Code's own documentation confirming Bitbucket/self-hosted marketplace support (source 08 above).
- Prompt-injection check: all 9 saved source snapshots were scanned for directive-style text aimed at an AI agent (e.g. "ignore previous instructions," fake system/admin authority claims, instructions to fetch further URLs or run commands). None was found in any source. This matters more than usual here since the subject is an attack on AI coding agents themselves — see also the note in §18.
Hunt Brief & Attack Chain
Hunt hypotheses
| # | Hypothesis | MITRE | Telemetry | Conf |
|---|---|---|---|---|
| H1 | Vulnerable agent binaries (Claude Code, Codex, Copilot, Gemini CLI) are present and executing on managed endpoints | T1195.002 | ProcessRollup2 | Low (visibility only) |
| H2 | Version/update-check invocations expose the installed version for manual confirmation | T1195.002 | ProcessRollup2 | Low |
| H3 | Git clone/fetch/checkout operations occur inside agent plugin directories (expected, but scopes every other hunt) | T1195.002 | ProcessRollup2 | Medium |
| H4 | A given plugin path shows repeated SHA re-pin/checkout churn — the "version bump → rug-pull" cadence AIR describes | T1195.002 | ProcessRollup2 | Medium |
| H5 | A git process spawns a shell or script interpreter — the signature of a malicious post-checkout hook | T1195.002, T1059 | ProcessRollup2 | High |
| H6 | A shell/script interpreter or LOLBin executes from inside an agent plugin directory | T1195.002, T1059 | ProcessRollup2 | High |
| H7 | A process spawned by git or from a plugin path reads SSH keys, cloud credentials, .npmrc, .env, or keychain items | T1552.001, T1552.004, T1555.001 | ProcessRollup2 | High |
| H8 | An agent process resolves DNS / makes outbound connections to a destination outside the vendor API and marketplace-host allowlist, following plugin update activity | T1071.001, T1567 | DnsRequest, ProcessRollup2 | Medium |
Attack chain (per Air Security)
| Step | What happens | Telemetry | Hunt angle |
|---|---|---|---|
| 1. Plant | Attacker publishes a genuinely benign plugin, pinned at commit aaa…aaa; it passes marketplace review | None (marketplace-side, pre-endpoint) | Out of EDR scope — marketplace review process, not this pack |
| 2. Adoption | Users install the plugin; every install pins to the reviewed aaa…aaa commit | ProcessRollup2 (git clone/checkout) | H3 |
| 3. Version bump | Attacker ships a routine, still-benign update; marketplace re-pins to new commit bbb…bbb | ProcessRollup2 (git fetch/checkout) | H3, H4 |
| 4. Rug-pull | Attacker creates a branch named bbb…bbb (or, for Gemini CLI, a default branch named FETCH_HEAD), sets it default, and points it at malicious code — the pinned commit object itself can stay untouched | None (git-server-side; no endpoint telemetry sees this step) | Documented gap — see §15 |
| 5. Auto-update to RCE | Background auto-update re-runs git checkout <pin>; git resolves the SHA-shaped name to the branch instead of the commit object; malicious code lands and executes with no user interaction | ProcessRollup2 (git checkout, then child process) | H4, H5, H6, H7, H8 |
Consolidated IOC Table
Plugin4Shell has no published campaign IOCs — no malware hashes, no C2 domains/IPs, no confirmed in-the-wild exploitation. The rows below are exposure and behavioral indicators only, honestly labelled as such.
| Type | Value | Confidence | Action | Context | Expiry |
|---|---|---|---|---|---|
| software-version | Claude Code < 2.1.179 | High | hunt | Vulnerable to Plugin4Shell SHA-pinning bypass | n/a — structural until patched fleet-wide |
| software-version | OpenAI Codex < 0.146.0 | High | hunt | Vulnerable; fix landed in commit "Verify Git plugin SHA checkouts" | n/a |
| software-version | GitHub Copilot, any version, with marketplace plugins enabled | High | hunt / enrich | No patch shipped; exposure confirmed where Copilot supports Bitbucket / self-hosted marketplaces | n/a — reassess when Microsoft ships a fix |
| software-version | Gemini CLI, any version | High | hunt | Deprecated by Google; will not be patched; Standard/Enterprise Gemini Code Assist licenses keep it running | n/a — indefinite |
| behavioral | git checkout inside a plugin directory resolving to a branch rather than the pinned commit object | Medium | hunt | Core exploitation mechanic; not independently observable via EDR (see §15 gap) | n/a |
| filename-pattern | Plugin install/cache path segments (e.g. ~/.claude/plugins/cache for Claude Code; exact Codex/Copilot/Gemini equivalents not confirmed in sources — REPLACE_WITH_your-tenant's plugin path) | Medium | pivot | Scoping value for §8 queries | n/a |
| process-name | Agent binaries (claude, codex, gemini, copilot) as parents of shell/script interpreters | Medium | hunt | Baseline for behavioral queries; high native FP (agents legitimately spawn shells) | n/a |
Affected Surface & Telemetry Matrix
| Surface | Required Telemetry | Priority | Gap Risk |
|---|---|---|---|
| Endpoints running Claude Code | ProcessRollup2, DnsRequest, NetworkConnectIP4 | Critical | Low — standard EDR coverage |
| Endpoints running OpenAI Codex | ProcessRollup2, DnsRequest, NetworkConnectIP4 | Critical | Low |
| Endpoints running Gemini CLI (Enterprise/Standard-licensed, still supported) | ProcessRollup2, DnsRequest, NetworkConnectIP4 | High | Medium — smaller install base, but zero patch path exists |
| Endpoints running GitHub Copilot with plugin marketplaces enabled | ProcessRollup2, DnsRequest, NetworkConnectIP4 | High | Medium — ~90% of Fortune 500 run Copilot per Microsoft (source 03); unpatched |
| Software/version inventory | Falcon Discover / Spotlight app inventory, RTR | Critical | High — Falcon process telemetry has no semantic-version field; §8 Q1/Q2 are visibility proxies only, not a substitute for inventory |
| Plugin marketplace git infrastructure (Bitbucket / self-hosted hosts) | Git server audit logs — outside Falcon's scope | Medium | High — the actual malicious branch creation happens server-side; no EDR telemetry observes it (see §15) |
ATT&CK Mapping
| Tactic | Technique ID | Technique Name | Observed Behavior |
|---|---|---|---|
| Initial Access | T1195.002 | Supply Chain Compromise: Compromise Software Supply Chain | Malicious plugin publication or repository takeover swaps trusted, SHA-pinned plugin code via the branch-name / FETCH_HEAD checkout-resolution bypass |
| Execution | T1059 (.001/.003/.004/.006) | Command and Scripting Interpreter | Malicious plugin payload or post-checkout git hook launches a shell or script interpreter |
| Execution | T1204 — N/A | User Execution | Explicitly not required — zero-click via background auto-update is the defining characteristic of this vulnerability |
| Credential Access | T1552.001 | Unsecured Credentials: Credentials In Files | Reads of .env, .npmrc, .git-credentials, cloud CLI config/token caches |
| Credential Access | T1552.004 | Unsecured Credentials: Private Keys | Reads of ~/.ssh key material |
| Credential Access | T1555.001 | Credentials from Password Stores: Keychain | security find-generic-password / find-internet-password invocations on macOS |
| Command and Control | T1071.001 | Application Layer Protocol: Web Protocols | Behavioral only — no confirmed C2 infrastructure; outbound calls from a compromised agent process to non-allowlisted hosts |
| Exfiltration | T1567 | Exfiltration Over Web Service | Data pushed out via legitimate-looking web endpoints from the compromised agent process |
Native Audit-Log Hunts
Non-CQL checks — run these directly (RTR, SSH, or a management tool) since they read information Falcon process telemetry does not expose (semantic version strings, git ref resolution, marketplace configuration).
1. Fleet version audit
Run via RTR or your software-deployment tool against every managed endpoint:
claude --version codex --version gemini --version gh copilot --version # or: gh extension list npm ls -g @anthropic-ai/claude-code npm ls -g @openai/codex
Flag: Claude Code < 2.1.179, Codex < 0.146.0, any Gemini CLI, any Copilot install with plugin/marketplace features enabled.
2. Per-plugin pin-vs-HEAD audit
This is literally the check the vulnerable agents omit — running it out-of-band is the strongest manual verification available. For each installed plugin directory, compare the resolved commit against the pin recorded in the marketplace's manifest/lock entry for that plugin:
git -C <plugin_dir> rev-parse HEAD # Compare the output against the SHA the marketplace manifest recorded as pinned # for this plugin. Any mismatch is a direct compromise indicator.
3. SHA-shaped branch / bad-default-branch audit
Run against any plugin repository your organization controls or mirrors:
git ls-remote --heads <repo> | grep -E '[0-9a-f]{40}' # SHA-shaped branch names
git remote show <repo> | grep 'HEAD branch' # must never be a 40-hex name or "FETCH_HEAD"
4. Marketplace host inventory
Enumerate configured plugin marketplaces per agent (Claude Code: ~/.claude/settings.json marketplace entries; check equivalent configuration for Codex/Copilot in your environment) and flag any entry hosted on Bitbucket or a self-hosted git server as elevated risk until the corresponding agent is confirmed patched.
CrowdStrike LogScale CQL Hunt Queries
Looks for: any execution of a Claude Code, Codex, Gemini CLI, or Copilot binary — the host population every other query in this pack is scoped against.
// HUNT: AI Coding Agent Binary Inventory (Plugin4Shell Exposure Baseline) // MITRE: T1195.002 // CONF: low // FP: low // COST: low // REQUIRES: ProcessRollup2 // FALSE POSITIVES: none — this is an asset-visibility query, not a detection. Every hit is expected. // LOOKBACK: 7d #event_simpleName = ProcessRollup2 | FileName = /^(claude|claude-code|codex|gemini|copilot|github-copilot|gh-copilot)(\.exe)?$/i | groupBy([ComputerName, FileName, ImageFileName], function=[count(as=Executions), max(@timestamp, as=LastSeen)], limit=20000) | table([ComputerName, FileName, ImageFileName, Executions, LastSeen]) | sort(LastSeen, order=desc)
Looks for: explicit version/health-check subcommands whose CommandLine an analyst can pull to confirm the actual installed version. FP: routine manual health checks (claude doctor) and CI pipelines that print version banners are expected and benign — triage, don't alert.
// HUNT: Agent Version / Self-Update Invocation Capture (Plugin4Shell Version Validation) // MITRE: T1195.002 // CONF: low // FP: medium // COST: low // REQUIRES: ProcessRollup2 // FALSE POSITIVES: routine manual health checks and CI pipelines that print a version banner are expected and benign. // TUNING: pull the returned CommandLine into RTR or Discover/Spotlight software inventory to read the actual version string — Falcon process telemetry has no semantic-version field. // LOOKBACK: 7d #event_simpleName = ProcessRollup2 | FileName = /^(claude|claude-code|codex|gemini|copilot|github-copilot|gh-copilot)(\.exe)?$/i | CommandLine = /(--version|doctor|update\s+--check|self-update)/i | table([@timestamp, ComputerName, UserName, FileName, CommandLine]) | sort(@timestamp, order=desc)
Looks for: git clone/fetch/checkout activity scoped to a path containing "plugin" — the routine mechanic Plugin4Shell abuses. FP: fires on every legitimate plugin install and every legitimate background auto-update, which is the vast majority of hits.
// HUNT: Marketplace Git Operations Inside Agent Plugin Directories // MITRE: T1195.002 // CONF: medium // FP: high // COST: low // REQUIRES: ProcessRollup2 // FALSE POSITIVES: legitimate plugin installs and background auto-updates are the majority of hits — this is a scoping/pivot query, not a standalone alert. // TUNING: pivot from a hit here into Q5-Q7 (hook execution, plugin-path interpreter spawn, credential-store access) — only escalate a hit that also matches one of those. // LOOKBACK: 7d #event_simpleName = ProcessRollup2 | FileName = /^git(\.exe)?$/i | CommandLine = /(clone|fetch|checkout)/i | CommandLine like "*plugin*" | table([@timestamp, ComputerName, UserName, ParentBaseFileName, CommandLine]) | sort(@timestamp, order=desc)
Looks for: hosts where a plugin has been re-pinned/checked-out to a new 40-hex commit unusually often — the "version bump → rug-pull" cadence described in the attack chain (§3, steps 3-4). FP: plugins under active development legitimately re-pin often right after install; fast-release marketplaces will also show repeat hits.
// HUNT: Repeated Plugin Re-Pin / Checkout Churn (Rug-Pull Cadence Anomaly) // MITRE: T1195.002 // CONF: medium // FP: medium // COST: medium // REQUIRES: ProcessRollup2 // FALSE POSITIVES: plugins under active development re-pin often in the first days after install; fast-cadence marketplaces show repeat hits too. // TUNING: exclude the first 24h after first-seen install per host+plugin; if your marketplace publishes a release calendar, exclude known release windows. // LOOKBACK: 7d #event_simpleName = ProcessRollup2 | FileName = /^git(\.exe)?$/i | CommandLine = /checkout/i | CommandLine like "*plugin*" | CommandLine = /[0-9a-f]{40}/i | groupBy([aid, ComputerName], function=[count(as=CheckoutEvents), collect(CommandLine, limit=10)], limit=10000) | CheckoutEvents > 3 | sort(CheckoutEvents, order=desc)
Looks for: a shell or script interpreter launched directly by git — the signature of a malicious post-checkout hook running immediately after the SHA-pinning bypass lands. IOA candidate (see §9). FP: repos with legitimate git hooks (husky, lint-staged, pre-commit) spawn interpreters from a git parent as normal developer workflow.
// HUNT: Git Process Spawning a Shell or Script Interpreter (Post-Checkout Hook Execution) // MITRE: T1195.002, T1059 // CONF: high // FP: low // COST: low // REQUIRES: ProcessRollup2 // FALSE POSITIVES: repos with legitimate git hooks (husky, lint-staged, pre-commit) spawn interpreters from a git parent outside agent plugin directories. // TUNING: exclude known-good repo paths / CI runners in ImageFileName or CommandLine; correlate with Q3 to confirm the parent git process was operating inside an agent plugin directory before alerting. // LOOKBACK: 7d #event_simpleName = ProcessRollup2 | ParentBaseFileName = /^git(\.exe)?$/i | FileName = /^(powershell|pwsh|cmd|sh|bash|zsh|dash|ksh|osascript|wscript|cscript|mshta|python[23]?|perl|ruby|node|curl|wget)(\.exe)?$/i | table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, ImageFileName, CommandLine]) | sort(@timestamp, order=desc)
Looks for: a shell, script interpreter, or LOLBin whose own executing image resides inside a plugin directory — the malicious plugin's own delivered payload running. IOA candidate (see §9). FP: plugins that legitimately bundle a helper/build script executing from their own directory on first install.
// HUNT: Shell or Script Interpreter Executing From an Agent Plugin Directory // MITRE: T1195.002, T1059 // CONF: high // FP: medium // COST: low // REQUIRES: ProcessRollup2 // FALSE POSITIVES: plugins that legitimately bundle a helper script (build step, native-module postinstall) executing from their own directory on first install. // TUNING: exclude the first-install window for that plugin path (correlate against Q3's first-seen timestamp); exclude your organization's reviewed, allowlisted plugin publishers. // LOOKBACK: 7d #event_simpleName = ProcessRollup2 | ImageFileName like "*plugin*" | FileName = /^(powershell|pwsh|cmd|sh|bash|zsh|dash|ksh|osascript|wscript|cscript|mshta|python[23]?|perl|ruby|curl|wget)(\.exe)?$/i | table([@timestamp, ComputerName, UserName, ImageFileName, CommandLine]) | sort(@timestamp, order=desc)
Looks for: a process spawned by git or executing from a plugin path that references SSH keys, cloud credential files, or macOS keychain items — the payoff step of a real compromise. IOA candidate (see §9). FP: legitimate developer tooling reads these same paths constantly; this query is pre-scoped (git/plugin-path parent only) specifically to suppress that noise.
// HUNT: Plugin- or Git-Hook-Spawned Process Touching Credential Stores // MITRE: T1552.001, T1552.004, T1555.001 // CONF: high // FP: low // COST: low // REQUIRES: ProcessRollup2 // FALSE POSITIVES: legitimate developer tooling (aws-cli, git credential helpers, dotenv loaders) reads these same paths constantly — this query is pre-scoped to git/plugin-path parents specifically to suppress that noise. // TUNING: do not widen the parent/path scope without adding a second condition (unusual child, non-interactive session, off-hours) or it will flood on ordinary developer activity. // LOOKBACK: 7d #event_simpleName = ProcessRollup2 | (ParentBaseFileName = /^git(\.exe)?$/i or ImageFileName like "*plugin*") | CommandLine = /(aws\/credentials|aws\\credentials|\.ssh\/|\.ssh\\|\.npmrc|git-credentials|\.env|find-generic-password|find-internet-password)/i | table([@timestamp, ComputerName, UserName, ParentBaseFileName, ImageFileName, CommandLine]) | sort(@timestamp, order=desc)
Looks for: DNS resolutions from an agent process to a domain outside a vendor-API/marketplace-host allowlist. FP: coding agents legitimately reach many domains (package registries, telemetry, MCP servers) — this query is only useful once tuned to your tenant's actual allowlist. Ships Investigate-only, not an IOA candidate, until tuned.
// HUNT: Agent-Process DNS/Network Egress to Non-Allowlisted Destinations // MITRE: T1071.001, T1567 // CONF: medium // FP: medium // COST: medium // REQUIRES: DnsRequest, ProcessRollup2 // FALSE POSITIVES: coding agents legitimately reach many domains (package registries, telemetry, MCP servers, the vendor's own API) — useful only once tuned to your tenant. // TUNING: ⚠ validate in tenant — replace the domain allowlist below with your organization's approved vendor API hosts and configured marketplace host(s) before relying on this query; ship as Investigate-only until tuned. // LOOKBACK: 7d #event_simpleName = DnsRequest | rename(field=ContextProcessId_decimal, as=TargetProcessId_decimal) | join(query={#event_simpleName = ProcessRollup2 | FileName = /^(claude|claude-code|codex|gemini|copilot|github-copilot|gh-copilot)(\.exe)?$/i}, field=TargetProcessId_decimal) | not (DomainName = /(anthropic\.com|openai\.com|githubcopilot\.com|github\.com|githubusercontent\.com|google\.com|googleapis\.com|bitbucket\.org)$/i) | table([@timestamp, ComputerName, UserName, DomainName, CommandLine]) | sort(@timestamp, order=desc)
CrowdStrike Custom IOA Recommendations
Three queries clear the bar for Custom IOA promotion (high CONF, low-medium FP, low cost). Deploy all three in Detect mode fleet-wide first; do not jump to Prevent given AI agents' legitimately high baseline of shell/script activity (see §11 Validation Gates).
IOA-1 — Git Process Spawning a Shell/Script Interpreter (from Q5)
Pattern: ParentBaseFileName matches git AND the child's FileName matches a shell/script-interpreter list. Exclusions: known CI runner images; repos with reviewed git hooks (husky/lint-staged) outside agent plugin directories. Deployment: Falcon Custom IOA rule, Detect mode, 14-day burn-in.
Scheduled Search: DETECT — Git Hook Spawns Interpreter (Plugin4Shell) — fires on any git-parented interpreter process; investigate whether the parent git operation touched a plugin path (cross-reference Q3) before escalating.
Triage: (1) confirm the parent git command from CommandLine, (2) check whether the working directory is an agent plugin path, (3) pull the child process's own CommandLine and any subsequent network/file activity, (4) check Q7 for the same host/timeframe, (5) if plugin-path-confirmed and unexpected, isolate the host and proceed to §14 Containment.
IOA-2 — Interpreter/LOLBin Executing From a Plugin Path (from Q6)
Pattern: ImageFileName contains a plugin-path segment AND FileName matches a shell/interpreter/LOLBin list. Exclusions: first-install window per plugin path; allowlisted publishers. Deployment: Custom IOA, Detect mode, 14-day burn-in, then evaluate Prevent for allowlisted-publisher exclusions only.
IOA-3 — Credential-Store Access From Git/Plugin-Spawned Process (from Q7)
Pattern: parent is git or image path contains a plugin segment, AND CommandLine references a credential-store path. Exclusions: none by default — this pattern is already narrow; widen only with a documented business reason. Deployment: Custom IOA, Detect mode; strong Prevent candidate after a clean 14-day burn-in given its low native FP.
Pivot queries: host-scoped — re-run Q5/Q6/Q7 with aid = "<affected aid>" added as the first filter after #event_simpleName; environment-wide — drop the host scope and widen §8's 7-day lookback to 30 days to check for a broader campaign.
Machine-Readable IOC Appendix
Plugin4Shell ships no campaign IOCs. The grid below is grouped for quick-copy convenience — vulnerable/fixed version strings, plugin path patterns, and process names — not a blocklist of malicious infrastructure.
type,value,action,severity,expiration,description,tags filename,REPLACE_WITH_your-tenant-plugin-cache-path,hunt,medium,2027-03-18,Plugin4Shell plugin-path scoping value — no campaign IOC published,supply-chain:Plugin4Shell domain,REPLACE_WITH_your-approved-marketplace-host,enrich,low,2027-03-18,Plugin4Shell allowlist entry for Q8 — populate with your tenant's actual marketplace host,supply-chain:Plugin4Shell
Claude Code: VULNERABLE < 2.1.179 | FIXED >= 2.1.179 (fixed 2026-06-17) OpenAI Codex: VULNERABLE < 0.146.0 | FIXED >= 0.146.0 (fix verified 2026-08-12) GitHub Copilot: VULNERABLE, all versions with marketplace plugins enabled — no fix shipped Gemini CLI: VULNERABLE, all versions — deprecated 2026-08-04, will not be patched
Process basenames: claude, claude-code, codex, gemini, copilot, github-copilot, gh-copilot, git Confirmed plugin cache path (Claude Code): ~/.claude/plugins/cache Codex/Copilot/Gemini plugin paths: REPLACE_WITH_confirmed-path (not published in sources surveyed — validate via native audit §7.4) Generic path-scoping wildcard used in §8 queries: *plugin*
claude --version; codex --version; gemini --version; gh copilot --version
git -C <plugin_dir> rev-parse HEAD
git ls-remote --heads <repo> | grep -E '[0-9a-f]{40}'
git remote show <repo> | grep 'HEAD branch'
Detection Validation Gates
| Gate | Requirement |
|---|---|
| Telemetry ready | ProcessRollup2 and DnsRequest broadly available across the fleet; Identity Protection license not required for any query in this pack |
| Benign baseline | Run Q1/Q2 once to establish a fleet inventory; run Q3/Q4 for 7 days in Investigate-only mode before setting the Q4 churn threshold for your environment |
| Positive tests | Validate Q5/Q6/Q7 against a benign, git-hook-heavy development repo (one using husky/lint-staged) to confirm your exclusion list actually catches it before promoting to a Custom IOA |
| Promotion | Q5, Q6, Q7 promote to Custom IOA in Detect-only mode for a minimum 14-day burn-in with zero benign hits before Prevent is even considered — AI coding agents have a legitimately high shell-spawn baseline, so this window matters more here than in a typical LOLBin hunt |
Hardening — Tiered
Patch Claude Code to ≥2.1.179 and Codex to ≥0.146.0 fleet-wide. MITRE M1051 (Update Software). Both fixes are shipped, low-risk version bumps — no compatibility rework needed. Verify: re-run §7.1's version audit; zero hosts should report a pre-fix version after rollout.
Restrict plugin marketplace hosts to GitHub-only wherever business requirements allow, for Copilot and any Claude Code/Codex marketplaces you administer. MITRE M1033 (Limit Software Installation). GitHub structurally rejects 40-hex branch names, which closes the branch-name variant of the bypass entirely for marketplaces hosted there (confirmed via GitHub's own statement, source 03, and independently via Claude Code's marketplace-host documentation, source 08). This does not help the Gemini CLI FETCH_HEAD variant. Verify: §7.4 marketplace host inventory — zero non-GitHub hosts remaining, or each one explicitly risk-accepted.
Gate or disable background plugin auto-update for GitHub Copilot and Gemini CLI specifically — the two vendors with no patch available — until each plugin's pin is manually re-verified against §7.2's pin-vs-HEAD check. MITRE M1033, M1038. ⚠ The exact configuration toggle name for auto-update was not confirmed in any source fetched for this pack — consult current vendor plugin-settings documentation before deploying; do not assume a specific config key.
Migrate Gemini CLI installations to Antigravity where feasible — Google's stated mitigation, and the only complete fix since Gemini CLI itself will not be patched. MITRE M1033. Verify: §7.1 version audit shows zero active Gemini CLI processes post-migration.
Move developer/agent credentials off long-lived static files (~/.aws/credentials, static SSH keys) onto short-lived, scoped credentials (cloud IAM role assumption, SSH certificate authorities, a secrets manager) so that even a successful Plugin4Shell exploitation yields a narrow, quickly-expiring blast radius instead of durable keys. MITRE M1041 (Encrypt Sensitive Information — data at rest for any vaulted secrets), M1026 (Privileged Account Management). Verify: CIS AWS Foundations Benchmark control on long-lived IAM access keys; audit for static SSH keys in developer home directories.
Application-control rule blocking script-interpreter execution from user-writable agent plugin directories (Windows AppLocker/WDAC; macOS/Linux equivalent via EDR prevention policy). MITRE M1038 (Execution Prevention). Verify: §8 Q6 spot-check shows zero interpreter executions from plugin paths post-deployment, apart from documented allowlist exceptions. ⚠ best-practice control — no CIS Benchmark or MS Security Baseline entry exists specifically for AI-agent plugin directories; the rule is a direct application of the general AppLocker/WDAC guidance to this new path.
Deployable Playbooks
Playbook A — Fleet-Wide Agent Version Enforcement
MITRE: T1195.002 → M1051 Estimated deploy time: 1-2 hours to script + one RTR/deployment-tool push cycle Prerequisites: RTR access or existing software-deployment tooling; network path to each vendor's package registry (npm for Claude Code/Codex) Reboot required: No
Step 1 — Audit current versions fleet-wide
What it does: establishes which hosts are below the fixed version. Why it works: you cannot enforce a patch you haven't measured compliance against.
# Run via RTR against each managed endpoint (or your fleet's equivalent) claude --version codex --version # Collect results centrally; flag Claude Code < 2.1.179 and Codex < 0.146.0
Verify: aggregate the returned versions; the count of non-compliant hosts is your rollout scope.
Rollback: none required — this step is read-only.
Step 2 — Push the update
# npm-based installs npm install -g @anthropic-ai/claude-code@latest npm install -g @openai/codex@latest # Native installer path — re-run the vendor's install/update command, then: # restart the terminal / re-launch the agent for the update to take effect
Verify: re-run Step 1's version check; expect ≥2.1.179 / ≥0.146.0 fleet-wide.
Rollback: npm install -g @anthropic-ai/claude-code@<previous-version> / @openai/codex@<previous-version> if the update breaks an unrelated workflow — note that rolling back re-exposes the host to Plugin4Shell, so treat this as a short-lived exception only, tracked to a re-patch date.
Playbook B — Marketplace Host Restriction (GitHub-Only)
MITRE: T1195.002 → M1033 Estimated deploy time: 2-4 hours Prerequisites: administrative access to each agent's marketplace configuration; inventory of currently-configured marketplace hosts (§7.4) Reboot required: No
Step 1 — Inventory configured marketplaces
# Claude Code marketplace entries live under enabledPlugins / marketplace config in: cat ~/.claude/settings.json # Review each marketplace's source URL for a non-GitHub git host (Bitbucket, GitLab, # self-hosted). Repeat against your organization's Codex/Copilot marketplace config.
Verify: a list of every non-GitHub-hosted marketplace currently trusted.
Rollback: none required — read-only.
Step 2 — Remove or risk-accept non-GitHub marketplaces
# Remove a marketplace (Claude Code): /plugin marketplace remove <marketplace-name> # If a non-GitHub marketplace is a genuine business requirement, document a formal # risk acceptance and compensate with the §8 Q5/Q6/Q7 Custom IOAs in Detect mode # specifically for hosts using that marketplace.
Verify: §7.4 marketplace host inventory returns zero non-GitHub hosts, or each remaining one is attached to a documented risk acceptance.
Rollback: re-add the marketplace with /plugin marketplace add <url> if removal breaks a required workflow; this restores the underlying exposure, so pair any rollback with the Custom IOA compensating control from Step 2.
Containment Runbook
| Phase | Actions | Owner | Evidence |
|---|---|---|---|
| Isolate | Network-contain the affected host via Falcon RTR/RTM; treat any credential the compromised agent process could have reached (cloud keys, SSH keys, git tokens, npm tokens) as compromised pending rotation; disable plugin auto-update fleet-wide as an emergency stopgap if the affected plugin is broadly installed | IR / SOC | Q5-Q8 hit details; §7.2 pin-vs-HEAD mismatch record |
| Eradicate | Remove the compromised plugin directory; confirm the git remote's default branch and resolved HEAD match the expected upstream (§7.3); reinstall the plugin fresh from a known-good, manually-verified pin; patch/upgrade the agent binary to the fixed version | IR / Endpoint Eng | Before/after §7.2 and §7.3 audit output |
| Recover | Rotate every credential the host could reach; audit downstream systems (cloud IAM, CI/CD, package registries, connected MCP servers) for anomalous access under the rotated credentials' prior values; re-enable plugin auto-update only after the fleet is confirmed patched; monitor via Q5-Q8 for 30 days post-incident | IR / Cloud & Identity Eng | Credential rotation log; 30-day Q5-Q8 monitoring results |
Detection Coverage Map
| Technique / Step | Behavior | CQL | IOA | Coverage |
|---|---|---|---|---|
| T1195.002 — Plant / Adoption | Malicious or later-hijacked plugin published and adopted via marketplace review | — | — | GAP — marketplace-side review process, no endpoint telemetry |
| T1195.002 — Version bump / Rug-pull | Branch renamed to the pinned SHA, or default branch renamed to FETCH_HEAD, on the attacker's git host | — | — | GAP — happens entirely server-side on the plugin's git host; no EDR visibility into remote repository state |
| T1195.002 — Auto-update checkout | Background auto-update re-runs git checkout against the (possibly hijacked) pin | Q3, Q4 | — | Partial — checkout activity is visible; Falcon cannot see whether git resolved the name to a ref or a commit object |
| T1059 — Malicious code execution | Post-checkout hook or plugin entrypoint runs a shell/script interpreter | Q5, Q6 | IOA-1, IOA-2 | Good |
| T1552 / T1555 — Credential access | Compromised-process reads of SSH keys, cloud credentials, keychain | Q7 | IOA-3 | Good |
| T1071 / T1567 — C2 / Exfil | Outbound calls from a compromised agent process | Q8 | — | Partial — requires tenant-specific allowlist tuning before it is reliable |
| Exposure — vulnerable version presence | Unpatched agent binaries running fleet-wide | Q1, Q2 | — | Partial — Falcon process telemetry cannot read a semantic version; true exposure measurement needs Discover/Spotlight software inventory alongside these queries |
See §11 for the full Detection Validation Gates (telemetry readiness, benign baselining, positive testing, and the promotion criteria for Q5/Q6/Q7 into Custom IOAs).
Hunt Summary Ticket
TITLE: Plugin4Shell — Multi-Vendor AI Coding Agent Plugin SHA-Pinning Bypass (0-Click RCE)
SEVERITY: High — zero-click RCE across 4 major AI coding agents; no confirmed
in-the-wild exploitation as of 2026-09-18
SCOPE: Endpoints running Claude Code < 2.1.179, OpenAI Codex < 0.146.0,
GitHub Copilot (any version, marketplace plugins enabled),
Gemini CLI (any version)
HYPOTHESIS: An attacker-controlled plugin repository resolves a SHA-pinned
checkout to a branch (Claude Code / Codex / Copilot) or to a default
branch named FETCH_HEAD (Gemini CLI), delivering malicious code
through a trusted, already-installed plugin during background
auto-update, with no user interaction required.
QUERIES: 8 CQL hunts (Q1-Q8); 3 Custom IOA candidates (Q5, Q6, Q7)
DO FIRST: 1. Patch Claude Code to >=2.1.179 and Codex to >=0.146.0 fleet-wide.
2. Run Q1 to inventory hosts running any of the four agents.
3. Restrict plugin marketplace hosts to GitHub-only wherever
feasible (§12/§13 Playbook B) — structurally blocks the
branch-name bypass variant.
FINDINGS: REPLACE_WITH_your findings after running Q1-Q8 in your tenant
GAPS: No EDR visibility into marketplace/repo-side branch creation (the
actual "rug-pull" happens off-endpoint, §15); Falcon process
telemetry has no semantic-version field, so Q1/Q2 require
cross-reference with Discover/Spotlight software inventory.
ACTIONS: Track Claude Code/Codex patch compliance; track Copilot/Gemini CLI
marketplace-host exposure until Microsoft/Google ship or formally
decline a fix; enable IOA-1/IOA-3 (Q5/Q7) in Detect mode fleet-wide.
OWNER: REPLACE_WITH_owning_team
VERSION: v0.1 (Draft) · 2026-09-18
Changelog
References
| Tier | Source | Used For | Access Date |
|---|---|---|---|
| 1 | Air Security — "Plugin4Shell - Zero Click RCE Vulnerability found in top four coding agents" | Primary technical research; attack chain; timeline; vendor patch status | 2026-09-18 |
| 2 | Help Net Security — "Zero-click RCE vulnerability hit four major AI coding agents, two remain unpatched" | Corroboration; researcher quotes | 2026-09-18 |
| 2 | The Register — "AI coding agents' 0-click RCE flaw could hand attackers keys to the kingdom" | On-record GitHub statement; Copilot exposure detail; Microsoft non-response | 2026-09-18 |
| 3 | Mallory.ai — Plugin4Shell story summary | Cross-check of vendor table, timeline, researcher names | 2026-09-18 |
| 1 | Claude Code — Official Changelog | Confirms version 2.1.179 exists (dated 2026-06-16) | 2026-09-18 |
| 1 | OpenAI Codex — GitHub Release rust-v0.146.0 | Confirms version 0.146.0 and the fix commit "Verify Git plugin SHA checkouts" | 2026-09-18 |
| 1 | Google Developers Blog — Gemini CLI to Antigravity CLI transition | Confirms Gemini CLI deprecation and Enterprise/Standard license carve-out | 2026-09-18 |
| 1 | Claude Code Docs — Discover and install prebuilt plugins | Confirms Bitbucket/self-hosted marketplace support; confirms ~/.claude/plugins/cache path | 2026-09-18 |
| 3 | OpenAI Codex Docs — Plugins | General plugin-directory context (did not confirm an exact cache path) | 2026-09-18 |
All 9 sources above were saved as plain-text snapshots at build time and scanned for prompt-injection content directed at an AI research agent. No injection attempt was found in any source. Given that Plugin4Shell's subject matter is an attack on AI coding agents themselves, this check was treated as mandatory rather than optional for every fetched page.