Comment l’attaque TeamPCP litellm backdoor compromet vos pipelines CI/CD
Isidore Bellemare
En 2025, 27 % des incidents de cybersécurité en France proviennent de failles de la chaîne d’approvisionnement logicielle (source : ANSSI). Analyse approfondie des menaces et plan d’action Parmi eux, l’attaque TeamPCP litellm backdoor a récemment fait couler beaucoup d’encre, dévoilant la fragilité de nos outils de CI/CD et de nos déploiements Kubernetes. Dans les prochains paragraphes, nous décortiquerons les mécanismes de cette menace, vous présenterons les différences entre les versions 1.82.7 et 1.82.8 de LiteLLM, et vous fournirons un plan d’action concret pour protéger vos environnements.
Comprendre l’attaque TeamPCP litellm backdoor
Origine et modus operandi
TeamPCP, le groupe déjà identifié comme responsable des compromissions précédentes de Trivy et KICS, a ciblé le package Python litellm - une bibliothèque largement utilisée pour l’interface avec les LLM (Large Language Models). En exploitant la confiance accordée aux dépendances hébergées sur PyPI, les acteurs ont publié deux versions infectées, 1.82.7 et 1.82.8, le 24 mars 2026. La chaîne d’approvisionnement a alors été détournée : le code malveillant a été injecté durant le processus de construction du wheel, garantissant son exécution dès l’import du module.
Les trois étapes du payload
Le payload de l’attaque TeamPCP litellm backdoor s’articule en trois phases distinctes :
- Credential harvester - collecte d’identifiants SSH, secrets Kubernetes, variables d’environnement .env et portefeuilles cryptographiques.
- Kubernetes lateral movement toolkit - création de pods privilégiés sur chaque nœud du cluster, puis élévation de privilèges.
- Persistent systemd backdoor - mise en place d’un service systemd (
sysmon.service) qui interroge régulièrement le domaine de commande-et-contrôle checkmarx.zone/raw pour récupérer de nouveaux modules.
“Le payload est une attaque à trois étages : un récolteur d’identifiants, un kit de déplacement latéral Kubernetes et un backdoor persistant qui interroge un serveur C2 toutes les 50 minutes.” - Kiran Raj, Endor Labs
Analyse détaillée des versions 1.82.7 et 1.82.8
Injection dans proxy_server.py
Dans la version 1.82.7, le code malveillant a été introduit dans le fichier litellm/proxy/proxy_server.py. L’injection s’est produite pendant ou après la construction du wheel, de sorte que l’import du module litellm.proxy.proxy_server déclenche immédiatement l’exécution du script. Aucun utilisateur ne doit interagir avec la bibliothèque ; le simple fait d’inclure litellm dans votre environnement suffit à activer le payload.
Le vecteur .pth aggravé Mal‑est‑ce ?
La version 1.82.8 va plus loin en ajoutant un fichier litellm_init.pth à la racine du wheel. Les fichiers .pth sont automatiquement traités par le module standard site.py au démarrage de chaque interpréteur Python. Le fichier contient une unique ligne de code :
import subprocess, base64; subprocess.Popen(['python3','-c',base64.b64decode('cHJpbnQoIkJhY2t3YXJkIik=')])
Cette instruction lance en arrière-plan un processus Python qui décodera et exécutera le même payload que la version précédente, mais sans nécessiter que le module litellm soit importé. Ainsi, tout script Python s’exécutant sur la machine infectée déclenchera l’attaque.
Tableau comparatif des deux versions
| Aspect | Version 1.82.7 | Version 1.82.8 |
|---|---|---|
| Point d’injection | proxy_server.py (import) | litellm_init.pth (site import) |
| Mode d’exécution | Synchronous au premier import | Asynchronous via subprocess.Popen |
| Complexité du code | Simple import + fonction | Base64 + lancement séparé |
| Niveau de persistance | Service systemd installé après élévation | Service systemd + .pth persistant au démarrage |
| Détection | Facile via recherche dans le module | Difficile, nécessite inspection du répertoire site-packages |
Impacts sur les environnements Kubernetes et CI/CD
Collecte de credentials
Le credential harvester balaye les fichiers ~/.ssh/id_rsa*, les variables d’environnement contenant des jetons d’accès cloud, ainsi que les secrets stockés dans Kubernetes (/var/run/secrets/kubernetes.io/serviceaccount/token). Les données sont ensuite compressées dans une archive chiffrée tpcp.tar.gz et envoyées via une requête HTTPS POST vers le domaine models.litellm.cloud.
Déploiement de pods privilégiés
En s’appuyant sur le token de compte de service (service account token), le script enumère chaque nœud du cluster et crée un pod avec les privilèges hostNetwork:true et hostPID:true. Le pod monte le système de fichiers du nœud (/), puis instaure le fichier ~/.config/sysmon/sysmon.py comme service systemd utilisateur, assurant ainsi une persistance à long terme même après le redémarrage du nœud.
Mesures de détection et de réponse
Audit des versions et rollback
- Lister les installations :
pip list | grep litellm - Identifier les versions compromises : toute mention de 1.82.7 ou 1.82.8.
- Revenir à une version propre :
pip install --upgrade litellm==1.82.6ou version antérieure fiable. - Vérifier les fichiers .pth :
grep -R "litellm_init.pth" $(python -c "import site; print(' '.join(site.getsitepackages()))").
Surveillance du trafic réseau
- Capturer les flux sortants vers les domaines
models.litellm.cloudetcheckmarx.zone(exemple de règle Suricata :alert http any any -> any any (http.host; content:"models.litellm.cloud"; msg:"TeamPCP C2 traffic"; sid:100001;)). - Analyser les logs d’audit Kubernetes pour détecter la création de pods avec les privilèges élevés mentionnés ci-dessus.
Suppression des mécanismes de persistance
- Arrêter et désactiver le service :
systemctl stop sysmon.service && systemctl disable sysmon.service. - Supprimer le script persistant :
rm -rf ~/.config/sysmon. - Redémarrer les nœuds pour réinitialiser les environnements Python et éliminer les processus en arrière-plan résiduels.
“Cette campagne n’est probablement pas terminée ; chaque environnement compromis génère des identifiants qui débloquent la cible suivante.” - Endor Labs
Bonnes pratiques pour sécuriser la chaîne d’approvisionnement
Renforcer les pipelines CI/CD
- Signer numériquement les artefacts : utilisez cosign ou Rekor pour garantir l’intégrité des wheels publiés.
- Intégrer des scans SCA (Software Composition Analysis) : JFrog Xray, Snyk ou Sonatype Nexus ; configurez-les pour bloquer toute version non approuvée.
- Appliquer la règle du moindre privilège sur les runners : exécutez les builds dans des conteneurs sans accès réseau externe. Escroquerie streaming IA musique : 10 M$ détournés
Gestion des dépendances Python
- Verrouiller les versions via un fichier
requirements.txtoupoetry.locket éviter les mises à jour automatiques non validées. - Utiliser des environnements virtuels isolés (
venv,conda) pour chaque projet afin de limiter la portée d’une infection. - Mettre en place une politique de mise à jour : surveillez les avis de sécurité de la communauté Python (ex. : le site
python-security.readthedocs.io).
Mise en œuvre - étapes actionnables
- Inventaire : recensez toutes les machines exécutant Python et les dépendances installées.
- Scan : lancez un audit avec un outil SCA capable de détecter les versions 1.82.7/1.82.8.
- Remédiation : désinstallez les versions infectées, appliquez les correctifs de configuration réseau et revoyez les permissions des pods.
- Surveillance continue : mettez en place des alertes sur les domaines C2 et les créations de pods privilégiés.
- Formation : sensibilisez les équipes DevOps aux risques de la chaîne d’approvisionnement et aux bonnes pratiques de revues de code.
Conclusion - prochaine action avec avis tranché
L’attaque TeamPCP litellm backdoor montre que la confiance aveugle accordée aux packages hébergés sur PyPI peut transformer une simple dépendance en porte d’entrée directe vers vos clusters Kubernetes et vos pipelines CI/CD. En suivant les mesures de détection, de réponse et de prévention présentées ci-dessus, vous réduirez significativement le risque d’escalade. Agissez dès maintenant : audit complet, retrait des versions compromises et renforcement de votre chaîne d’approvisionnement ; c’est la meilleure défense contre une campagne de compromission qui ne montre aucun signe d’arrêt.