Plugin4Shell: Your Coding Agent Never Verified the Plugin Hash It Pinned
security sep 26, 2026

Plugin4Shell: Your Coding Agent Never Verified the Plugin Hash It Pinned

Zero-click RCE in Claude Code, Codex, Copilot and Gemini CLI: the commit you pinned was never checked after checkout. Two vendors still have no fix. Here's the 40-minute audit.

/blog/plugin4shell-coding-agent-plugin-sha-pinning-small-shop

Note: AIR Security published the Plugin4Shell research on September 18, 2026 — eight days old as of writing. The bug was found in May 2026 and disclosed to all four vendors in June, so the patched versions have been available for a while. That is the part that should bother you: the fix has existed for months and the version on your machine is whatever your last npm install happened to grab.

There is a specific kind of security story that small shops are wired to ignore, and this is one of them.

It has a branded name. It affects four products from four of the largest companies in software. The write-up contains the phrase “supply chain.” Every instinct built by twenty years of vendor fearmongering says: this is an enterprise problem, somebody else’s patch cycle, not the thing standing between you and payroll on Friday.

Except the four products are Claude Code, OpenAI Codex, GitHub Copilot, and Gemini CLI. If you are a three-to-ten person shop that writes any software at all — a Shopify theme, an internal inventory tool, the Python script that reconciles your marketplace orders — one of those four is almost certainly running on a machine that also holds your SSH keys, your cloud credentials, your Stripe secret, and a .env file you have been meaning to clean up since February.

And the bug is not “an attacker could trick a careless developer into running something.” It is: a plugin you already reviewed, already installed, and already pinned to a specific commit can be swapped for attacker-controlled code by a background auto-update, with no click, no prompt, and no reinstall.

The pin didn’t hold. That’s the whole story. The rest is detail about why, and what you do on Monday.

The lock was real. The check was missing.

Here is the mechanism every agent plugin marketplace sells you on, and which you have probably repeated to a nervous business partner:

“We don’t just install whatever’s at the top of the repo. We pin to a specific commit hash. The 40-character SHA is a cryptographic fingerprint of the exact code we reviewed. If anyone changes a single byte, the hash changes. So the code that runs is the code we looked at.”

Every clause of that is true, and the conclusion is still wrong — because pinning to a SHA only helps if somebody checks, after the files land on disk, that the thing in the working directory is actually that commit.

AIR Security’s finding is that the agents didn’t. They ran the equivalent of git clone and git checkout <sha>, and then trusted the result. They never asked git what it had actually resolved.

Trick one: name a branch after a commit hash

Git has an ordering problem baked into git checkout. When you hand it a string, it has to decide what that string refers to — a branch, a tag, or a raw commit object. Git prefers a reference (a branch or tag name) over a bare commit when both exist and both match.

A commit hash is a 40-character hexadecimal string. Nothing stops you from creating a branch whose name is a 40-character hexadecimal string.

So: an attacker who controls a plugin repository creates a branch literally named a3f91c... — the same 40 hex characters as the commit the marketplace has pinned. That branch contains whatever they want. When the agent runs git checkout a3f91c..., git resolves it as the branch, not the commit. The working directory fills with attacker code. Every log line, every manifest, every UI element still shows the pinned SHA, because the string the agent asked for is the string it got. It just didn’t mean what everyone assumed.

Trick two: the FETCH_HEAD variant

The Gemini CLI path was slightly different. It used git fetch with an explicit ref — which is the more careful-looking pattern — but still didn’t validate what the final checkout landed on. AIR’s variant here abuses FETCH_HEAD, the special ref git writes after a fetch. If a repository’s default branch is itself named FETCH_HEAD, the checkout resolves to the branch rather than the commit that was actually fetched.

Same outcome, different door. Which is the real tell: this wasn’t one team making one careless call. Four independent codebases converged on the same unsafe assumption, which means the assumption is the industry default, not a bug in one product.

Trick three, and this is the one that matters: auto-update

A vulnerability that requires a victim to install a malicious plugin is a phishing problem. Annoying, survivable, largely a training issue.

