Offre Durée limitée : -10% sur tout le site* avec le code WELCOME10

LiteLLM piégé sur PyPI et Docker Hub : un malware vole clés SSH et tokens API

Publié le 23 octobre 2026 9 min de lecture 7 sections

Ce qui s'est passé le 21 octobre 2026

Le 21 octobre 2026, un attaquant a compromis le pipeline de release de LiteLLM, un proxy Python open source qui unifie les appels à plusieurs fournisseurs d'IA (OpenAI, Anthropic, Azure, AWS Bedrock). Le package malveillant litellm==1.107.0 a été publié sur PyPI à 11 h 39 UTC et retiré à 13 h 23 UTC, soit environ deux heures d'exposition. Les images Docker officielles sur Docker Hub et GHCR (tags v1.107.0, latest, main-latest, main-stable) sont restées en ligne jusqu'à 14 h 20 UTC (source : LiteLLM).

Les administrateurs qui auto-hébergent LiteLLM sur un VPS ou un serveur dédié sont les premiers concernés : un simple pip install --upgrade litellm ou un docker pull pendant la fenêtre d'exposition suffisait à déployer le malware. Chez un hébergeur comme ElypseCloud, nous recommandons systématiquement d'épingler les versions des packages dans tout fichier de configuration Docker ou requirements.txt.

C'est la deuxième compromission de la chaîne d'approvisionnement de LiteLLM en sept mois. En mars 2026, les versions 1.82.7 et 1.82.8 avaient déjà été piégées pendant environ 40 minutes via une attaque ciblant la dépendance Trivy dans le pipeline CI/CD (source : LiteLLM). Cette récidive souligne le risque structurel lié aux dépendances tierces pour quiconque auto-héberge des services sur un VPS Linux ou un serveur dédié.

Comment le malware fonctionne

La version 1.107.0 embarquait trois scripts malveillants obfusqués, injectés dans chaque wheel Python et dans les images Docker. Le malware cible trois systèmes d'exploitation : Linux, macOS et Windows. Son mécanisme repose sur le système de hooks de démarrage de l'interpréteur Python.

FichierRôle
cook.pth (12 octets)Hook de démarrage Python : contient import cook. Le module site de Python exécute automatiquement les lignes import des fichiers .pth à chaque lancement de l'interpréteur, sans action de l'utilisateur.
cook.py (22 251 caractères)Voleur de credentials (nom interne : "privcollect"). Code obfusqué sur une seule ligne, exécuté dans un thread daemon pour rester invisible. Collecte les secrets puis les chiffre en AES-256-CBC avec enveloppe RSA-4096 avant exfiltration vers litellm.dev/healthcheck.
sitecustomize.py (25 509 octets)Daemon détaché (Linux et macOS uniquement) qui interroge un dépôt GitHub toutes les heures. Si le fichier domain.txt contient un nom d'hôte, le daemon télécharge et exécute du code arbitraire via exec().

Sous Windows, le daemon sitecustomize.py ne se détache pas en arrière-plan, mais le voleur cook.py s'exécute de la même façon à chaque lancement de Python depuis la ligne de commande, un script ou un service. Le fichier /tmp/remote_code_loader.lock sert de verrou pour empêcher les exécutions multiples du daemon. La variable d'environnement PRIVCOLLECT_ACTIVE=1 signale qu'un processus de vol est déjà en cours. Ces deux marqueurs sont aussi des indicateurs de compromission.

Quelles données sont dérobées

Le voleur de credentials cible 16 catégories de secrets sur le serveur compromis. Pour un administrateur de VPS ou de serveur dédié, voici les données les plus critiques :

  • Clés SSH et GPG : le contenu du répertoire ~/.ssh/ (clés privées, authorized_keys, known_hosts) et les clés GPG privées. Un attaquant en possession de vos clés SSH peut se connecter à tous les serveurs qui les acceptent. Consultez notre guide sécuriser SSH sur VPS Linux pour les bonnes pratiques de rotation.
  • Credentials cloud : identifiants AWS IAM, jetons Azure, clés de service GCP, tokens Kubernetes. Si votre VPS utilise un rôle IAM ou stocke des credentials cloud en variables d'environnement, ils sont exfiltrés.
  • Mots de passe des navigateurs : bases de données de Chrome, Firefox, Edge et Brave (mots de passe, cookies, historique). Concerne les environnements de développement avec un navigateur installé, y compris les postes desktop.
  • Tokens d'outils IA et CI/CD : tokens Claude, Cursor, GitHub Copilot, Gemini, ainsi que les credentials GitLab Runner, Jenkins et Vault.
  • Mots de passe de bases de données : le malware parcourt les fichiers de configuration courants (.env, config.php, settings.py) et extrait les chaînes de connexion MySQL, PostgreSQL et tout autre SGBD. Il exécute aussi des requêtes Prisma pour extraire les clés API (master keys et virtual keys) stockées dans la base de données du proxy LiteLLM.
  • Variables d'environnement : le malware lit les variables d'environnement de tous les processus en cours sur le serveur, pas seulement ceux de Python. Tout secret passé en variable d'environnement (tokens API, mots de passe, clés de chiffrement, credentials SMTP, URL de connexion DATABASE_URL) est compromis.

