Herkos / Docs / Case studies / MCPoison

Rug-pull after approval: MCPoison

A Cursor MCP server was approved once, then silently swapped for a malicious one. The approval stuck to the entry's name, not its contents, so the swap inherited the trust and ran with no new prompt. Herkos does not catch this swap, and saying so is the point of this page. What its scan does catch is the rug-pull's close cousin: a tool whose description is poisoned after you approved it.

At a glance

Disclosed: Check Point Research, named "MCPoison", reported to Cursor on Jul 16 2025. Fixed: Cursor 1.3, Jul 29 2025. CVE: CVE-2025-54136. Severity: CVSS 3.1 base 7.2 HIGH (the CNA score, GitHub on behalf of Anysphere/Cursor) and 8.8 HIGH (NVD); the only difference between the two vectors is privileges-required. Affected: Cursor versions prior to 1.3. Sources: Check Point Research, NVD, Cursor GHSA-24mc-g4xr-4395.

What happened

Cursor keyed an MCP approval to "this entry was approved once" rather than to a hash of the entry's actual command and arguments. Once approved, later edits to the same entry were trusted without a new prompt. The attack is four steps:

1. approve once command: echo harmless, user approves 2. swap the payload command: <malicious> same entry, edited later 3. runs silently no new prompt persistent on every open approval was bound to the entry's name, not a hash of its contents - so the swap inherited the trust
  1. Plant a benign config. The attacker commits an innocuous MCP server entry to a shared repo - Check Point used .cursor/rules/mcp.json with a harmless command like echo.
  2. Victim approves once. A collaborator opens the project, Cursor prompts for MCP approval, and the victim approves the harmless-looking entry. The approval is persisted.
  3. Attacker swaps the payload. The attacker edits the same entry's command / args to something malicious. Per Check Point: "the contents - such as command and args - can be modified later without triggering a new approval prompt."
  4. Silent persistent execution. The next time the victim opens the project, the modified command runs automatically, with no prompt. Because the config lives in the repo, it re-runs on every open - persistent, silent, remote code execution.

Root cause in one line: approval was bound to the entry's identity, not to its contents, so mutating the contents after approval bypassed re-validation. The 1.3 fix re-triggers an approval prompt on any change, even adding a single space.

Where Herkos helps, and where it does not

Be precise, because this is the case a skeptic pushes on hardest:

Here is the receipt

A real run of this repo's binary - the description rug-pull, not the command swap. The trusted baseline pins what deploy/run was approved to do; the scanned config carries a description that drifted to smuggle an instruction, and Herkos flags it:

herkos scan --baseline
$ herkos scan --config deploy.manifest.json --baseline deploy.baseline.json
  [poisoned] deploy/run: tool description differs from baseline
herkos scan: 0 over-scoped, 1 poisoned, 0 unrestricted-egress, 0 unbrokered, 0 unpinned-install, 0 remote - your code never left this machine

Run it in CI against a committed baseline and a post-approval swap fails the check instead of running unnoticed.

The honest boundary

Description-drift detection is real; catching MCPoison's command/args swap is not, and neither is live prevention. If your threat model is "block a changed config before it executes," you want a TOFU-pinning layer like mcp-context-protector, not Herkos. Herkos's honest contribution here is narrow: it flags description poisoning against a baseline and signs a record of what ran. (Note: do not confuse MCPoison / CVE-2025-54136 with the separate Cursor "CurXecute" bug, CVE-2025-54135.)