Plugin4Shell is not that, because of one sentence in the research, quoted in Help Net Security’s write-up: “the same git checkout re-runs on background auto-update — the default in Claude Code and Codex — so when the marketplace bumps the pinned SHA, the swap reaches already-installed plugins with no user action.”

Read the attack chain the way an attacker would build it:

  1. Publish a genuinely useful plugin. A linter wrapper, a test-runner shim, a nice git helper. Make it good.
  2. Wait. Accumulate installs. Get listed. Earn the marketplace’s trust and a pinned SHA.
  3. Create the branch named after the pinned hash. Put your payload in it.
  4. Do nothing else. Auto-update propagates it to every existing installation.

No user is tricked. No user does anything. The malicious code arrives on a schedule, on machines whose owners made the correct security decision — they installed a reviewed, pinned, popular plugin from a legitimate marketplace.

This is the same structural shape as the dependency-confusion and typosquat problems in npm and PyPI, but with a much worse permission model attached, which is the next section.

What a plugin can actually reach on a small-shop dev machine

In the npm world, a malicious postinstall script is bad. In the agent world, a malicious plugin is worse, because agent plugins do not run in a sandbox with a narrow API. They run as the developer, with the developer’s environment, inside a process whose entire job is to read files, execute commands, and talk to the network.

Inventory what that actually means on the laptop or mini PC where your agent runs:

AssetTypically present?What an attacker does with it
~/.ssh/id_ed25519 and known_hostsAlmost alwaysPushes to your repos, reaches your VPS, reads your deploy targets
~/.aws/credentials, gcloud tokensCommon if you host anythingSpins up compute, reads buckets, pivots into prod
.env files in project directoriesEffectively universalStripe keys, DB passwords, webhook secrets, SMTP creds
~/.npmrc, ~/.pypirc publish tokensCommon in shops that ship packagesPublishes a poisoned release under your name
GitHub / GitLab PAT in shell configVery commonReads private repos, opens PRs, edits CI workflows
Agent’s own API keysAlwaysBurns your model budget; also a laundering channel
Browser session cookiesIf the agent has browser accessLogged-in access to your admin panels
The mounted network shareIf your dev box maps the office NASEverything on it

That table is the reason this is a business story and not a developer story. “Compromise of a dev laptop” in a 200-person company means one endpoint gets reimaged. In a four-person shop, the dev laptop is the infrastructure. There’s no separation to fall back on — which is the same argument behind running agents on a dedicated sandbox box rather than your daily driver, and the same reason an agent’s “sandbox” that’s really just a config setting isn’t a boundary at all.

The patch table, and the two rows you should read twice

AgentStatus as of disclosureFixed version
Claude Code (Anthropic)Patched2.1.179 or later
Codex (OpenAI)Patched0.146.0 or later
GitHub Copilot (Microsoft)No fix shipped—
Gemini CLI (Google)Will not be fixed — deprecated—

Two of four vendors patched. Two did not, for two different and instructive reasons.

Microsoft/GitHub Copilot: no patch at disclosure. AIR reported this in June 2026. Publication was September 18. That is roughly three months of vendor lead time on a zero-click RCE in a product with an enormous install base, and the public write-up says a fix had not shipped. If your team uses Copilot’s plugin or extension surface, you are — pending an update from Microsoft that may well have landed between this research and you reading this — running an unpatched agent. Check your version and your changelog before you assume otherwise. Do not take this article’s word for the current state; take it as the reason to go look.

Google/Gemini CLI: deprecated, not patched. This is the row that deserves the most attention, because of a date.

The Gemini CLI trap: “stay on the stable version” is now the unsafe advice

Google announced on May 19, 2026 that it was consolidating developer tooling under the Antigravity brand and retiring the standalone Gemini CLI and the Gemini Code Assist IDE extensions. The cutoff was June 18, 2026: after that date, Gemini CLI and the Code Assist IDE extensions stopped serving requests for Google AI Pro and Ultra and free-tier accounts. Users were told to migrate to Antigravity CLI.