Vérifier si votre serveur est touché

Connectez-vous en SSH à votre VPS ou serveur dédié. Les commandes suivantes en ligne de commande fonctionnent sur Ubuntu, Debian et la plupart des distributions Linux.

Vérifier la version du package LiteLLM

pip show litellm 2>/dev/null | grep Version

Si la sortie affiche Version: 1.107.0, votre installation est compromise. Toute autre version (notamment 1.106.2) n'est pas concernée.

Chercher les fichiers malveillants

Le malware dépose ses fichiers dans le répertoire site-packages de Python. Cherchez-les dans tous vos environnements virtuels et dans l'installation système :

sudo find / -type f \( -name "cook.pth" -o -name "cook.py" -o -name "sitecustomize.py" \) -path "*/site-packages/*" 2>/dev/null

Vérifier le daemon de chargement de code distant

ls -la /tmp/remote_code_loader.lock 2>/dev/null
ls -la /tmp/.pc_once_* 2>/dev/null

Si l'un de ces fichiers existe, le malware a été exécuté sur votre serveur. Le fichier .pc_once_* est un marqueur d'exfiltration réussie : vos credentials ont déjà été envoyés à l'attaquant.

Vérifier les images Docker

Si vous utilisez LiteLLM via Docker sur votre VPS, vérifiez le tag de l'image en cours d'exécution :

docker inspect --format='{{.Config.Image}}' $(docker ps -q --filter ancestor=ghcr.io/berriai/litellm) 2>/dev/null
docker inspect --format='{{.Config.Image}}' $(docker ps -q --filter ancestor=litellm/litellm) 2>/dev/null

Si l'image porte le tag v1.107.0, latest, main-latest ou main-stable et a été tirée (pull) entre le 21 octobre 11 h 46 UTC et 14 h 20 UTC, elle contient le malware.

Consulter les logs du serveur

Si le malware a été exécuté, vérifiez les logs système pour détecter d'éventuelles connexions suspectes ou des mouvements latéraux. Depuis la ligne de commande :

sudo journalctl --since "2026-10-21" | grep -i "ssh.*accepted"
sudo grep "Accepted" /var/log/auth.log | tail -50

Recherchez des connexions depuis des adresses IP inconnues. Si votre serveur héberge un serveur web (Nginx ou Apache), consultez aussi les logs d'accès pour repérer des requêtes inhabituelles. Auditez les scripts cron et les tâches planifiées pour vérifier qu'aucune porte dérobée n'a été ajoutée :

sudo crontab -l
sudo ls -la /etc/cron.d/
for user in $(cut -f1 -d: /etc/passwd); do echo "--- $user ---"; sudo crontab -u "$user" -l 2>/dev/null; done

Pour un audit plus poussé des processus et des connexions réseau, consultez nos guides auditer l'activité d'un serveur avec auditd et lister les ports en écoute avec ss.

Nettoyer un serveur compromis

Si vous avez détecté des fichiers malveillants, suivez cette procédure dans l'ordre. Utilisez un shell sh ou bash brut pour les premières étapes : tout lancement de Python réexécute le hook cook.pth.

1. Tuer le daemon

sudo lsof /tmp/remote_code_loader.lock 2>/dev/null | awk 'NR>1{print $2}' | xargs -r kill -9
rm -f /tmp/remote_code_loader.lock /tmp/.pc_once_*

2. Supprimer les fichiers malveillants

sudo find / -type f \( -name "cook.pth" -o -name "cook.py" \) -path "*/site-packages/*" -delete 2>/dev/null

