The connection runs code before you touch anything
How VSCode Remote-SSH agent forwarding exposes a signing oracle to the remote host, the CVE-2022-41034 cross-machine RCE, and where the pivot shows up in telemetry.
Visual Studio Code’s Remote-SSH extension runs code on the remote host the instant the connection opens. Not when a file loads. On connect. VSCode issues a bootstrap command over the SSH channel that looks for ~/.vscode-server/bin/<commit-hash>/. If that directory is missing, it pulls a Node.js server build pinned to the client’s commit hash, extracts it, and launches it. The server binds a listening socket on the remote and tunnels it back to the client through SSH port forwarding. File reads, integrated terminals, the extension host, language servers - all of it rides that socket. The client trusts the remote to host and execute its server. The bootstrap runs through the user’s login shell, so the remote runs whatever .bashrc or .profile sources first. Code execution against the client’s remote toolchain precedes any human action.
The same server model underlies Dev Containers, Remote-WSL, and Remote Tunnels. Tunnels extends the reach further, because the server registers with a Microsoft relay and becomes addressable through it. The download of the server payload trusts the update channel and the commit-hash pin, not a per-tenant signature the operator controls. That is the floor. Agent forwarding is where it gets loud.
When ForwardAgent yes is set - in ~/.ssh/config, or through the agent-forwarding path Remote-SSH honors - the remote session receives an SSH_AUTH_SOCK pointing at a forwarded Unix domain socket. The socket is a proxy back to the local ssh-agent on the workstation. The private key never traverses the link. What crosses is a signing oracle. Any process on the remote that can open that socket can ask the local agent to sign challenges. Enumerating the forwarded socket lists every identity the agent holds. Signature requests against those identities authenticate to any host that trusts them. The key stays home. The capability to use the key does not.
The exposure classes as CWE-668, exposure of a resource to the wrong control sphere. The agent socket is a workstation-scoped credential capability, and forwarding places it inside a remote sphere the workstation does not control. On any multi-user or compromised remote, root reads the socket. So does any process running as the user, including the long-lived VSCode server and every extension loaded in the remote extension host. The relevant technique is MITRE T1563.001, SSH hijacking, with T1552.004 for the key material it reaches and T1550 for use of the forwarded credential against downstream hosts.
The lifetime is the part operators miss. A forwarded agent on an interactive SSH session dies when the shell exits. The VSCode server does not exit with the shell. It persists as a background process after the editor window closes, subject to an idle timeout that defaults to keeping it warm for reconnection. The forwarded socket lives as long as the client keeps the SSH connection multiplexed to that server. The window during which the remote can request signatures is not the length of a terminal session. It is the length of a VSCode session, plus the server’s warm-hold, which routinely means hours after the developer has walked away from the keyboard.
The exploit path needs no memory corruption. An attacker with code execution on the remote - a compromised host, a poisoned .bashrc, a malicious extension auto-updated in place - locates SSH_AUTH_SOCK in the environment of the vscode-server process tree. It lists loaded identities, then signs authentication challenges against lateral targets: bastions, git remotes, production hosts that trust the developer’s key. This is T1021.004, remote services over SSH, driven by a signing oracle the operator forwarded voluntarily. No password. No key theft. The developer already did the hard part by loading the key and forwarding the agent.
The extension host is the second vector, and it is worse because it is normal operation. VSCode extensions execute in the remote extension host with the user’s privileges. They auto-update from the Marketplace. A compromised or typosquatted extension runs code in exactly the context that holds the forwarded socket. Marketplace integrity has failed repeatedly - researchers have published proof-of-concept malicious extensions that accrued thousands of installs before removal, and typosquatted names against popular packages remain a working delivery method. An extension does not need an exploit primitive to reach the agent. It needs an install.
The third vector is documented. CVE-2022-41034, Visual Studio Code Remote Code Execution, CVSS v3.1 7.8, vector AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H, disclosed by Thomas Shadwell of Google. The bug is argument injection through a crafted command: URI - CWE-88 class. A victim who follows a malicious link triggers command execution. Because VSCode multiplexes multiple machines through a shared server trust, an attacker who controlled one connected host could drive execution onto other machines the victim had connected to through the same client. Cross-machine RCE over the editor’s own trust fabric. Patched in VSCode 1.71.2, September 2022. Anything earlier remains exposed where it is still deployed.
Agent hijacking itself is not novel. The OpenSSH manual has warned against forwarding an agent to untrusted hosts for two decades, because any user with root on the remote can access the socket and use the loaded identities. What changed is that Remote-SSH normalized long-lived, forwarded, background-persisted sessions across a broad developer population that treats the remote as trusted infrastructure. The Project Zero disclosure demonstrated the shared-trust pivot inside VSCode itself. The Marketplace incidents demonstrated supply-chain delivery straight into the extension host. The primitives are old. The deployment scale is the change.
Telemetry is where defenders find out how little fires. On the workstation, near nothing. The local agent signs a challenge and returns a signature. That operation emits no security event. EDR on the developer’s machine sees an outbound SSH session and no anomaly, because the signing is indistinguishable from the developer’s own use - it is the developer’s own agent doing the signing. There is no client-side event that separates a legitimate signature from a hijacked one.
On the remote, the signal exists only if it is instrumented. auditd with a watch on connect() to the forwarded socket path, or execve auditing, catches unexpected ssh and ssh-add invocations whose parent process is the node server-main.js in the vscode-server tree. Sysmon for Linux emits Event ID 1 for those process creations and Event ID 3 for the outbound connections to lateral targets. The tell is lineage: an SSH client spawned under the editor server, connecting outbound to a host the developer did not open a terminal to. Without that parent-child correlation the events read as ordinary developer activity and pass unflagged.
The reliable seam is downstream. On the lateral target, sshd logs Accepted publickey for <user> from <remote-ip>, and the source IP is the compromised remote, not the developer’s workstation. Pubkey authentication for a developer identity, sourced from a server that identity should never authenticate from, is the detection that survives. It requires correlating expected source IPs to identities in the SIEM, which most SSH logging does not do by default. The signature is valid. The key is authorized. Only the origin is wrong, and origin is the field defenders most often discard.
The correlation rule that catches this is narrow and buildable. Baseline each engineer identity to the set of source IPs it legitimately authenticates from - workstation, VPN egress, sanctioned bastions. Alert on Accepted publickey for that identity from any IP outside the set, and raise severity when the source IP is itself a Remote-SSH endpoint running a vscode-server process. That single rule converts the invisible signing oracle into an observable pivot at the point where it is used, not where it is exposed.
The residual reality after patching CVE-2022-41034 is that the patch closed one argument-injection RCE. It did nothing to the forwarding exposure, because forwarding is not a bug. It is configuration behaving as specified. The auto-executing, warm-held server is likewise design, not defect. No version boundary removes the signing oracle. The controls that reduce it are scoping and confirmation: agent forwarding disabled by default and enabled per trusted host only, ForwardAgent no as the baseline, keys held in hardware tokens that require a physical touch per signature so a forwarded socket cannot sign silently, per-signature confirmation forced on the agent, per-host keys so one hijacked socket does not sign for the entire fleet, and separate identities for interactive work versus automation. Where the remote is not fully trusted for the lifetime of a warm-held VSCode server, the agent does not belong on it.
CVE-2022-41034 is closed. The forwarded agent is not a CVE. It is a design decision that hands a signing capability to a process the workstation does not control, for longer than the operator thinks, over a channel the operator opened. The mechanism is not advanced. It is forwarded. And that is why it works.
Keep Reading
ai-securityMythos AI cleared for distribution, no validation report
REDLINE breaks down the security risk in releasing Mythos AI to trusted US organizations: not the model, the missing adversarial validation and zero prompt-level telemetry.
rsyncmemcpy walks off the end of the receiver
rsync shipped six CVEs in January 2025. LLMs did not write new bugs - they compressed variant discovery, harness generation, and vulnerable deployment.
github-securityMegalodon hijacked 55,000 GitHub repos via token replay
Megalodon compromised 55,000+ GitHub repositories through PAT harvesting, pull_request_target abuse, and OAuth scope inheritance. Technical breakdown.
Latest on the Wire
Full wire →- 17 Years Frozen in Street View: A Tokyo Car Outlived the House It Sat BesideHacker News
- A distributed-systems veteran wrestles with McKenney's parallel programming bibleHacker News
- AI agents resorted to hacking public data sites to finish routine tasksHacker News
- California's billionaire wealth tax will fail because billionaires can move — the land can'tHacker News
New signal daily · RSS
Stay in the loop
New writing delivered when it's ready. No schedule, no spam.