- Deux versions (1.82.7, 1.82.8) du paquet PyPI de la bibliothèque d’intégration LLM largement utilisée LiteLLM ont été distribuées avec une charge utile malveillante injectée, déclenchant lors de l’installation une attaque visant à voler les identifiants système
- L’origine de l’attaque remonte à une compromission de la chaîne d’approvisionnement de Trivy, l’outil de scan de sécurité CI/CD, qui a entraîné la fuite d’identifiants CircleCI puis le vol du jeton de publication PyPI et d’un PAT GitHub
- Les utilisateurs de l’image Docker officielle LiteLLM Proxy n’ont pas été affectés, car les versions y sont épinglées dans
requirements.txt, mais les environnements ayant fait un pip install direct depuis PyPI doivent être vérifiés immédiatement
- Des centaines de commentaires de spam automatisés ont inondé le fil d’issues GitHub, empêchant toute discussion utile ; il a été confirmé qu’il s’agissait d’une tentative délibérée de perturber la communication de réponse à l’incident
- L’impact s’étend à de nombreux projets dépendant de LiteLLM, comme DSPy et CrewAI, remettant une nouvelle fois en lumière les faiblesses structurelles de la sécurité de la chaîne d’approvisionnement logicielle
Vue d’ensemble de l’incident et circonstances de la découverte
- Lors de la configuration d’un nouveau projet, le système s’est comporté de manière anormale, avec épuisement de la RAM et lancement de processus de type fork bomb
- L’enquête a montré qu’un blob malveillant encodé en base64 avait été ajouté à
proxy_server.py, puis décodé pour créer et exécuter un fichier distinct
- La version 1.82.7 contenait la charge utile dans
litellm/proxy/proxy_server.py, avec exécution au import litellm.proxy
- La version 1.82.8 ajoutait en plus un fichier
litellm_init.pth, de sorte que le simple fait d’installer le paquet entraînait l’exécution automatique du malware au démarrage de Python
- Les fichiers
.pth utilisent un mécanisme exécuté automatiquement au démarrage par le module site de Python, permettant d’exécuter du code arbitraire après le mot-clé import
- Ce mécanisme existe depuis Python 2.1 et a été introduit sans PEP distinct
- L’équipe FutureSearch a signalé la première compromission : uvx a installé automatiquement la dernière version de litellm (sans version figée), puis Cursor a chargé automatiquement un serveur MCP local, déclenchant l’infection
Vecteur d’attaque et lien avec TeamPCP
- L’attaque a été attribuée au même acteur que le groupe TeamPCP, récemment impliqué dans la compromission de Trivy
- Chaîne de compromission : piratage de Trivy → fuite complète des identifiants CircleCI → vol du jeton de publication PyPI + PAT GitHub → diffusion des paquets malveillants
- Les comptes GitHub du CEO et du CTO de LiteLLM ont également été entièrement compromis : les descriptions de leurs dépôts personnels ont toutes été remplacées par "teampcp owns BerriAI", et des issues ont notamment été fermées
- Le jeton
PYPI_PUBLISH était stocké comme variable d’environnement dans le projet GitHub, et a fuité via Trivy
- Le compte avait bien la 2FA activée, mais le vol du jeton a permis de la contourner
- Les attaquants ont publié à répétition la même phrase sur les issues GitHub via des centaines de comptes bots, bloquant les échanges utiles
- Le même schéma a été observé sur le dépôt Trivy, avec plus de 700 commentaires de spam
- Parmi ces comptes de spam figuraient de vrais utilisateurs GitHub ayant déjà contribué ; certains historiques montrent depuis février des commits "Update workflow configuration" ayant injecté des voleurs d’identifiants dans des workflows CI
- La compromission de Trivy remonterait à au moins 5 jours plus tôt ; comme l’alerte la plus récente n’avait été publiée que la veille, les mainteneurs ont pu être touchés sans en avoir conscience
- Les attaquants ont aussi utilisé des canisters du protocole Internet Computer (ICP) pour livrer la charge utile, ce qui empêche de se défendre uniquement via du blocage DNS
Fonctionnement de la charge utile malveillante
- Elle crée un processus Python en arrière-plan, décode un stage embarqué et l’exécute
- Un collecteur d’identifiants est lancé ; en cas de collecte réussie, la clé AES est chiffrée avec la clé publique RSA de l’attaquant, puis les données volées sont transmises à un hôte distant
- URLs trouvées dans le malware :
checkmarx.zone/raw et models.litellm.cloud
- Les cibles principales sont surtout les clés SSH de
~/.git-credentials et les informations de portefeuilles crypto
- La charge causait une telle surcharge CPU qu’elle a paradoxalement facilité sa détection ; certains ont signalé avoir remarqué l’anomalie au bruit des ventilateurs
- Les mêmes symptômes sont apparus lors d’une installation de Harbor : un processus
grep -r rpcuser\rpcpassword tournait sous forme de fork bomb pour chercher des portefeuilles crypto
Réponse de l’équipe LiteLLM
- Les versions affectées (v1.82.7 et v1.82.8) ont été supprimées de PyPI
- Changement des mots de passe de tous les comptes mainteneurs, suppression et rotation de toutes les clés GitHub/Docker/CircleCI/pip
- Les nouveaux comptes mainteneurs sont désormais @krrish-berri-2 et @ishaan-berri
- L’ensemble du paquet a été temporairement mis en quarantaine sur PyPI, puis rétabli après retrait des versions infectées
- Toutes les nouvelles releases ont été suspendues et les publications gelées jusqu’à la fin d’un audit complet de la chaîne d’approvisionnement
- L’enquête et la remédiation sont menées en collaboration avec l’équipe sécurité Mandiant de Google
- Trivy a été épinglé sur sa dernière version sûre, v0.35.0 (au lieu du premier choix v0.69.3, révisé après retour de la communauté)
- Un renforcement de la sécurité est à l’étude : migration vers Trusted Publishing (OIDC à jeton JWT), comptes PyPI séparés, etc.
- L’heure de première publication des versions malveillantes serait autour de 08:30 UTC, et la mise en quarantaine par PyPI vers 11:25 UTC
Étendue de l’impact et projets en aval
- LiteLLM est l’unique bibliothèque d’appel de fournisseurs LLM de DSPy, et CrewAI l’utilise aussi en repli
- Airflow, Dagster, Unsloth.ai, Polar, nanobot dépendent également de LiteLLM
- D’après les recherches GitHub, plus de 628 projets incluent LiteLLM dans leur
requirements.txt sans figer de version
- Les utilisateurs du chemin de distribution officiel Proxy Docker ne sont pas affectés, car
requirements.txt y fige explicitement la version
- Dans les déploiements Docker, l’accès au système de fichiers hôte et aux variables d’environnement est plus limité, ce qui apporte une sécurité relative ; toutefois, les identifiants montés restent exposés
- La cible principale des attaquants est la clé SSH personnelle ; l’accès aux clés LLM semble secondaire
- Des utilisateurs d’outils comme Harbor ou browser-use, qui installent LiteLLM automatiquement comme dépendance, ont aussi signalé des dommages indirects
- CrewAI a épinglé litellm sur 1.82.6 (dernière version sûre), sans mentionner la compromission dans le message de commit
- DSPy a ouvert publiquement une issue pour suivre la réponse
- LangChain possède sa propre couche d’appel aux fournisseurs LLM et n’est donc pas directement affecté par cette compromission de la chaîne d’approvisionnement (sauf en cas d’usage optionnel du paquet
langchain-litellm)
Débat communautaire : sécurité de la chaîne d’approvisionnement et sandboxing
- Il n’est plus possible de faire confiance aveuglément aux dépendances et aux environnements de développement ; une défense en profondeur est jugée nécessaire, avec isolation par VM + primitives conteneur + listes d’autorisation + filtrage egress + seccomp + gVisor
- Après 50 ans de compromis en faveur de la facilité, le modèle de sécurité devrait être repensé en profondeur
- Certains estiment qu’il faut du sandboxing au niveau du langage de programmation
- Java disposait déjà d’un tel mécanisme dès la v1.2 dans les années 1990, mais il a été abandonné pour des raisons d’ergonomie
- Certains y voient le bon moment pour développer des langages orientés capabilities
- Le runtime workerd de Cloudflare est cité comme solution existante capable d’isoler des modules
- Des outils d’isolation au niveau OS existent déjà : pledge/unveil sur OpenBSD, chroot/namespace/cgroup sur Linux, Capsicum sur FreeBSD
- Guix peut créer en quelques secondes des conteneurs isolés capables d’installer des dépendances sans accéder à
$HOME
- Il est recommandé d’utiliser plus activement des outils d’isolation en espace utilisateur comme Firejail et bwrap
- Le sandboxing et le modèle de permissions (Intents) existent déjà sur mobile, mais les restrictions générales d’exécution restent mal acceptées sur desktop
- En réponse, certains soulignent que la fermeture des app stores d’Apple ou Meta et le sandboxing de sécurité sont deux sujets distincts, et qu’il est possible de construire des outils sûrs tout en laissant le contrôle à l’utilisateur
- Un outil canary/honeypot pour macOS a été publié (
github.com/dweinstein/canary) : il monte de faux secrets via WebDAV/NFS afin de détecter des accès anormaux
- Certains estiment qu’il faut ériger une barrière entre publication de paquets et dépôts publics : configurer un dépôt public directement comme Trusted Publisher élargit la surface d’attaque
- En réponse, d’autres rappellent que l’objectif initial de Trusted Publishing est de fournir un lien auditable entre le code source et l’artefact publié ; passer par un dépôt privé serait donc un recul
Recommandations de sécurité opérationnelle
- Les dépendances doivent obligatoirement être épinglées avec checksum SHA256
- Il faut exploiter un miroir interne de paquets pour éviter d’utiliser directement les dernières versions
- Il est préférable d’utiliser des artefacts de build et d’éviter de dépendre d’installations à la volée lors du déploiement, comme avec
uv run
- Cela élimine aussi le risque structurel d’un arrêt du système en cas d’indisponibilité de PyPI
- Les artefacts déployables offrent des avantages en auditabilité, rollback rapide et blocage des endpoints réseau sortants à risque
- Le paramètre
exclude-newer de uv permet d’exclure les nouveaux paquets publiés récemment
- On peut configurer
[tool.uv] exclude-newer = "5 days" dans pyproject.toml
- En CI/CD, la vraie solution consiste à remplacer les jetons de publication par des workflows OIDC afin de supprimer le secret lui-même
- GitHub et PyPI prennent tous deux en charge OIDC : si seul le job de publication a le droit d’accéder au point de terminaison OIDC, il n’existe plus de jeton à voler depuis un job Trivy
- Les outils de scan de sécurité comme Trivy doivent s’exécuter sur des workers séparés sans droits de publication
- Il faut maintenir les lockfiles et adopter des mises à jour hebdomadaires plutôt qu’une adoption immédiate des dernières versions
- Les fichiers
.pth de Python permettent l’exécution automatique de code ; l’option -S peut empêcher l’import de site, mais avec des problèmes potentiels de compatibilité
- Il est recommandé de scanner l’ensemble des dépendances du projet avec des outils comme
osv-scanner
- Commandes pour vérifier une infection :
find / -name "litellm_init.pth" -type f 2>/dev/null
find / -path '*/litellm-1.82.*.dist-info/METADATA' -exec grep -l 'Version: 1.82.[78]' {} \; 2>/dev/null
- Certains soulignent aussi la nécessité d’une rotation globale des identifiants à l’échelle de tout l’écosystème des gestionnaires de paquets
Audit SOC2 et question de fiabilité
- Il a été relevé que l’auditeur SOC2 de LiteLLM était Delve, une société récemment controversée
- SOC2 ne vérifie que le fait de suivre effectivement des प्रक्रेस documentés, et ne garantit pas le niveau réel de sécurité
- Même avec un SOC2 bien mené, il reste incertain qu’une telle attaque de chaîne d’approvisionnement aurait pu être empêchée
Projets alternatifs à LiteLLM
- Bifrost (
github.com/maximhq/bifrost) : alternative en Rust, permettant de configurer des clés virtuelles même sur une instance open source gratuite
- Portkey (
portkey.ai) : service proxy avec offre gratuite, jugé plus rapide que LiteLLM
- pydantic-ai : alternative basée sur Python
- any-llm (
github.com/mozilla-ai/any-llm) : projet de Mozilla
- LLM Gateway (
llmgateway.io) : propose un guide de migration depuis LiteLLM
- InferXgate (
github.com/jasmedia/InferXgate) : nouveau projet, avec une prise en charge limitée des fournisseurs
- Certains développeurs estiment qu’en pratique il n’existe que deux grands types d’API de fournisseurs LLM, OpenAI et Anthropic, et qu’un simple appel direct via
requests.post() serait plus sûr
- En réponse, d’autres rappellent que l’API compatible OpenAI d’Anthropic n’est pas recommandée comme solution long terme / production, et que les API natives de chaque fournisseur proposent des fonctionnalités spécifiques qui ne se mappent pas à l’API OpenAI
Aucun commentaire pour le moment.