Pour sitecustomize.py, vérifiez qu'il s'agit bien de la version malveillante (25 509 octets) avant de le supprimer, car certains projets utilisent légitimement ce fichier :

sudo find / -type f -name "sitecustomize.py" -path "*/site-packages/*" -size +20k -delete 2>/dev/null

3. Reconstruire les environnements Python

Ne vous contentez pas de réinstaller le package par-dessus : recréez vos environnements virtuels depuis zéro. Cette migration vers la version saine élimine tout résidu des scripts malveillants dans le répertoire site-packages :

rm -rf /chemin/vers/venv
python3 -m venv /chemin/vers/venv
source /chemin/vers/venv/bin/activate
pip install litellm==1.106.2

4. Redémarrer les services

Après le nettoyage, redémarrez tous les services qui utilisent Python pour vous assurer qu'aucun processus ne tourne encore avec le code compromis en mémoire :

sudo systemctl restart litellm
sudo systemctl restart votre-service-python

Vérifiez qu'aucun processus Python suspect ne persiste :

ps aux | grep python

5. Rotation de tous les secrets

Partez du principe que tout secret accessible depuis le serveur est compromis. Renouvelez dans cet ordre :

  • Clés SSH : générez de nouvelles paires et configurez les fichiers authorized_keys sur chaque serveur distant (configurer une clé SSH sur VPS Linux) ;
  • Tokens API (OpenAI, Anthropic, AWS, GCP, Azure) ;
  • Mots de passe des bases de données MySQL et PostgreSQL ;
  • Clés API LiteLLM (master key et virtual keys) ;
  • Tokens CI/CD (GitLab, Jenkins, GitHub).

Pensez aussi à vérifier vos sauvegardes (backup) : si une sauvegarde automatique a capturé l'état compromis, ne la restaurez pas telle quelle. Pour configurer la rotation automatique de vos mises à jour de sécurité, consultez notre guide automatiser les mises à jour de sécurité sur VPS Ubuntu.

Bonnes pratiques Docker pour auto-hébergeurs

Cet incident illustre pourquoi les tags :latest et les mises à jour automatiques de dépendances sont dangereux en production sur un VPS ou un serveur dédié. Que votre serveur tourne sur un VPS en virtualisation KVM, une machine virtuelle ou un serveur physique dédié, les mêmes règles s'appliquent à tout conteneur Docker :

  • Épinglez les versions : dans votre Dockerfile et votre fichier de configuration docker-compose.yml, spécifiez toujours un tag de version précis (litellm/litellm:v1.106.2) au lieu de :latest. Un docker pull sur :latest aurait tiré la version piégée pendant la fenêtre d'exposition. Consultez notre guide Docker Compose sur VPS Linux pour configurer vos services correctement.
  • Vérifiez les digests : pour les services critiques, épinglez le hash SHA256 de l'image plutôt que le tag. Un tag peut être réécrit par un attaquant, mais pas le digest : litellm/litellm@sha256:abc123...
  • Ne tirez pas en production automatiquement : désactivez les outils comme Watchtower ou les cron jobs de docker-compose pull && docker-compose up -d sur vos serveurs de production. Testez chaque nouvelle version sur un environnement isolé avant de la déployer.
  • Isolez les conteneurs : ne montez pas le socket Docker (/var/run/docker.sock) dans un conteneur qui n'en a pas besoin. Ne lancez pas les conteneurs en mode --privileged. Utilisez un utilisateur non-root dans vos images. Installer Docker sur un VPS détaille les configurations initiales sécurisées.
  • Monitoring des conteneurs : un conteneur compromis peut consommer des ressources anormales en processeur, RAM ou bande passante réseau sortante. Surveillez les ressources de votre VPS avec htop et btop pour repérer les anomalies. Auditez régulièrement les processus en cours avec docker top et les connexions réseau avec ss -tlnp.

Ces principes s'appliquent à tout service conteneurisé : proxy IA, panel Pterodactyl, serveur de jeu, base de données. Un seul conteneur tirant un tag compromis suffit à exposer l'ensemble des secrets du serveur.

Questions fréquentes

FAQ

Mon serveur utilise LiteLLM mais pas la version 1.107.0, suis-je touché ?

