Blog · Sicherheitslage ·
LiteLLM 1.107.0: Lieferkettenangriff auf ein KI-Werkzeug
Am 21. Oktober 2026 erschien auf PyPI und Docker Hub die Version 1.107.0 von LiteLLM, einem verbreiteten Python-Paket und Proxy für den Zugriff auf Sprachmodelle vieler Anbieter. Sie enthielt einen Schädling, der beim Start des Python-Interpreters lief und Zugangsdaten einsammelte. Nach Angaben von LiteLLM war die Version innerhalb von drei Stunden entfernt. Wer sie installiert hat, muss seine Zugangsdaten als offengelegt behandeln. Hier steht, wie der Angriff lief, wer betroffen ist und was Agenturen daraus für KI-Werkzeuge in Kundenprojekten ableiten. Die Untersuchung läuft noch. Stand: 24. Oktober 2026.
Was passiert ist
LiteLLM ist ein Open-Source-Projekt der Firma Berri AI: eine Python-Bibliothek und ein Proxy-Server, die vor die Schnittstellen vieler Modellanbieter eine gemeinsame Schnittstelle setzen. Eine Anwendung spricht mit dem Proxy, der Proxy spricht mit OpenAI, Anthropic, Google oder einem eigenen Modell. Dafür hält er die API-Schlüssel aller angebundenen Anbieter, oft für mehrere Projekte oder Kunden zugleich.
Am 21. Oktober 2026 erreichte die Version 1.107.0 nach dem Bericht von The Hacker News PyPI als Wheels und als manipuliertes Quellarchiv und Docker Hub als Images. Der Code darin war schädlich. LiteLLM gibt an, die Version innerhalb von drei Stunden nach der Veröffentlichung entfernt zu haben. Auf PyPI steht sie heute unter Quarantäne: „This release has been quarantined by PyPI admins“, sie lässt sich nicht installieren, und die Projektseite verweist auf 1.106.2 als stabile Version.
Wie die Version zustande kam, hat LiteLLMs Sicherheitsverantwortlicher Oliver Jensen gegenüber The Hacker News beschrieben: „We have identified that the malicious actions were taken through three engineers' GitHub accounts.“ Ein Konto löste den Release-Lauf aus, ein zweites genehmigte ihn: „The threat actor used one user's account to trigger the project-releaser run, and another user's account to approve it.“ Die benutzten Token seien gestohlene OAuth-Token der GitHub-Kommandozeile; alle beteiligten Token sind widerrufen. Wie der Angreifer an sie kam, war am 21. Oktober offen: „We have not yet identified the mechanism by which the threat actor acquired the relevant tokens.“ Als Sofortmaßnahme entfernte LiteLLM alle Nutzer bis auf einen aus seiner GitHub-Organisation.
Einen Tag später nannte LiteLLM einen Verdacht. Eine interne Installation des eigenen Proxys war öffentlich erreichbar und protokollierte Antwortkörper. LiteLLM schreibt, man habe „specific situations in which Claude Code will embed a user's GitHub CLI OAuth token into a response body“ beobachtet, und: „in situations where response bodies are logged, these tokens may appear in LiteLLM logs.“ Auf der Installation fand sich zudem ein unerwartetes Nutzerkonto mit unauffälligem Namen. Am 19. Oktober hatte LiteLLM die Release-Rechte vorübergehend auf zwei Ingenieure erweitert. Offen bleibt, wie die Installation öffentlich wurde und ob dieser Weg allein genügte. Der Geschäftsführer Krrish Dholakia: „We're continuing our root cause investigation and will share the specific changes we're making to harden our release pipeline.“
Es ist der zweite Vorfall dieser Art bei LiteLLM in diesem Jahr. Im März 2026 war das Projekt im Zuge der Lieferkettenangriffe der Gruppe TeamPCP betroffen, mit demselben Startmechanismus wie jetzt. Danach verlegte LiteLLM seine Releases in ein privates Repository, machte Docker-Tags unveränderlich und signierte seine Images. Ob die beiden Vorfälle zusammenhängen, ist nicht belegt; die Sicherheitsfirma Socket sieht dafür keine Hinweise.
Wie der Angriff funktioniert
Der Schädling brauchte keinen Aufruf von LiteLLM. Er kam als Datei
cook.pth in das Paketverzeichnis von Python. Dateien mit dieser
Endung liest der Interpreter bei jedem Start, und eine Zeile, die mit
import beginnt, führt er aus. Die Datei enthielt eine Zeile:
import cook. Damit lief der Code, sobald irgendein Python-Programm in
dieser Umgebung startete, vor dem ersten Import von LiteLLM. Ein Testlauf, ein
Notebook, ein Skript in der CI, das nur pip install ausgeführt hat:
jedes davon genügte.
Gesammelt wurden laut The Hacker News Cloud-Token, Entwicklerkonten und private
Schlüssel, die Zugangsdaten-Dateien von KI-Werkzeugen, Browserdaten von Chromium
und Firefox, Umgebungsvariablen sowie die gespeicherten Zugangsdaten und die
Modellkonfiguration des LiteLLM-Proxys selbst. Der Schädling packte alles in ein
komprimiertes Archiv und sandte es per HTTP POST an einen Pfad
/healthcheck auf der Domain litellm[.]dev.
Eine zweite Stufe las eine Datei domain.txt aus einem
GitHub-Repository. Stand darin eine Domain, lief der Sammler erneut, holte von
dieser Domain den Pfad /codepy und führte die Antwort aus. Nach der
Analyse von StepSecurity prüfte ein Hintergrundprozess diesen toten Briefkasten
stündlich. Der Angreifer konnte also jede Stunde neuen Code nachliefern, ohne das
Paket zu ändern. Zum Zeitpunkt des Berichts antwortete das Repository mit 404.
Dass es ein Proxy für Sprachmodelle traf, ist kein Zufall. Ein solcher Proxy hält die Schlüssel aller Anbieter, die er anbindet, und seine Konfiguration ordnet Modelle Schlüsseln zu. Wer die Maschine liest, auf der er läuft, hat auf einen Schlag die Zugänge mehrerer Projekte, oft mehrerer Kunden. Genau diese Daten standen auf der Liste des Schädlings.
Wer betroffen ist
Betroffen ist, wer die Version 1.107.0 von PyPI installiert oder eines der Docker-Images aus dem Zeitfenster am 21. Oktober geladen hat. Das Fenster war kurz; nach Angaben von LiteLLM war die Version innerhalb von drei Stunden entfernt. Die Quellen nennen unterschiedliche Uhrzeiten, weshalb hier keine stehen. Frühere Versionen sind nach Angaben von LiteLLM nicht betroffen; The Hacker News empfiehlt, 1.106.2 festzuschreiben, und zwar über den Hash des Pakets. Unternehmenskunden, die ihre Images aus LiteLLMs privater Registry beziehen, sind nach Angaben des Unternehmens nicht betroffen.
Tückisch sind die indirekten Wege. Eine CI-Pipeline, die zur Bauzeit die jeweils neueste Version installiert, hat in diesem Fenster 1.107.0 gezogen, ohne dass jemand eine Datei geändert hat. Ein Werkzeug, das LiteLLM als Abhängigkeit mitbringt, hat es ebenso. Und weil der Schädling beim Start des Interpreters lief, zählt nicht, ob LiteLLM je benutzt wurde, sondern ob es installiert war.
Was Sie jetzt prüfen
-
Installationen finden. In jeder Umgebung, in der Python läuft:
Entwicklerrechner, Server, CI-Runner, Notebooks, Container.
pip show litellmoderpip freezezeigen die Version, bei Images der Digest des geladenen Tags. Suchen Sie auch in Lockfiles nach 1.107.0, denn ein Lockfile hält fest, was gebaut wurde, auch wenn es inzwischen überschrieben ist. - Den Rechner als kompromittiert behandeln. Alles, was der Prozess lesen konnte, gilt als offengelegt: zuerst die Schlüssel der Modellanbieter, dann Cloud-Token, SSH-Schlüssel, Browser-Sitzungen, Umgebungsvariablen. Beim Anbieter widerrufen, dann von einem sauberen Rechner aus neu ausstellen, dann die Verbrauchs- und Rechnungsübersicht der Anbieter auf fremde Nutzung prüfen.
- Neu aufbauen statt säubern. Der tote Briefkasten konnte stündlich neuen Code liefern. Was auf dem Rechner sonst noch angekommen ist, weiß niemand. Ein neues Image, eine neue Maschine, die Daten aus einer Sicherung von vor dem 21. Oktober.
- Festschreiben. Pakete mit Version und Hash in den Requirements oder im Lockfile, Images mit Digest statt Tag. Eine neue Version kommt nicht am Tag ihres Erscheinens in ein Kundenprojekt, sondern nach einer Wartefrist und einem Blick in die Änderungen.
- Eine eigene Laufzeit für KI-Werkzeuge. Ein Proxy für Sprachmodelle läuft getrennt von allem anderen, ohne Cloud-Zugänge mit Administratorrechten in derselben Umgebung, mit Schlüsseln je Kunde und Zweck und mit Ausgabenlimits beim Anbieter. Dann begrenzt ein Vorfall sich auf das, was dieser Prozess sehen durfte.
- Log-Hygiene. Anfrage- und Antwortkörper eines Proxys für Sprachmodelle gehören nicht ins Protokoll. Sie können Token tragen, wie LiteLLMs eigener Verdacht zum Einstieg zeigt. Was protokolliert wird, wird geschwärzt, kurz aufbewahrt und ist nicht öffentlich erreichbar.
- Eine Liste erlaubter Abhängigkeiten. Für Kundenprojekte gilt eine Liste geprüfter Pakete und ihrer Bezugsquellen. Ein neues Werkzeug kommt nach einer Prüfung hinein, nicht, weil es in einem Tutorial stand. Wer Software an Kunden übergibt, übergibt diese Liste mit; die Checkliste zur Übergabe hat dafür einen Platz.
Lieferkette als Dauerzustand
Ein weit verbreitetes Werkzeug ist in einem Jahr zweimal über seinen offiziellen Vertriebsweg kompromittiert worden. Nach dem ersten Mal hatte LiteLLM getan, was man rät: ein privates Repository für Releases, unveränderliche Docker-Tags, signierte Images, eine Freigabe durch eine zweite Person. Im Oktober hielt das nicht, weil der Angreifer die Token zweier Konten besaß. Der eine löste aus, der andere genehmigte. Die Signatur bestätigt dann, dass ein Release den offiziellen Weg genommen hat, nicht, dass dieser Weg unbenutzt von Fremden blieb.
Für Agenturen heißt das: Die Maßnahmen eines Herstellers am Release-Pfad sind nötig, aber nicht hinreichend. Was in der eigenen Hand bleibt, sind drei Dinge: die Wartefrist zwischen Erscheinen und Einsatz einer Version, der Blick in die Änderungen vor dem Einsatz und das Festschreiben auf eine geprüfte Version mit Hash. Ein Angriff, der drei Stunden lang online war, trifft nur, wer in diesen drei Stunden ohne Prüfung installiert hat.
Von Werkzeugen, die in Kundenprojekte kommen, lässt sich mehr verlangen als eine gute Dokumentation: eine veröffentlichte Geschichte der eigenen Vorfälle mit Berichten, signierte Releases mit nachvollziehbarer Herkunft, ein Release-Prozess, den eine gestohlene Sitzung allein nicht auslösen kann. LiteLLM hat seine Vorfälle offen beschrieben und Maßnahmen angekündigt. Das ist der Maßstab, an dem sich auch das nächste Werkzeug messen lassen muss, bevor es in ein Projekt kommt.
Was das für Kunden von obhut heißt
obhut führt keinen Code von LiteLLM aus. Aus diesem Projekt nutzt obhut allein die Preisliste der Modelle, die unter der MIT-Lizenz steht. Ein Operator lädt sie von Hand als Datei herunter und importiert sie; sie wird als Daten gelesen, und zur Laufzeit wird nichts von dort abgerufen. Von dem Vorfall ist obhut darum nicht betroffen.
Für die eigenen Abhängigkeiten gilt bei obhut: Jede Produktionsabhängigkeit ist in einem Katalog auf eine Version festgeschrieben. Für jede Abhängigkeit werden die Lizenz- und Hinweisdateien gelesen und ihre Hashes in einem Register festgehalten, das ein Test mit dem Arbeitsbereich vergleicht. Das Anwendungs-Image ist auf einen Digest festgeschrieben, und jedes Release prüft seine Images und Go-Binärdateien mit Trivy.
Eine Grenze bleibt, und sie gilt für jeden Betreiber: Festschreiben und Scannen halten ein Release nicht auf, das am Tag seines Erscheinens offiziell, signiert und schädlich ist. Dagegen helfen nur die Wartefrist und der Blick in die Änderungen, bevor eine neue Version in den Betrieb geht.
Häufige Fragen
Ich habe LiteLLM nie direkt installiert. Kann ich trotzdem betroffen sein?
Ja, wenn ein anderes Werkzeug es als Abhängigkeit mitgebracht hat oder eine CI-Pipeline am 21. Oktober die jeweils neueste Version installiert hat. Suchen Sie in Lockfiles und in jeder Python-Umgebung nach dem Paket und seiner Version, nicht nur in Ihren eigenen Requirements.
Reicht es, auf 1.106.2 zurückzugehen?
Nein. Der Wechsel der Version macht nicht ungeschehen, was beim Start des Interpreters gelaufen ist. Die Zugangsdaten, die der Prozess lesen konnte, sind zu widerrufen und neu auszustellen, der Rechner ist neu aufzubauen. Die Version 1.106.2 festzuschreiben, mit Hash, ist danach der richtige Stand.
Woran erkenne ich, ob die schädliche Version gelaufen ist?
An einer Datei cook.pth und einem Modul cook im
Paketverzeichnis von Python und an ausgehenden Verbindungen zu
litellm[.]dev in Ihren Protokollen. Fehlen diese Spuren, beweist das
nichts: Der Angreifer konnte Code nachliefern. War 1.107.0 installiert, gilt der
Rechner als kompromittiert, mit oder ohne Spuren.
Was unterscheidet diesen Fall von einer gewöhnlichen Sicherheitslücke?
Es gab keine Lücke, die ein Update schließt. Der offizielle Vertriebsweg hat den Schädling geliefert, mit gültiger Herkunft. Gegen eine Lücke hilft das Update, gegen einen vergifteten Vertriebsweg helfen Wartefrist, Prüfung, Festschreiben mit Hash und eine Laufzeit, in der ein Werkzeug nur sieht, was es braucht.