# LiteLLM : du secret oublié dans Git à une release malveillante signée

> La version 1.107.0 de LiteLLM a distribué un malware via PyPI et Docker. Chaîne d’accès, contournement du pipeline, preuves et réponse à incident.

Publié le: 2026-11-04  
Canonical: https://rootzero.eu/research/litellm-release-empoisonnee-2026/

**Publication des artefacts compromis : 21 octobre 2026. Analyse rédigée le 4 novembre 2026.** Pendant environ deux heures, `litellm==1.107.0` sur PyPI et plusieurs tags Docker de LiteLLM pointaient vers du code malveillant. Le [rapport final de LiteLLM](https://docs.litellm.ai/blog/security-incident-oct-21-2026-attack-chain) montre une chaîne remarquable : secret publié en mai, jeton toujours valide en octobre, accès propriétaire à l’organisation GitHub, modification du workflow de release, puis construction depuis un dépôt ressemblant au dépôt officiel. Le paquet et les images ont été **signés par l’infrastructure légitime**. La signature prouvait donc le passage par ce pipeline, pas l’innocuité de sa source.

## La chaîne de confiance, maillon par maillon

Le 27 mai, un fichier `opencode.json` contenant une clé administrateur d’une plateforme interne expérimentale a été commité dans un dépôt public. Un commit ultérieur a supprimé le fichier trois heures plus tard, mais la clé n’a pas été révoquée et l’ancien commit est resté lisible dans l’historique. Le 10 octobre, l’attaquant a utilisé cette clé pour interroger l’API des agents gérés. La réponse contenait un jeton OAuth `gh` appartenant à un propriétaire de l’organisation GitHub de LiteLLM. ([Chronologie détaillée](https://docs.litellm.ai/blog/security-incident-oct-21-2026-attack-chain))

Ce premier pivot a changé l’échelle de l’incident. Un jeton de propriétaire pouvait déclencher des workflows, pousser sur des dépôts et outrepasser certaines protections. L’attaquant a créé des branches et workflows éphémères pour exfiltrer des secrets de CI, dont deux autres jetons de mainteneurs. Ces actions sont documentées par les traces d’API et de workflows dans le rapport. Elles rappellent que les secrets disponibles à un job peuvent être obtenus **par un workflow autorisé à tourner**, même si l’attaquant ne peut pas les lire directement depuis l’interface GitHub.

Le 21 octobre, l’attaquant a modifié le workflow du dépôt privé `BerriAI/project-releaser`. Il a ajouté un paramètre `source_owner` permettant de choisir l’organisation source. Un push avec un jeton de mainteneur a été refusé par la protection de branche ; le même push avec le jeton du propriétaire a réussi. Le workflow a ensuite construit depuis `Berri-AI/litellm`, organisation au nom visuellement proche de `BerriAI/litellm`, puis une seconde identité compromise a validé la porte d’approbation de production. Le tag source officiel `v1.107.0` sur GitHub ne contient pas les fichiers malveillants : comparer le tag au paquet PyPI suffit à révéler que la provenance du build a divergé. ([Rapport LiteLLM](https://docs.litellm.ai/blog/security-incident-oct-21-2026-attack-chain), [avis sur les artefacts](https://docs.litellm.ai/blog/security-incident-oct-21-2026))

## Ce que faisait le paquet et pourquoi la signature ne suffisait pas

Selon [LiteLLM](https://docs.litellm.ai/blog/security-incident-oct-21-2026), la version compromise contenait du malware compilé qui volait des identifiants et chargeait des outils d’exécution de code distante. Les fichiers `cook.pth` et `sitecustomize.py` sont des indicateurs importants : Python peut exécuter du code via ces mécanismes **au démarrage de l’interpréteur**, avant l’appel voulu par l’utilisateur. Ainsi, lancer `pip` dans l’environnement infecté peut relancer le code malveillant. Le contenu exact et l’étendue des secrets accessibles dépendent de l’hôte ou du conteneur où l’artefact a été exécuté.

La chaîne de signature n’a pas été cryptographiquement cassée. Le workflow officiel a construit et publié un artefact venant d’une source substituée. LiteLLM précise que les images compromises avaient même une signature valide sous l’ancienne clé Cosign ; l’équipe a ensuite changé cette clé. Vérifier seulement « signature valide » répond à la question *qui a signé ?* ; il faut aussi vérifier *quel commit et quel dépôt ont été construits ?* et *quel digest exact tourne ?*.

## Comment on pouvait trouver l’attaque

La première alerte publique est une [issue GitHub](https://github.com/BerriAI/litellm/issues/47826) publiée le 21 octobre à 12 h 06 UTC. Une enquête défensive aurait plusieurs points d’appui indépendants : recherche de secrets dans **tout l’historique Git**, inventaire des clés non révoquées, audit des appels à l’API interne, suivi des contournements de protection de branche, comparaison entre source qualifiée et source du build, puis inspection des roues et images publiées. Les journaux d’exécution avaient été partiellement effacés par l’attaquant ; les événements d’audit GitHub et les digests de registre gardent donc une valeur distincte.

La fenêtre confirmée est précise : sept roues PyPI publiées vers 11 h 40 UTC et isolées avant 13 h 23 ; images Docker publiées à partir de 11 h 46 et retirées ou réorientées avant 14 h 20. Les tags `latest`, `main-latest` et `main-stable` ont été concernés, pas uniquement `v1.107.0`. Les versions antérieures et le registre privé Enterprise étaient déclarés non affectés dans l’[avis de l’éditeur](https://docs.litellm.ai/blog/security-incident-oct-21-2026).

## Réponse d’un exploitant exposé

Vérifier les versions, les digests **historiques** et les journaux de déploiement, puis isoler les environnements qui ont démarré l’artefact. Un tag Docker corrigé aujourd’hui ne prouve pas que son digest était sain pendant la fenêtre d’attaque. LiteLLM recommande de reconstruire conteneurs et environnements Python depuis une source vérifiée ; si l’artefact a tourné avec un accès sortant, considérer les secrets accessibles comme exposés et les renouveler, y compris clés de fournisseurs de modèles, rôle IAM, `DATABASE_URL` et clés du proxy. Une simple désinstallation ne fait pas revenir un secret exfiltré. ([Instructions officielles](https://docs.litellm.ai/blog/security-incident-oct-21-2026))

La cause systémique est l’addition de deux frontières mal tenues : un secret « supprimé » mais jamais invalidé, puis un pipeline qui autorisait un propriétaire compromis à changer la source du build. Les approbations et les signatures restaient présentes dans l’interface ; elles validaient un processus dont la provenance avait été détournée.

La revue des jetons et des droits de déploiement rejoint [l’analyse de l’attaque Binance](/research/binance-signature-portefeuille-eip7702-2026/), avec une chaîne d’accès différente. Pour une plateforme d’agents, notre guide des [contrats et de l’orchestration multi-agent](/research/systemes-multi-agents-contrats-orchestration/) explique comment borner les outils, les permissions et les actions soumises à validation ; ces contrôles complètent la vérification des dépendances.

**Sources principales :** [rapport de cause racine LiteLLM](https://docs.litellm.ai/blog/security-incident-oct-21-2026-attack-chain), [avis et mesures d’urgence LiteLLM](https://docs.litellm.ai/blog/security-incident-oct-21-2026).