So far, so ordinary. Products get sunset. But read the carve-out in the same announcement: organizations holding a Gemini Code Assist Standard or Enterprise license keep access, and Gemini CLI remains reachable via paid Gemini and Gemini Enterprise Agent Platform API keys. Google explicitly said it would continue to support Gemini CLI for those customers, with access to current models.

Now stack the two facts:

  • Paying customers were told they could keep running the legacy CLI, precisely so they wouldn’t have to break working pipelines.
  • Four months later, a zero-click RCE lands in the legacy CLI’s plugin path, and the answer is “it’s deprecated, migrate to Antigravity.”

If you are a small shop with a Code Assist license and a CI pipeline that shells out to gemini — and there are a lot of those, because it was the cheapest way to get a model into a build step — you did the conservative thing. You stayed on the supported-for-you version. You avoided a migration in the middle of Q3. And the conservative thing is now the dangerous thing, because “still accessible” and “still receiving security fixes” turned out to be different products wearing the same name.

This is a generalizable lesson and worth writing on the wall next to your renewal dates: a vendor’s commitment to keep a deprecated tool running is not a commitment to keep it safe. Those are separate promises, they get made by separate teams, and only one of them is usually in writing.

If you’re running the legacy Gemini CLI anywhere — a developer’s shell, a GitHub Action, a cron job on the box in the closet — treat the migration as a security task with a deadline, not a roadmap item. And when you do move, check whether Antigravity CLI’s plugin system validates post-checkout SHAs before you re-add the same plugins.

This is the third hit on agent add-ons this year, not the first

The reason to stop treating this as one bug: the agent add-on ecosystem has been getting hit repeatedly, and the numbers keep being large.

AIR’s earlier SkillJacking research, published July 2, 2026, found roughly 925 agent skills serving about 134,000 agents that were sitting on instantly hijackable dependencies — deleted GitHub accounts, unregistered package names, expired domains, freed cloud-app slots. The researchers demonstrated it by re-registering the deleted owner account behind the most popular video-generation skill on skills.sh, which had 11,483 installs. They pushed nothing malicious. They just showed that they could have, to every machine that installed it after that point.

Separately, earlier work cited in Help Net Security’s coverage had a malicious plugin reaching more than 26,000 agents.

OWASP now maintains an Agentic Skills Top 10 project, which is the clearest possible signal that this is a recognized category rather than a run of bad luck.

Three data points, one pattern: the distribution layer for agent capabilities was built at startup speed on top of git and package registries, without the decade of hard-won paranoia that npm and PyPI accumulated the hard way. Plugin4Shell is that gap showing up in the verification step. SkillJacking is the same gap showing up in the ownership step.

We’ve written before about the liability shape of running agents you don’t fully control and about how quickly the MCP and agent-tooling layer became a security surface of its own. This is that thesis with a specific, checkable version number attached — which makes it more useful than either.

The 40-minute audit

You do not need a security program. You need forty minutes and a willingness to write down what you find.

Minutes 0–5: pin down your versions.

On every machine that runs an agent — including the one in the closet and the CI runner nobody logs into:

AgentVersion checkWant
Claude Codeclaude --version≥ 2.1.179
Codexcodex --version≥ 0.146.0
Copilot CLI / extensionCheck the extension version in your IDE and GitHub’s changelogConfirm a Plugin4Shell fix is named
Gemini CLI (legacy)gemini --versionNo safe version exists — plan migration

Write the result somewhere durable. If you can’t answer “what version is the agent on the build server running,” that’s the actual finding.

Minutes 5–15: list every installed plugin and skill, and who owns it.

For each one, three columns: name, source repo/marketplace, and the human or org that controls that source. The third column is the one that matters and the one nobody fills in. SkillJacking exists because that column was blank across 925 skills.

Anything whose owner is a deleted account, a personal account with no activity in a year, or a name you can’t trace: uninstall it now and decide later whether you actually needed it. A plugin you can’t attribute is a plugin you’re trusting anonymously.

While you’re in there, run the check the agents skipped — on one plugin, by hand. AIR’s recommended fix is one sentence: resolve the commit actually in the working tree and abort unless it equals the pinned SHA. You can do that yourself in about ten seconds per plugin, assuming the agent left a git checkout on disk. Most do; some copy files out of one, in which case skip to the second command.

