DEV Community

Cover image for How a Trusted Extension Name Delivered an SSH Backdoor Through Cursor's Store
Dhruv Agrawal
Dhruv Agrawal

Posted on Edited on Originally published at blog.dhruuv.me

How a Trusted Extension Name Delivered an SSH Backdoor Through Cursor's Store

On October 29, Cursor's extension marketplace served tampered builds of Anthropic's Claude Code extension, versions 2.1.312 and 2.1.313. They were live for about 11 hours. They added an attacker's SSH key to developers' machines.

Anthropic's systems were not breached. The official package was not breached either. The attacker, Anthropic said, "used access to Cursor's marketplace." In a statement quoted by BankInfoSecurity, Cursor said at most 5,000 users were affected.

To see how that is possible, follow the path an extension takes to your editor.

Diagram of the extension delivery chain: publisher, Open VSX registry, Cursor marketplace proxy, editor, extension host, with the tampered step marked at the marketplace
Delivery chain as described in Cursor's extension docs. The incident shows only that the copy Cursor's marketplace served was tampered with.

The delivery chain

Cursor is built on VS Code. It does not use Microsoft's extension marketplace. Its docs say it gets third-party extensions from Open VSX, an open extension registry. Downloads pass through Cursor's own marketplace proxy, which scans them for malware before serving them.

So a Claude Code update reaches you in four hops:

  1. Anthropic builds and publishes the extension.
  2. A registry stores it.
  3. Cursor's marketplace serves it under the name anthropic.claude-code.
  4. Your editor installs it and loads it.

You see the name at step 3. At step 4, your editor runs whatever step 3 served. If someone can change those bytes, the name still looks right.

What the tampered build did

StepSecurity compared a malicious build with the clean one. Its tampered extension.js was the official 2.1.312 bundle with a 7,386-byte loader added to the front. The file was about 3.6 MB. As StepSecurity put it, "The malicious build is the official extension in every respect except one."

In the 2.1.313 build, the new version number appeared only in the repackaged manifest. That made it look like a routine update.

The loader runs as soon as the editor loads the extension. No Claude conversation is needed. VS Code's own docs explain why that matters: "The extension host has the same permissions as VS Code itself." An extension can read your files, use the network and start processes.

Animated walkthrough of the loader's stages after the extension loads: recon, SSH key, enable SSH, reverse tunnel, persistence, beacon
Loader stages as listed in StepSecurity's analysis.

According to StepSecurity, the loader then ran six stages:

  • collected host details and the names of up to 40 secret-looking environment variables
  • added an attacker key to ~/.ssh/authorized_keys
  • tried to turn on the SSH server
  • tried to open a reverse SSH tunnel, retrying every 60 seconds
  • set up persistence so the tunnel restarts after a reboot or login
  • reported back to the attacker's server

StepSecurity says no credential values were read. Cursor notes that enabling SSH usually needs admin rights. The key and persistence could still land without them.

The evidence

Cursor's notice gives the window: about 2:27am to 1:35pm Pacific on October 29. Cursor removed the malicious versions, took its marketplace offline, rebuilt it and rotated keys. The Hacker News reports that Microsoft said the build was never published on the Visual Studio Marketplace or npm. StepSecurity verified the Open VSX copy of 2.1.312 as clean.

How the attacker got access to Cursor's marketplace has not been disclosed.

What to do

  • Check, don't assume. If you installed or updated Claude Code from Cursor's marketplace that day, follow Cursor's check steps. Look in authorized_keys for a key labelled svc-telemetry.
  • Updating is not cleaning. Anthropic has released 2.1.314. But security researcher Kevin Beaumont noted that the replacement versions, which auto-update, "do not remove the prior backdoors." Persistence and the SSH key must be removed by hand.
  • Add an install cooldown. Cursor lets team admins delay installs and updates until a version has been public for a set number of hours. The default is 0, meaning off. The malicious versions were live for about 11 hours.
  • Turn on signature checks if you can. Cursor lets Enterprise teams require valid Open VSX extension signatures before install.

Where this breaks down

The root cause is unknown, so the fixes above are guesses about which layer failed. No source says whether signature checking would have caught this build. That depends on whether the attacker could also sign or bypass signing, which nobody has said.

A cooldown helps only if someone spots the bad build before the delay runs out. It also delays good security fixes.

The weakest part of this post is its general lesson. Any store that serves you code could be the weak hop. But the evidence covers one incident in one marketplace, and its root cause is still undisclosed.

Takeaway

The extension name tells you who should have written the code. It does not tell you who last touched the bytes.

Which hops sit between your editor and the publishers you trust?

Sources

Top comments (5)

Collapse
 
alexshev profile image
Alex Shev •

The lesson is broader than one marketplace incident: a trusted display name is not an identity boundary. Install flows should surface immutable publisher identity, signed artifact digest, requested capabilities, and the previous-version diff. Those facts give a reviewer something concrete to verify before an update gains shell or SSH reach.

Collapse
 
dhruv1206 profile image
Dhruv Agrawal •

Agreed. The version diff alone would have caught this one: the only change from the real 2.1.312 was a small loader added at the top.

Collapse
 
robertpaschal profile image
Odinaka Robert Nnamani •

a trusted name doing the smuggling is the nasty part. store trust does a lot of heavy lifting here.

Collapse
 
dhruv1206 profile image
Dhruv Agrawal •

Exactly. Same name, same code, plus one tiny loader. Hard to spot.

Collapse
 
robertpaschal profile image
Odinaka Robert Nnamani •

yeah, the tiny loader is the part that gets past a skim. name match does the rest.