Tous les articles

Un paquet, toutes vos clés : la compromission d'une release de LiteLLM

Sécurité · 4 min de lecture ·

J'écris du PHP et du Node, et ceci est un incident Python. J'en parle quand même, parce que le schéma ne dépend pas du langage : une bibliothèque placée devant tous vos fournisseurs d'IA détient toutes leurs clés, donc une seule release empoisonnée les atteint toutes. D'après le tracker d'incidents pwn.watch, c'est ce qui est arrivé à LiteLLM le 21 octobre 2026.

Ce que rapporte le tracker

Voici le résumé du tracker, soit le récit d'un seul agrégateur ; avant d'agir, c'est l'avis officiel du projet qu'il faut vérifier. Le tracker classe l'entrée comme critique et la marque comme corrigée. Il indique que des attaquants ont compromis le pipeline de release de LiteLLM et publié sur PyPI un litellm==1.107.0 malveillant, resté en ligne environ deux heures, ainsi que des tags compromis sur Docker Hub et GHCR : v1.107.0, latest, main-latest et main-stable. Toujours selon le tracker, les wheels déposent cook.pth, cook.py et sitecustomize.py, qui volent les identifiants cloud, développeur et d'outils IA à chaque démarrage de Python et ouvrent un chargeur de code à distance. Le tracker parle de la deuxième compromission de LiteLLM en 2026.

Le tag latest mérite qu'on s'y arrête. Un conteneur qui tire latest à chaque redémarrage n'avait besoin d'aucune mise à jour manuelle pour recevoir ce code.

L'affaire n'est peut-être pas isolée non plus. Railway, dans sa propre divulgation du même jour, dit penser que son incident fait partie d'une série d'attaques contre des plateformes, visant à compromettre des fournisseurs d'inférence IA.

Pourquoi une passerelle IA est la cible

Connaissance générale : une bibliothèque de passerelle ou de proxy existe pour que votre application parle à de nombreux fournisseurs de modèles via une seule interface. Le processus qui l'exécute a donc toutes les clés de fournisseurs dans son environnement en même temps. Une dépendance compromise ordinaire récupère les clés d'un service. Celle-ci récupère celles de tous, et une clé d'IA, c'est du budget que quelqu'un d'autre peut dépenser.

Release empoisonnée Processus passerelle toutes les clés en env Clé fournisseur A Clé fournisseur B Identifiants cloud une release, toutes les clés
Le rayon d'impact d'une passerelle, c'est chaque identifiant qu'on lui a confié, pas un seul service.

Les fichiers de démarrage aggravent les choses. Là encore, connaissance générale et comportement documenté de Python : les fichiers .pth de site-packages sont traités au lancement de l'interpréteur, et leurs lignes qui commencent par import sont exécutées ; sitecustomize tourne lui aussi au démarrage. Une wheel malveillante de ce type n'attend donc pas que votre code importe la bibliothèque. N'importe quel processus Python de cet environnement l'exécute, y compris un script ponctuel ou une suite de tests.

Pour les lecteurs PHP et Node, le même schéma vaut pour tout ce qui détient beaucoup d'identifiants : un wrapper autour de Stripe, de Twilio ou de plusieurs SDK d'IA, ou une CLI de déploiement qui a accès à tous les environnements.

Un confinement qui limite les dégâts

Rien de tout cela n'empêche un pipeline d'être compromis. Cela limite ce qu'une mauvaise release peut atteindre.

1. version et hash épinglés décident de ce qui entre 2. conteneur isolé, aucun secret de prod hors les siens Bibliothèque passerelle 3. Clés limitées, courtes, plafonnées
Chaque couche réduit ce qu'une release empoisonnée peut atteindre, même si elle est installée.

Épinglez par version et par hash. Un numéro de version seul n'arrête pas un fichier republié ou remplacé ; un hash, si. Avec pip, un fichier de requirements qui liste les hashes et l'option --require-hashes refuse tout le reste ; les lock files de uv et de pip-tools font le même travail. Épinglez les images de conteneur par digest plutôt que par un tag mouvant comme latest, et attendez quelques jours avant d'adopter une release toute neuve. Isolez. Installez et exécutez la passerelle dans son propre environnement ou conteneur, qui ne détient que les clés nécessaires, et n'installez jamais de nouveaux paquets sur une machine où des secrets de production sont chargés. Limitez la portée des clés. Donnez à chaque fournisseur une clé distincte, de courte durée et avec un plafond de dépense, pour qu'une clé volée coûte peu et se remplace vite.

pip install --require-hashes -r requirements.txt
pip show litellm
Traitez toute bibliothèque qui détient plusieurs clés de fournisseurs comme la dépendance la plus sensible que vous ayez : épinglez-la par hash, faites-la tourner seule, et donnez-lui des clés qui expirent.

Si la 1.107.0 a été installée ou tirée

Ordre général : sortez l'environnement ou le conteneur du service, vérifiez quelle version et quel tag d'image ont réellement tourné, reconstruisez à partir d'une release saine et épinglée, puis faites tourner chaque clé que cet environnement pouvait lire, depuis une machine propre. À cause des fichiers de démarrage, tout processus Python de cet environnement compte, pas seulement la passerelle. Confirmez les versions et tags touchés dans l'avis officiel de LiteLLM.

Source : le tracker d'incidents pwn.watch, entrée « LiteLLM 1.107.0 PyPI/Docker supply-chain compromise ».

Ce que disent les lecteurs

  1. Pas encore de mot. Le premier est toujours le plus courageux.
Vous construisez quelque chose du genre ? Je peux vous aider. Écrivez-moi sur WhatsApp