From inside the plugin’s directory:

# 1. Which commit is actually on disk right now?
git rev-parse HEAD

# 2. Does this repo contain any ref whose entire name is 40 hex characters?
git for-each-ref --format='%(refname:short)' | grep -Ei '^[0-9a-f]{40}$'

Command 1 is the missing verification. Compare its output against the SHA in the plugin’s manifest or the marketplace listing. If the branch trick was used on you, those two strings will not match — the pinned string was a branch name, and HEAD is that branch’s tip commit, which has its own, different hash. That one mismatch is the whole vulnerability, visible in a single command that nobody was running.

Command 2 is the smoking gun rather than the symptom. A legitimate repository has no branch or tag whose name is a bare 40-character hex string; there is no honest reason to create one. Empty output is what you want. Any output at all — plus the same look for a ref named FETCH_HEAD — means somebody constructed the shape of this attack inside a repo you installed.

Neither command proves you were clean last month; a swapped plugin that has since been reverted leaves nothing in your working tree. They tell you where you stand today, which is still more than the agent’s own UI will, because the UI has been happily printing the pinned SHA the entire time.

Minutes 15–20: turn off plugin auto-update where you can’t patch.

This is the specific mitigation AIR recommends for anyone on Copilot or the legacy Gemini CLI: disable plugin auto-updates and stay off marketplace plugins until a fix lands. On the patched agents (Claude Code, Codex) auto-update is now the safer setting, because it’s how you get security fixes — so the rule is not “auto-update bad,” it’s “auto-update on a patched agent, manual on an unpatched one.”

Minutes 20–35: rotate on the assumption you were exposed.

Not because you were breached. Because the cost of rotation is an hour and the cost of being wrong is your business. Priority order, highest damage first:

  1. Cloud provider access keys (AWS/GCP/Azure) used from the dev machine
  2. Any .env secrets in projects the agent had read access to — payment keys first
  3. Package registry publish tokens (~/.npmrc, ~/.pypirc)
  4. GitHub/GitLab personal access tokens; prefer short-lived or fine-grained ones on the way back
  5. SSH keys — and while you’re in there, move to hardware-backed keys if you haven’t, because a resident FIDO2 key on a hardware security key is a key that a compromised plugin cannot copy off the disk

Minutes 35–40: check egress, not just files.

Exfiltration needs a network. Look at outbound connections from the agent machine over the last few weeks — your router or firewall log is enough. You’re not looking for a smoking gun; you’re finding out whether you could have seen one. If the answer is “we have no outbound visibility at all,” that’s a bigger finding than anything Plugin4Shell did, and it’s cheap to fix.

Where the honest uncertainty is

A few things this piece is deliberately not claiming, because the public record doesn’t support them:

  • There is no CVE ID or official CVSS score published for Plugin4Shell as of writing. Researchers characterize it as a high-severity, zero-click RCE. Treat the absence of a CVE as a paperwork gap, not a severity discount — but don’t repeat a number to your team that nobody has assigned.
  • No confirmed in-the-wild exploitation has been reported. AIR built working proof-of-concept exploits against all four agents; that is not the same as victims. The reason to act is the auto-update propagation path, which makes a first exploitation event unusually cheap and unusually wide.
  • Vendor status moves. Microsoft may have shipped a Copilot fix between the September 18 publication and you reading this. Check the changelog rather than trusting a table in an article.

When to stop and do something else instead

Not every shop should spend Monday on this.

Skip the audit if you don’t run agent plugins at all. If your use of Claude Code or Codex is “I type in a terminal, it edits files, I have installed nothing from a marketplace,” your exposure to this specific bug is close to zero. Update the binary and move on. Don’t let a branded vulnerability pull you into a project you don’t need.

Stop and fix something bigger if you found any of these during the audit: a shared admin account, plaintext secrets in a Slack channel, no offsite backup, or a dev machine with no disk encryption. Any one of those is a larger expected loss than Plugin4Shell and cheaper to fix. Rank by damage, not by headline.

