Le 21 octobre à 11 h 39 UTC, sept wheels de litellm==1.107.0 ont été publiés sur PyPI. Chacun contenait un malware compilé : un voleur de credentials qui téléchargeait aussi des outils d'exécution de code à distance. Huit minutes plus tard, les tags Docker et GHCR latest, main-latest et main-stable pointaient vers la même compilation piégée.
À 13 h 23, le paquet était retiré. À 14 h 20, les tags de conteneurs étaient réaiguillés.
Deux heures. Assez pour qu'un runner CI l'installe, qu'un portable l'importe et que quelques clés d'API quittent le bâtiment.
Si vous construisez quoi que ce soit avec de l'IA, c'est votre stack maintenant. Pas une métaphore, pas l'histoire d'un autre secteur. LiteLLM est le proxy que beaucoup d'équipes placent devant OpenAI, Anthropic et Bedrock pour avoir une seule API, une seule clé, une seule facture. Quand il est compromis, la compromission hérite de toutes les clés de fournisseurs que vous y avez stockées.
La partie que tout le monde va sauter
L'échec intéressant n'est pas le malware. C'est la façon dont la version a été signée.
LiteLLM a passé l'année suivant son incident de mars à durcir son pipeline : publications depuis un dépôt privé, Trusted GitHub Publishers, tags immuables, images signées, un portable de publication enfermé au bureau. De leur propre aveu, l'attaquant n'a cassé aucun de ces contrôles. Il a utilisé des credentials déjà autorisés à les manipuler :
- Un token OAuth de la CLI GitHub appartenant à un développeur, capable d'exécuter des workflows.
- Un token appartenant à un propriétaire de l'organisation GitHub, capable de contourner la protection des branches et de forcer un changement de workflow.
- Les credentials d'un troisième compte, utilisés après publication pour supprimer les enregistrements d'exécution.
D'où venait le premier token ? D'un log.
L'enquête de LiteLLM a montré que Claude Code insère parfois le token OAuth de la CLI GitHub d'un utilisateur dans le corps d'une réponse. Leur proxy journalisait les corps de réponse. Le token s'est retrouvé dans les logs d'un déploiement LiteLLM interne exposé sur l'internet public. Un attaquant disposant d'un compte sur ce déploiement a lu les logs, pris le token et est entré dans le pipeline de publication.
Relisez la chaîne. Personne n'a fait de phishing. Personne n'a cassé de mot de passe. Un assistant de code que beaucoup d'équipes utilisent aujourd'hui a placé un credential vivant dans une réponse où il n'aurait jamais dû être, la couche d'observabilité l'a écrit sur disque, et le disque était accessible.
Pourquoi ce n'est pas un problème LiteLLM
Le schéma se généralise à chaque stack IA d'une entreprise normale cette semaine :
- Les tokens circulent dans le contexte du modèle. Les agents collent des en-têtes, des variables d'environnement et des sorties CLI dans les réponses. Tout ce qui est dans une réponse peut être journalisé, mis en cache, tracé ou évalué.
- Les proxies agrègent les credentials. Une passerelle IA détient les clés de chaque fournisseur vers lequel elle route. Fonctionnellement, c'est un gestionnaire de secrets avec une surface HTTP.
- Les agents héritent de l'identité du développeur. Si votre assistant tourne avec votre session
ghet vos droits de dépôt, un token volé n'est pas « un token », c'est vous. - Les déploiements fantômes n'ont pas de propriétaire. Un « compte utilisateur au nom anodin » sur un déploiement interne exposé sur internet est la découverte la plus banale du monde, et la plus coûteuse.
La même semaine a apporté un second avertissement indépendant. Railway a indiqué qu'un tiers non autorisé avait pu accéder à des variables d'environnement chiffrées et, pour certaines, en obtenir le contenu déchiffré, et estimait que la campagne visait des entreprises de plateforme pour atteindre des fournisseurs d'inférence IA. Ils ont demandé à tous leurs clients de renouveler toutes leurs clés.
Deux fournisseurs, une semaine, la même classe d'actif : des credentials qui vivent à côté de l'infrastructure IA.
Les quatre contrôles qui auraient brisé cette chaîne
Rien d'exotique. Chaque maillon meurt de quelque chose d'ennuyeux.
1. Traitez les logs comme une surface de credentials. Masquez à l'écriture, pas à la lecture. Si un corps de réponse peut contenir un token bearer, un token OAuth ou une chaîne de connexion, le logger doit filtrer avant le stockage. Gardez une rétention courte pour tout ce qui contient des prompts et des réponses, et considérez tout stockage de logs ayant contenu des tokens comme un événement de rotation.
2. Séparez « publier » de « contourner ». La barrière de publication existait et fonctionnait. Ce qui a échoué : la même classe d'identité pouvait la désactiver. Une protection de branche qu'un propriétaire d'organisation peut désactiver avec un token est une suggestion avec une piste d'audit. L'autorité de publication doit être derrière des credentials de courte durée liés au matériel et une seconde personne, et rien d'autre ne devrait pouvoir modifier le workflow qui les utilise.
3. Épinglez, puis prouvez. pip install litellm sans version, c'est l'instruction d'exécuter ce qu'internet a publié le plus récemment. Épinglez des versions exactes et des digests pour les images. Puis allez plus loin : un lockfile est une affirmation, pas une preuve. Générez un SBOM, vérifiez les signatures quand elles existent, et comparez ce qu'une nouvelle version fait réellement dans votre environnement.
4. Supposez l'exposition, pour que la réponse soit une checklist et non un débat. Les consignes de LiteLLM sont le modèle : si vous l'avez installé, considérez l'environnement comme compromis. Supprimez les fichiers dropper, tuez le démon qui détient /tmp/remote_code_loader.lock, renouvelez le rôle d'instance, chaque clé de fournisseur dans la base du proxy, les clés maîtresse et virtuelles, et le DATABASE_URL. Vérifiez ensuite CloudTrail pour GetCallerIdentity depuis le rôle d'instance au moment de l'installation, puis pour l'usage de ces credentials depuis l'extérieur de votre infrastructure.
Ce qu'il faut faire cette semaine
Si vous exploitez un proxy LiteLLM, faites ceci avant lundi :
pip show litellmsur chaque hôte, image et runner CI. Tout ce qui est en 1.107.0 est compromis, pas « peut-être compromis ».- Cherchez sur le système de fichiers
litellm-1.107.0.dist-info,cook.pthetsitecustomize.py. L'absence ne prouve rien ; la présence confirme l'exposition. - Renouvelez les clés de fournisseurs, les clés maîtresse et virtuelles, le mot de passe de la base et le rôle d'instance cloud.
- Si vous êtes sur Railway, renouvelez vos variables d'environnement et vos credentials de registre privé.
- Auditez qui peut modifier vos workflows de publication et retirez les tokens de propriétaire d'organisation de tout ce qui est automatisé.
Si vous préférez que quelqu'un parcoure le stack avec vous, c'est le type de mission que nous menons à prix fixe : inventaire des credentials, rotation et contrôles de pipeline pour arrêter le suivant. Écrivez à [email protected].
La conclusion inconfortable
L'histoire confortable, c'est qu'un fournisseur a passé une mauvaise semaine. L'histoire exacte, c'est que le stack IA moderne assemble trois choses qui n'ont jamais été pensées ensemble : un modèle qui répétera volontiers n'importe quel token de son contexte, un proxy qui stocke les clés de tout, et un outillage de publication qui fait plus confiance à un credential qu'à une personne.
La fenêtre du malware a duré deux heures. La trace de credentials qu'il laisse lui survivra. Réparez la trace.