Tech 10 min read

VS Code Remote-SSH: a compromised host can run commands on your local terminal

IkesanContents

TL;DR

What happened A malicious extension on a compromised SSH host can open your local terminal and send it commands through VS Code or Cursor Remote-SSH (Calif’s demo). The RCE crosses from the remote side back to your local machine

Who’s affected Anyone using Remote-SSH as a sandbox for untrusted code or AI agents; anyone connecting to untrusted or shared SSH hosts

What to do Limit connections to hosts you trust as much as your local machine. For isolation, use a VM, container, or OS sandbox instead. There is no CVE and no patch (Microsoft treats it as by design)


If you use VS Code Remote-SSH as a “box to lock sketchy code into on the remote side,” Calif’s Vibe Hacking writeup breaks that assumption. With the remote side taken over, VS Code or Cursor over Remote-SSH can be made to open your local terminal and have command strings sent to it.

Microsoft’s Remote-SSH Marketplace page even carries a Security Note that a compromised remote could use the VS Code Remote connection to execute code on your local machine (the original reads “A compromised remote could use the VS Code Remote connection to execute code on your local machine”). This is less “a CVE nobody knew about” and more a concrete walk-through that steps through the Remote-SSH trust model. The trust boundary (the design line of “don’t trust anything past here”) isn’t drawn between local and the SSH host.

A remote extension opens your local terminal

Per Calif, the entry point is a malicious extension on a compromised SSH server. When a developer connects to that server over Remote-SSH, the remote-side extension can invoke VS Code commands.

Two commands are used. workbench.action.terminal.newLocal creates a local integrated terminal. As the “Local” in the name implies, it runs on the developer’s own machine, not in the SSH host’s shell.

Then workbench.action.terminal.sendSequence sends a string. As a VS Code terminal feature, it’s a legitimate function for sending Enter or escape sequences from a keybinding. Per Calif, appending a newline (\n) makes the string execute as-is. The result is the same as the user typing a command locally and pressing Enter. Neither command prompts the developer for confirmation. Just calling them from the remote side makes the local terminal act.

flowchart TD
    A["Developer connects to<br/>an SSH host via Remote-SSH"] --> B["The SSH host is compromised"]
    B --> C["Remote-side extension<br/>runs VS Code commands"]
    C --> D["newLocal creates<br/>a local terminal"]
    D --> E["sendSequence sends<br/>a command string"]
    E --> F["Command runs on<br/>the developer's machine"]

    style F fill:#991b1b,color:#fff

Cyber Security News frames this as a Remote-SSH RCE (Remote Code Execution). But as far as can be confirmed, it isn’t a CVE number or a patched-version notice; the focus is the design Remote-SSH has from the start, strongly linking a trusted remote and the local side.

Why a remote extension can act locally

With Remote-SSH, VS Code runs extensions split across two places. Knowing this split reveals the path by which remote-side code reaches your local terminal.

VS Code calls the place that runs extensions the “extension host.” During a Remote-SSH connection, an extension host stands up on both the local side and the SSH host. Which side an extension runs on is decided by its extensionKind setting.

KindextensionKindRuns onExample role
UI extension["ui"]The developer’s local machineThemes, keybindings, local device integration
Workspace extension["workspace"]The side with the workspace (the SSH host)Language servers, linters, file operations

VS Code’s official docs define a UI extension as one that needs to run near the UI because it needs local assets, devices, or low latency, and a Workspace extension as one that needs to run where the workspace is because it accesses workspace contents. When you open a repository on an SSH host and work on it, the extensions supporting that work run on the SSH host.

On top of this split, the command API that extensions can call connects the path. As workbench.action.terminal.newLocal’s name says, even an extension running on the SSH host has its command target on the local side. The remote extension host and the local extension host are joined by a single trusted channel, and the remote side can call the local side’s commands. An attacker who takes over the SSH host gets that channel along with it.

Using it as an AI agent’s escape hatch is risky

This becomes a problem when you want to keep an AI coding agent away from local. The setup of “enter an SSH host in Cursor or VS Code and run the AI agent remotely, so the local terminal is safe” stands on the opposite assumption from the Remote-SSH Security Note.