Non. Seule la version 1.107.0 publiée le 21 octobre 2026 contenait le malware. Si vous utilisez une version antérieure (comme 1.106.2) ou postérieure et que vous ne l'avez pas mise à jour pendant la fenêtre d'exposition (11 h 39 à 13 h 23 UTC sur PyPI), votre installation n'est pas compromise. Vérifiez avec pip show litellm.

J'utilise LiteLLM via Docker avec le tag :latest, suis-je concerné ?

Vous êtes concerné si vous avez exécuté un docker pull entre le 21 octobre 11 h 46 UTC et 14 h 20 UTC. Le tag :latest pointait vers la version piégée pendant cette fenêtre. Vérifiez la date de création de votre image avec docker inspect. Si elle tombe dans cet intervalle, considérez le serveur comme compromis et suivez la procédure de nettoyage.

Pourquoi ne pas utiliser le tag :latest en production ?

Le tag :latest est un pointeur mobile : il désigne toujours la dernière image publiée, sans garantie de contenu. Si un attaquant publie une version piégée, votre prochain docker pull tire automatiquement le malware. En épinglant un tag de version précis (v1.106.2), vous contrôlez exactement ce qui tourne sur votre serveur et vous pouvez tester avant de mettre à jour.

Comment le malware se lance-t-il automatiquement ?

Le fichier cook.pth contient la ligne import cook. Python exécute automatiquement les instructions import présentes dans les fichiers .pth du répertoire site-packages à chaque démarrage de l'interpréteur. Le malware se lance donc à chaque commande python, y compris via pip, un script cron ou un service systemd qui utilise Python.

Les clients LiteLLM Enterprise sont-ils concernés ?

Non. Les clients Enterprise qui utilisent le registre de release privé de LiteLLM n'ont pas reçu la version compromise. Seuls les utilisateurs du registre public PyPI et des images Docker publiques sont concernés.

Faut-il réinstaller tout le serveur après une compromission ?

La reconstruction complète du serveur est la méthode la plus sûre si le daemon de chargement de code distant (sitecustomize.py) a eu le temps de s'exécuter, car il peut avoir téléchargé et exécuté des charges utiles supplémentaires non documentées. Si vous ne pouvez pas reconstruire, suivez la procédure de nettoyage complète, puis auditez les processus actifs, les clés SSH autorisées et les tâches cron pour détecter toute persistance.

Le malware affecte-t-il aussi les serveurs sous Windows ?

Oui. Les wheels Python publiées sur PyPI ciblaient trois systèmes d'exploitation : Linux, macOS et Windows. Sous Windows, le daemon sitecustomize.py ne se détache pas en arrière-plan, mais le voleur cook.py s'exécute de façon identique à chaque lancement de l'interpréteur Python. Les serveurs Windows hébergeant LiteLLM via pip sont donc touchés de la même manière que les VPS Linux.

Comment surveiller mon serveur après le nettoyage ?

Activez un monitoring continu des ressources (processeur, RAM, bande passante réseau) avec htop, btop ou un outil de monitoring dédié. Surveillez les logs d'authentification (auth.log, journalctl) pour détecter des connexions depuis des adresses IP inconnues. Vérifiez régulièrement les processus actifs et les ports en écoute avec ss -tlnp. Si une activité suspecte apparaît dans les logs après le nettoyage, envisagez une reconstruction complète du serveur depuis une sauvegarde (backup) saine.

La virtualisation protège-t-elle contre ce type d'attaque ?

Non. Le malware s'exécute au niveau de l'interpréteur Python, pas de l'hyperviseur. Qu'il tourne sur une machine virtuelle, un VPS en virtualisation KVM, un conteneur Docker ou un serveur physique dédié, le voleur de credentials accède aux mêmes secrets. La virtualisation isole les machines entre elles, mais ne protège pas l'environnement compromis lui-même.

D'autres gestionnaires de packages présentent-ils le même risque ?

Oui. Les attaques de chaîne d'approvisionnement touchent tous les écosystèmes de packages : PyPI (Python), npm (Node.js), Composer (PHP), RubyGems, crates.io (Rust). Le principe est le même : un package légitime est remplacé par une version piégée pendant une courte fenêtre. Les bonnes pratiques sont identiques quel que soit le langage : épinglez les versions dans vos fichiers de configuration (requirements.txt, package.json, composer.json), vérifiez les checksums et testez les mises à jour sur un environnement isolé avant tout déploiement en production.