Don’t rip out your agent workflow over this. The tools that got patched got patched in weeks after private disclosure, which is the system working. The rational response to “the plugin verification step was missing” is “verify the plugin verification step,” not “go back to writing everything by hand.” The economics of agent-assisted work in a small shop don’t change because one layer of the stack needed a fix.

And if you’re on the legacy Gemini CLI, this is the exception to all of the above. That one isn’t a hardening task; it’s a migration with no safe version at the end of the current path. Put it on the calendar this week.

The part that will still be true in a year

Plugin4Shell will be patched everywhere, eventually. The structural thing underneath it won’t be.

Agent marketplaces are selling a trust signal — reviewed, popular, pinned — that is cheaper to display than to enforce. The display is a badge and a hash in a manifest. The enforcement is a post-checkout verification that four separate engineering teams all skipped. When a trust signal is cheap to display and expensive to enforce, the gap between them is where every supply-chain attack of the next three years is going to live.

For a small shop, the practical translation is unglamorous and permanent:

  • Count your plugins. The number should be small enough to recite from memory. If it isn’t, that’s not sophistication, it’s surface area.
  • Know who owns each one. Not the repo URL — the human.
  • Keep the machine that runs agents separable from the machine that holds the crown jewels, even if “separable” just means a second cheap box and a different SSH key.
  • Assume every agent add-on runs as you, because it does, and price your trust accordingly.

The SHA was never the protection. Somebody checking the SHA was. Turns out nobody was checking.

Sources

7 refs
  1. AIR Security — “Plugin4Shell: Zero Click RCE Vulnerability found in top 4 most popular coding agents” — the primary research: the branch-name-collision and FETCH_HEAD mechanisms, the attack chain, per-agent patch status, and the recommended fix (“resolve the commit actually in the working tree and abort unless it equals the pinned SHA”).air.security
  2. Help Net Security — “Zero-click RCE vulnerability hit four major AI coding agents, two remain unpatched” (September 18, 2026) — the disclosure timeline (found May 2026, reported to vendors June 2026), fixed versions 2.1.179 and 0.146.0, Microsoft’s lack of a shipped fix, Google’s deprecation answer, and the auto-update propagation quote.helpnetsecurity.com
  3. Google Developers Blog — “An important update: Transitioning Gemini CLI to Antigravity CLI” (May 19, 2026) — the June 18, 2026 cutoff for Pro/Ultra and free tiers, and the explicit statement that Gemini Code Assist Standard/Enterprise licensees retain access with continued support. The basis for the “deprecated but still running in paid shops” problem.developers.googleblog.com
  4. google-gemini/gemini-cli Discussion #27274 — the same transition notice in the project’s own repo, including the community thread from operators working out what the carve-out means for existing pipelines.github.com
  5. AIR Security — “SkillJacking: 925 Skills Hijacked From Their Maintainers, Affecting 134K Agents” (July 2, 2026) — the 925-skill / ~134,000-agent figures, the hijackable-dependency categories, and the skills.sh video-generation skill with 11,483 installs taken over via a re-registered deleted owner account.air.security
  6. OWASP Agentic Skills Top 10 — evidence that agent add-on supply chain risk is now a tracked category with its own taxonomy, not a one-off incident.owasp.github.io
  7. The Next Web — “A single git trick beat the safety lock on four AI coding agents” — independent trade coverage of the same research, useful for the plain-language framing of why SHA pinning failed.thenextweb.com

← All articles

comments

loading comments…

^ consulting ⌥ building ⌘ shipping
[read next]
security · sep 12
An Attacker Ran Hundreds of AI Agents Against Print Servers and Hit 395 Businesses in a Weekend. One Victim Went From First Contact to Domain Admin in 7 Minutes.
ai agents · sep 7
Before You Install an MCP Server on Faith, Run This Free Three-Question Security Check
security · sep 25
996 Cheap Managed Switches Got Emptied in One Night. 564 of Them Still Had the Factory Password.
security · sep 14
A VPN Gateway With a Perfect 10.0 Severity Score Is Being Exploited Right Now. Feds Got 3 Days to Patch It. You Probably Haven't Heard About It At All.