RC RANDOM CHAOS

Malicious VSCode extension shipped through official Marketplace

Technical analysis of a compromised VSCode extension reaching GitHub credentials: extension host privileges, MITRE ATT&CK mapping, telemetry gaps.

· 6 min read
Malicious VSCode extension shipped through official Marketplace

A malicious VSCode extension shipped through the official Marketplace. The package executed in the editor process on install and on every workspace open. Targets ran the extension under their own user identity, with the same filesystem reach VSCode itself holds, and with whatever Git credential helpers the host had configured. The blast radius reached GitHub repositories through cached OAuth tokens, SSH keys, and the GitHub CLI session file. This is supply chain compromise at the developer endpoint, not at the registry.

The extension model is the enabling condition. VSCode extensions are not sandboxed. They run as Node.js code inside the Extension Host process, a child of the renderer, with full access to the user’s home directory, environment variables, child_process.spawn, network sockets, and the VSCode API surface. There is no capability declaration. There is no permission prompt at install. The Marketplace performs static scanning and signature checks against a publisher identity, but the publisher identity itself is the trust anchor - and that anchor has been demonstrated to fail through credential theft, account takeover, and dependency-confusion-style name squatting against unmaintained but popular extensions.

The primitive in this case is malicious code execution at install plus persistence via the activation event. activationEvents in the extension manifest can be set to onStartupFinished, which fires on every VSCode launch regardless of workspace. That gives the attacker a per-process foothold that runs whenever the developer opens the editor. No user interaction beyond the initial install. The execution context is the developer’s interactive session, not a background service, so it inherits Kerberos tickets on Windows domain hosts, keychain unlock state on macOS, and ssh-agent connections on Linux. MITRE T1195.002, compromise software supply chain. T1546 class persistence through the activation hook. T1059.007 for the JavaScript execution layer.

What the extension actually does once active follows the standard developer-host playbook. Read ~/.gitconfig and parse credential.helper entries. Walk ~/.config/gh/hosts.yml for GitHub CLI session tokens. Enumerate ~/.ssh for private keys and known_hosts to map the developer’s repository reach. Read environment variables for GITHUB_TOKEN, NPM_TOKEN, AWS_ACCESS_KEY_ID, and any *_TOKEN pattern injected by the shell or by a tool like direnv. On macOS, query the login keychain through the security CLI if Touch ID is cached. On Windows, hit the Credential Manager through PowerShell’s CredentialManager module or direct DPAPI calls under the user’s session key. T1555.001, T1555.003, T1552.001, T1552.004.

The GitHub reach is what makes this incident category dangerous. A developer endpoint with cached gh auth login state holds a Personal Access Token or a fine-grained token with whatever scopes the developer accepted. For many engineers that is repo, workflow, and read:org. Workflow scope is the pivot. A token with workflow can modify .github/workflows/ files, which means the attacker can push a commit that introduces a workflow change executing arbitrary code inside the repository’s GitHub Actions runner. That runner holds GITHUB_TOKEN with permissions defined by the repository, has access to repository secrets, and on self-hosted runners has network reach into the organisation’s infrastructure. T1078.004 for the valid cloud account use. T1098.001 for any persistence written back into the account.

The extension also targets the source tree itself. Walk the workspace root for .env, .env.local, terraform.tfstate, *.pem, kubeconfig, and config.json in the .docker directory. Read package.json and look for private registry URLs that imply an internal npm or Artifactory instance the developer’s machine can reach. Exfiltration runs over HTTPS to attacker infrastructure, often staged through a CDN-fronted endpoint or a Discord webhook to defeat naive egress filtering. The traffic blends with the legitimate telemetry VSCode and its extensions already generate, which is the reason network detection at the endpoint is weak for this class.

What threat actors do at this stage is documented from prior incidents. The Lazarus-attributed VSCode extension activity in late 2024 used typosquatted package names targeting Solidity and Web3 developers. The npm ecosystem has seen parallel attacks - the Ledger Connect Kit incident in December 2023, the ctx and phpass takeovers, the eslint-scope token theft in 2018 that pivoted through publisher credentials to ship malicious versions. The VSCode Marketplace is the same trust model with a larger attack surface per package because of the unrestricted Node.js runtime. The attacker objective is consistent across these cases: harvest developer credentials, pivot into source repositories, plant code that survives review, and use the build pipeline as the delivery mechanism for downstream targets.

Telemetry on the developer endpoint is thin by default. The Extension Host process runs as a child of Code.exe on Windows, or as a node process spawned from the Electron main process on macOS and Linux. EDR products that monitor process creation will see a node process with a parent of Code.exe, which is normal and high-volume. Sysmon Event ID 1 will fire on the child process creation, but without context the event is noise. The signal is in what that node process does next. Sysmon Event ID 11, file creation, on paths under ~/.ssh or ~/.config/gh is anomalous for an editor child process. Sysmon Event ID 3, network connection, from a node process whose parent is VSCode to a non-Microsoft domain is anomalous. Sysmon Event ID 10, process access, against keychain or DPAPI-related processes from an editor child is anomalous. None of these fire on default EDR rules. They require detection engineering specifically built for the editor-as-attack-surface case.

GitHub-side telemetry catches the second stage. The audit log records git.clone, pull_request, and workflow_run events with the actor’s PAT. A token that suddenly clones repositories the developer has not touched in months, or that authenticates from an IP outside the developer’s normal egress range, is visible if the organisation is consuming audit log events into a SIEM. Most are not. The fine-grained PAT model with explicit repository selection narrows the blast radius but does not eliminate it, because developers routinely grant tokens broader scope than the immediate task requires. The classic PAT with repo scope is still common and grants access to every repository the user can read.

The network IOC layer is weakest where it matters most. Exfiltration over HTTPS to a Cloudflare-fronted domain looks identical to legitimate extension telemetry at the packet level. TLS interception in developer environments is rare because it breaks Git operations against signed commits and corporate proxies frequently exempt developer subnets. The only reliable network signal is destination reputation, and attackers rotate infrastructure faster than reputation feeds update. Detection has to live on the endpoint, scoped to the specific behaviour of an editor process touching credential stores or making outbound connections to non-allowlisted destinations.

The technical reality post-incident is that the extension was removed from the Marketplace and the publisher account was suspended. That does not retract the code from the endpoints where it already ran. Every developer who installed the extension before the takedown still holds whatever artefacts the extension wrote to disk, and any credentials accessed during the active period must be treated as compromised. PAT rotation, SSH key rotation, and review of GitHub audit logs for the window between install and removal are the minimum scope. Self-hosted Actions runners that received jobs triggered by affected developers’ tokens require forensic review of the runner host, not just credential rotation. Workflow files modified by compromised tokens persist in repository history and re-execute on subsequent triggers unless explicitly reverted and the runner secret material rotated.

The pattern is not new. The control gap is not new. Extension marketplaces ship code with full user privilege on the most credential-rich endpoint in the engineering organisation, and the trust model assumes publisher identity is sufficient. It is not. Until VSCode extensions run under an enforceable capability manifest, every install is a code-execution decision the developer is not equipped to evaluate, and the GitHub-side blast radius is set by whatever scopes the developer’s tokens already hold.

See also: NordVPN for tunneled traffic when operating outside controlled networks.


#ad Contains an affiliate link.

Share

Keep Reading

Stay in the loop

New writing delivered when it's ready. No schedule, no spam.