If a remote-side AI agent merely goes rogue, the damage looks like it stays within the SSH host’s filesystem. But during a Remote-SSH connection, the SSH host holds a path to local-side features through VS Code Server and extensions. Calif’s demo reaches local terminal creation and input injection through that path. If the SSH host is a trusted operating environment, this is a convenient path; used as an isolation box for unknown repositories or AI agent experiments, the path that reaches your local terminal stays open.

Cursor inherits the same assumption

Calif notes this isn’t a Cursor-specific flaw, but that Cursor inherits VS Code’s Remote-SSH machinery. As an IDE like Cursor 3 leans toward being the agent’s execution environment, the line between local and remote blurs further. Even when you think you’re running on the cloud or an SSH host, the IDE has built connection paths to your local terminal, credentials, files, and browser.

Separately, Cursor also had CVE-2026-26268, where Git hooks lead to RCE outside the sandbox. That one was Git hooks, this one is Remote-SSH local terminal creation, but the shape is close. A dev tool’s legitimate feature, combined with the auto-execution of AI agents or remote development, reaches the host side without passing through the user’s confirmation.

The iTerm2 CVE-2026-41253 that Calif also reported was likewise a case where SSH-host output executes on your local terminal. From different angles, they break the assumption that an SSH connection is “closed off on the far side.”

Microsoft treats this as by design

Per Calif, in the GitHub issue around this behavior, Microsoft has signaled it won’t make changes to harden extension safety here. So it isn’t a wait-for-patch situation; it’s something to handle on the operations side, given the Remote-SSH trust model. At the time of writing, no CVE number can be confirmed. This isn’t the kind of problem you wait for a fixed version on.

VS Code has Workspace Trust, which opens untrusted repositories in a restricted mode, but it isn’t designed to guard this Remote-SSH path. What Workspace Trust looks at is trust in the workspace’s file contents, not a judgment of whether to trust the connected SSH host itself. On Microsoft’s tracker, a discussion that Workspace Trust can’t be cleanly applied to a Remote-SSH workspace has been open for a while.

Where to check

If you’ve already connected over Remote-SSH to an unknown or shared server, look at the local-side history. On macOS the shell history, on Windows the PowerShell history, the VS Code integrated terminal logs, and the EDR’s process execution history are the leads. If there’s a curl, an osascript, a PowerShell download-and-run, or commands reading SSH keys or cloud credentials, cross-check them against the remote host you connected to in that time window.

Look at the remote-side extensions of VS Code or Cursor too. Check whether unfamiliar extensions or files with recent modification times remain under ~/.vscode-server/extensions on the SSH host, or ~/.cursor-server for Cursor. But as Calif also writes, if the SSH host is fully taken over, there are cases where this check alone can’t pick up the traces (the attacker can later rewrite files on the compromised server). Assuming traces get wiped after the breach, prioritize chasing the execution history on the local terminal side.

The concrete local places to check:

OSWhere to lookWhat to pick up
macOSShell history (~/.zsh_history, etc.), EDR process logscurl, osascript, reads of SSH keys / cloud credentials
WindowsPowerShell history, EDR process logsDownload-and-run, reads of credentials / tokens
CommonVS Code integrated terminal logs, shell start timesThe host you had a Remote-SSH connection to in that window

If unfamiliar commands show up, cross-check them against the SSH host you were connected to at that time.

If isolation is the goal, flip the direction

Remote-SSH suits “reaching a server under your own control conveniently from your local IDE.” If the connection target is as trustworthy as your local machine, a path that reaches your local terminal isn’t a problem.

The problem is when isolation is the goal. Use Remote-SSH as an isolation box for unknown repositories or AI agent experiments, and per Calif’s demo, a path that reaches your local terminal remains. When isolation is the goal, separate the execution environment itself rather than entering by SSH from the IDE.

  • Run the IDE inside a VM, container, or OS sandbox, cut off from your local production environment
  • If you must use Remote-SSH, make the connection target disposable and keep no credentials or SSH keys on it
  • Treat a remotely-running AI agent with the same caution as a local one

The sandbox-exec and Windows Sandbox covered in the existing post Local isolation for AI agents were about binding the agent itself on the local side. That’s a different direction from offloading to the remote over Remote-SSH; whether the SSH host is a party you can trust is questioned from the premise.

References