{"id":5808,"date":"2026-10-23T13:54:46","date_gmt":"2026-10-23T11:54:46","guid":{"rendered":"https:\/\/wwwschutz.de\/blog\/?p=5808"},"modified":"2026-10-23T16:10:43","modified_gmt":"2026-10-23T14:10:43","slug":"litellm-supply-chain-angriff-ki-gateway","status":"publish","type":"post","link":"https:\/\/wwwschutz.de\/blog\/litellm-supply-chain-angriff-ki-gateway\/","title":{"rendered":"LiteLLM Supply-Chain-Angriff: Was Betreiber von KI-Gateways jetzt tun m\u00fcssen"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><em>Am 21. Oktober 2026 lieferte die Release-Pipeline von LiteLLM Schadcode aus: Version 1.107.0 stahl Zugangsdaten und war darauf ausgelegt, weiteren Code nachzuladen. Beschrieben wird, wer betroffen sein kann, wie sich ein Befall pr\u00fcfen und bereinigen l\u00e4sst, ohne den Schadcode erneut zu starten, und was Betreiber von KI-Gateways dauerhaft \u00e4ndern sollten.<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein KI-Gateway steht zwischen Anwendungen und Modellanbietern. Es b\u00fcndelt Schl\u00fcssel, leitet Anfragen weiter und protokolliert oft Prompts und Antworten. Ger\u00e4t ein solcher Baustein unter fremde Kontrolle, ist der Schaden gr\u00f6\u00dfer als bei einer gew\u00f6hnlichen Bibliothek. Der LiteLLM Supply-Chain-Angriff vom 21. Oktober 2026 zeigt das: In wenigen Stunden gelangte Schadcode auf Server, Container und Entwicklerrechner. Wer LiteLLM kompromittiert vorfindet oder dies vermutet, braucht eine klare Reihenfolge statt Aktionismus.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Stand der Quellen: 22.10.2026. Das Projekt hat weitere Updates angek\u00fcndigt, einzelne Angaben k\u00f6nnen sich daher \u00e4ndern.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zur Abgrenzung: Im M\u00e4rz 2026 gab es bereits einen fr\u00fcheren Vorfall mit anderen Versionen (1.82.7 und 1.82.8). Hier geht es ausschlie\u00dflich um den Oktober-Vorfall und die Version 1.107.0. Die Angaben st\u00fctzen sich auf die <a href=\"https:\/\/docs.litellm.ai\/blog\/security-incident-oct-21-2026\" target=\"_blank\" rel=\"noopener\">Vorfallsseite von LiteLLM<\/a> und die technischen Analysen von <a href=\"https:\/\/www.wiz.io\/blog\/litellm-compromised-steals-credentials\" target=\"_blank\" rel=\"noopener\">Wiz<\/a> und <a href=\"https:\/\/research.jfrog.com\/post\/litellm-hijacked-again\/\" target=\"_blank\" rel=\"noopener\">JFrog<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Was am 21. Oktober 2026 passiert ist<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Laut LiteLLM wurde die Release-Pipeline des Projekts kompromittiert. Dabei entstand eine Version 1.107.0, die nicht dem ver\u00f6ffentlichten Quelltext entsprach. Sie landete auf drei Wegen bei Nutzern:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>als Paket <code>litellm<\/code> auf PyPI, dem zentralen Paketverzeichnis f\u00fcr Python,<\/li>\n\n\n\n<li>als Container-Image auf Docker Hub,<\/li>\n\n\n\n<li>als Container-Image in der GitHub Container Registry (GHCR).<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Bei den Images waren die Tags <code>v1.107.0<\/code>, <code>latest<\/code>, <code>main-latest<\/code> und <code>main-stable<\/code> betroffen. Besonders t\u00fcckisch: Der offizielle Quelltext-Tag <code>v1.107.0<\/code> auf GitHub enth\u00e4lt laut Projekt keine Schaddateien. Die Artefakte stammten aus einem vom Angreifer kontrollierten Doppelg\u00e4nger-Repository. Wer den Quelltext pr\u00fcft, sieht also nichts Auff\u00e4lliges. Enterprise-Kunden, die die private Registry des Projekts nutzen, waren laut LiteLLM nicht betroffen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Alle Zeiten stammen aus der Vorfallsseite und gelten f\u00fcr den 21. Oktober 2026 in UTC. Zur Umrechnung: In Deutschland gilt noch die Sommerzeit (UTC plus zwei Stunden), 11:39 UTC entspricht also 13:39 Uhr.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Kanal<\/th><th>Betroffen<\/th><th>Zeitfenster (UTC)<\/th><\/tr><\/thead><tbody><tr><td>PyPI<\/td><td>litellm 1.107.0 (sieben Wheels, so hei\u00dfen vorgebaute Pakete)<\/td><td>11:39 bis 13:23<\/td><\/tr><tr><td>Docker Hub<\/td><td>Tags v1.107.0, latest, main-latest, main-stable<\/td><td>11:46 bis 14:20<\/td><\/tr><tr><td>GHCR<\/td><td>gleiche Tags, unter anderem litellm, litellm-database, litellm-non_root<\/td><td>ab 11:46, sp\u00e4testens bis 13:51<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"563\" src=\"https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/zeitleiste-c6371129-1024x563.webp\" alt=\"Zeitfenster der Schadversion beim LiteLLM Supply-Chain-Angriff auf PyPI, Docker Hub und GHCR am 21. Oktober 2026 in UTC\" class=\"wp-image-5804\" srcset=\"https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/zeitleiste-c6371129-1024x563.webp 1024w, https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/zeitleiste-c6371129-300x165.webp 300w, https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/zeitleiste-c6371129-768x422.webp 768w, https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/zeitleiste-c6371129-1536x845.webp 1536w, https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/zeitleiste-c6371129-640x352.webp 640w, https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/zeitleiste-c6371129.webp 1600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Zeitfenster der Schadversion 1.107.0 je Kanal (UTC, 21. Oktober 2026).<\/figcaption><\/figure>\n\n\n\n\n<p class=\"wp-block-paragraph\">Die Zeitleiste der Vorfallsseite beginnt fr\u00fcher. Um 10:08 UTC wurde eine \u00c4nderung am Release-Workflow vorgenommen, um 11:08 lief die Produktions-Pipeline an, und gegen 11:40 erschienen die sieben Wheels auf PyPI. Um 13:23 UTC entfernte PyPI die Version. Die GHCR-Tags waren um 13:51 UTC nicht mehr verf\u00fcgbar, die Docker-Hub-Tags zeigten noch l\u00e4nger auf das kompromittierte Image. Erst um 14:20 UTC waren alle betroffenen Tags entfernt oder auf gepr\u00fcfte Artefakte umgestellt. Die Vorfallsseite nennt in ihrer \u00dcbersicht au\u00dferdem eine abweichende Uhrzeit f\u00fcr den Beginn. Diese Abweichung wird hier nicht aufgel\u00f6st, die Beitragsangaben folgen der UTC-Zeitleiste.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Als Meldestelle im Repository dient das <a href=\"https:\/\/github.com\/BerriAI\/litellm\/issues\/47826\" target=\"_blank\" rel=\"noopener\">Issue 47826<\/a>. Details aus dem Issue werden hier nicht verwendet.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wer vom LiteLLM Supply-Chain-Angriff betroffen sein kann<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Supply-Chain-Angriff, auf Deutsch Lieferkettenangriff, trifft nicht das Ziel selbst, sondern einen Baustein, dem das Ziel vertraut. Hier war es ein Softwarepaket, das automatisch bezogen wird. Betroffen sein k\u00f6nnen daher alle, die im genannten Zeitfenster Artefakte aus den drei Kan\u00e4len bezogen oder gestartet haben:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Installationen mit <code>pip install litellm<\/code> ohne feste Version, die im Zeitfenster liefen und 1.107.0 zogen.<\/li>\n\n\n\n<li>Installationen, die ausdr\u00fccklich auf <code>litellm==1.107.0<\/code> festgelegt waren. Auch das feste Pinning, also das Festlegen einer genauen Version, sch\u00fctzte hier nicht.<\/li>\n\n\n\n<li>Docker-Starts oder Neubauten von Images mit den genannten Tags im Zeitfenster, auch in CI-Pipelines und auf Entwicklerrechnern.<\/li>\n\n\n\n<li>Images, die intern auf Basis eines dieser Tags gebaut und anschlie\u00dfend weiterverwendet wurden.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Nicht betroffen sind laut Projekt Versionen bis einschlie\u00dflich 1.106.2. Als R\u00fcckfallstand empfiehlt LiteLLM, auf 1.106.2 oder eine ausdr\u00fccklich vor dem Vorfall ver\u00f6ffentlichte Version festzulegen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein weiterer Punkt ist die Frage der Abh\u00e4ngigkeiten. Viele Werkzeuge ziehen LiteLLM als Unterpaket nach. Beim fr\u00fcheren Vorfall im M\u00e4rz wurde das laut <a href=\"https:\/\/docs.litellm.ai\/blog\/security-update-march-2026\" target=\"_blank\" rel=\"noopener\">LiteLLM<\/a> belegt. F\u00fcr den Oktober liegt es nahe, ist aber nicht einzeln best\u00e4tigt. Deshalb lohnt sich ein Blick in Lockfiles und Abh\u00e4ngigkeitsb\u00e4ume, nicht nur in die direkt installierten Pakete.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Unterschied zum M\u00e4rz-Vorfall ist wichtig. Damals galt laut der <a href=\"https:\/\/docs.litellm.ai\/blog\/security-update-march-2026\" target=\"_blank\" rel=\"noopener\">Meldung zum M\u00e4rz-Vorfall<\/a> das offizielle Proxy-Image als nicht betroffen. Diesmal waren Images betroffen. Wer sich auf die fr\u00fchere Einsch\u00e4tzung verl\u00e4sst, k\u00f6nnte den Container-Betrieb zu Unrecht f\u00fcr sicher halten.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die Reichweite l\u00e4sst sich nicht verl\u00e4sslich beziffern. Wiz nennt rund 49.000 Downloads laut einer Statistik-Webseite, doch wie viele davon tats\u00e4chlich zum Einsatz kamen, ist offen. Entscheidend ist ohnehin der eigene Bestand: Ein einziger betroffener Host mit Zugriff auf Cloud-Rollen gen\u00fcgt f\u00fcr den Ernstfall.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Was der Schadcode tut: Start bei jedem Python-Aufruf<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Laut JFrog legt das Paket drei Dateien im Verzeichnis <code>site-packages<\/code> ab, in das Python seine Pakete installiert: <code>cook.pth<\/code>, <code>cook.py<\/code> und <code>sitecustomize.py<\/code>. Der Mechanismus ist unscheinbar und wirksam.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Eine <code>.pth<\/code>-Datei ist eigentlich daf\u00fcr gedacht, Suchpfade f\u00fcr Python zu erg\u00e4nzen. Zeilen, die mit <code>import<\/code> beginnen, werden aber beim Start des Interpreters ausgef\u00fchrt. Die Datei <code>cook.pth<\/code> importiert so <code>cook.py<\/code>.<\/li>\n\n\n\n<li><code>sitecustomize.py<\/code> ist ein Hook, den Python beim Start automatisch l\u00e4dt, sofern die Datei existiert. Nicht jede solche Datei ist b\u00f6sartig, sie kommt gelegentlich legitim vor. Unerwartete Fundstellen sind aber zu pr\u00fcfen.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Die Folge: Der Code l\u00e4uft bei jedem Start von Python, auch wenn die Anwendung <code>litellm<\/code> nie importiert. Auch ein Aufruf von <code>pip<\/code> startet Python und kann den Schadcode ausl\u00f6sen. Genau deshalb st\u00fctzen sich die Pr\u00fcfschritte weiter unten zuerst auf das Dateisystem.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Gesammelt wird laut JFrog und Wiz sehr viel. Dazu z\u00e4hlen Zugangsdaten f\u00fcr Cloud-Dienste (AWS, Azure, Google Cloud), Kubernetes- und Docker-Konfigurationen, SSH- und GPG-Schl\u00fcssel, Zugangsdaten f\u00fcr Git, npm und PyPI, Umgebungsvariablen und <code>.env<\/code>-Dateien, Browserdaten sowie Zugangsdaten von KI-Werkzeugen. Auf Systemen mit einer LiteLLM-Proxy-Datenbank liest der Code laut beiden Analysen die dort gespeicherten Schl\u00fcssel der Modellanbieter aus.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die Daten werden verschl\u00fcsselt und per POST an eine nachgebaute Domain gesendet, die dem Original \u00e4hnelt. Sie wird hier als <code>litellm[.]dev<\/code> entsch\u00e4rft geschrieben und sollte im Netz gesperrt werden. Ohne den privaten Schl\u00fcssel des Angreifers l\u00e4sst sich mitgeschnittener Verkehr nicht entschl\u00fcsseln. Das hilft den Betroffenen aber nicht, denn die Daten sind abgeflossen, sobald sie den Host verlassen haben.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Der Nachlade-Loader<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Zus\u00e4tzlich enth\u00e4lt <code>sitecustomize.py<\/code> laut JFrog einen Loader. Er startet auf Linux und macOS einen losgel\u00f6sten Hintergrundprozess, der eine Lock-Datei unter <code>\/tmp\/remote_code_loader.lock<\/code> h\u00e4lt. Der Prozess ist darauf ausgelegt, st\u00fcndlich eine Adresse aus einer \u00f6ffentlichen GitHub-Datei zu lesen, den sogenannten Dead Drop. Das ist ein toter Briefkasten: Der Angreifer kann dort jederzeit eine Adresse hinterlegen, ohne selbst direkt aufzutreten. Dann w\u00fcrde der Loader weiteren Code von dort holen und ausf\u00fchren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Analysezeitpunkt war diese Datei nach JFrog leer, laut Wiz nur mit Testwerten belegt. Das Repository wurde von GitHub gegen 13:30 UTC entfernt. Ein Nachladen wurde nicht beobachtet. Ob es dazwischen Nachladungen gab, ist nicht belegt. Der Loader bleibt dennoch ein m\u00f6glicher Fernzugriffsweg und geh\u00f6rt zur Bereinigung. Auf Windows scheitert der Hintergrundteil laut JFrog, weil er auf eine Funktion angewiesen ist, die dort fehlt. Der Stealer in <code>cook.py<\/code> l\u00e4uft aber auch dort.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wie der Angriff m\u00f6glich wurde: Tokens in Logs<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Der Kern der Lehre liegt nicht im Schadcode, sondern im Weg, auf dem er in die Release-Pipeline gelangte. Wiedergegeben wird hier die Darstellung von LiteLLM, erg\u00e4nzt um den Hinweis, dass der Erstzugang unbest\u00e4tigt ist.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der Angreifer verf\u00fcgte laut Projekt \u00fcber GitHub-CLI-Zugangsdaten von drei Entwicklerkonten. Eine davon hatte Berechtigungen f\u00fcr Workflows, ein weiteres Token geh\u00f6rte einem Organisations-Owner. Damit wurden Branch-Schutzregeln deaktiviert und der Release-Workflow so ge\u00e4ndert, dass er ein Doppelg\u00e4nger-Repository nutzte. Die dort gebauten Artefakte wurden anschlie\u00dfend signiert und ver\u00f6ffentlicht. Au\u00dferdem wurden laut Projekt Workflow-Protokolle gel\u00f6scht, was die Analyse erschwert. Am Montag davor waren Release-Rechte f\u00fcr zwei interne Ingenieure vor\u00fcbergehend erweitert worden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Woher stammten die Tokens? Das Projekt vermutet: aus Logs einer \u00f6ffentlich erreichbaren internen LiteLLM-Instanz. Laut LiteLLM kann Claude Code das OAuth-Token der GitHub-CLI in einen Antworttext einbetten. Wird der Antwortk\u00f6rper protokolliert, landet das Token im Log. Das ist eine Aussage des Projekts mit den Worten \u201ekann\u201c und \u201evermutet\u201c. Sie ist kein belegter Fehler eines Herstellers und wird hier nicht als solcher dargestellt. Als Ausgangspunkt nennt die Vorfallsseite ein unbekanntes, unauff\u00e4llig benanntes Konto auf dieser Instanz. M\u00f6glicherweise f\u00fchrte eine k\u00fcrzlich geschlossene Sicherheitsl\u00fccke dorthin, best\u00e4tigt ist das nicht.<\/p>\n\n\n\n<figure class=\"wp-block-pullquote\"><blockquote><p>Ein KI-Gateway sieht, was Anwendungen und Agenten anfragen und antworten. Landet das im Log, wird das Log zum Schl\u00fcsselbund.<\/p><\/blockquote><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Beobachtung gilt weit \u00fcber LiteLLM hinaus. Wer KI-Agenten oder Coding-Werkzeuge mit weitreichendem Zugriff einsetzt, sollte wissen, welche Geheimnisse in Eingaben und Antworten auftauchen k\u00f6nnen. Zur Kontrolle solcher Agenten gibt es einen Beitrag \u00fcber <a href=\"https:\/\/wwwschutz.de\/blog\/ki-agentensysteme-und-datenschutz\/\" data-type=\"post\" data-id=\"4310\">KI-Agentensysteme und Datenschutz<\/a>. Auch die den Risiken von Entwicklerwerkzeugen mit viel Handlungsspielraum \u00fcber Agentic Coding sind zu nennen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Bemerkenswert ist auch die Rolle der Signatur. Die ver\u00f6ffentlichten Artefakte waren signiert. Das beweist nur, dass der Workflow lief, nicht, dass der Code der erwartete war. Eine Signatur ist so vertrauensw\u00fcrdig wie die Pipeline, die sie erzeugt. Die Vorfallsseite betont zudem, dass der Angreifer mehrere voneinander unabh\u00e4ngige Schutzma\u00dfnahmen \u00fcberwinden musste.<\/p>\n\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"819\" src=\"https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/angriffsweg-6d64c163-1024x819.webp\" alt=\"F\u00fcnf Stufen des Angriffswegs von Tokens in Logs bis zu signierten Schadartefakten\" class=\"wp-image-5805\" srcset=\"https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/angriffsweg-6d64c163-1024x819.webp 1024w, https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/angriffsweg-6d64c163-300x240.webp 300w, https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/angriffsweg-6d64c163-768x614.webp 768w, https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/angriffsweg-6d64c163-1536x1229.webp 1536w, https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/angriffsweg-6d64c163-640x512.webp 640w, https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/angriffsweg-6d64c163.webp 1600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Angriffsweg laut Vorfallsseite von LiteLLM, der Erstzugang ist unbest\u00e4tigt.<\/figcaption><\/figure>\n\n\n\n\n<h2 class=\"wp-block-heading\">Schritt 1: Betroffenheit pr\u00fcfen, ohne Python zu starten<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Die Vorfallsseite nennt <code>pip show litellm<\/code> als Pr\u00fcfung auf die installierte Version. Dieselbe Seite warnt davor, mit <code>pip install<\/code> zu beginnen, weil jeder Python-Start die Schaddateien erneut ausf\u00fchrt. Daraus ergibt sich eine Schlussfolgerung, die nicht ausdr\u00fccklich in der Quelle steht: In einer m\u00f6glicherweise befallenen Umgebung sollte zuerst das Dateisystem gepr\u00fcft werden. Auch <code>pip show<\/code> ist ein Python-Prozess. Es eignet sich in einer sauberen Umgebung, etwa in einem frisch gestarteten Container ohne Befall, nicht aber als erster Schritt auf dem verd\u00e4chtigen Host.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Eine sinnvolle Reihenfolge f\u00fcr die Pr\u00fcfung:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Bestand erfassen: Hosts, Container, CI-Runner, Entwicklerrechner und Images, auf denen LiteLLM laufen oder gebaut worden sein k\u00f6nnte.<\/li>\n\n\n\n<li>Image-Digests und Tags in Registries und Deployments ansehen. Ein Digest ist der Hashwert eines Images und identifiziert es unver\u00e4nderlich, im Gegensatz zu einem Tag wie <code>latest<\/code>. LiteLLM nennt f\u00fcr den Docker-Hub-Image-Index einen Digest, der mit <code>sha256:1bbcb1443bc9<\/code> beginnt. Der vollst\u00e4ndige Wert steht auf der <a href=\"https:\/\/docs.litellm.ai\/blog\/security-incident-oct-21-2026\" target=\"_blank\" rel=\"noopener\">Vorfallsseite<\/a>. F\u00fcr die anderen GHCR-Images waren die Werte dort gek\u00fcrzt.<\/li>\n\n\n\n<li>Das Dateisystem durchsuchen, ohne Python zu starten.<\/li>\n\n\n\n<li>Laufende Prozesse und die Lock-Datei pr\u00fcfen.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr Schritt 3 und 4 reichen Standardwerkzeuge von Linux:<\/p>\n\n\n\n<pre><code>find \/ -xdev \\( -name 'cook.pth' -o -name 'cook.py' -o -name 'litellm-1.107.0.dist-info' \\) 2&gt;\/dev\/null\nfind \/ -xdev -name 'sitecustomize.py' 2&gt;\/dev\/null\nls -l \/tmp\/remote_code_loader.lock\nfuser -v \/tmp\/remote_code_loader.lock\ndocker images --digests | grep -i litellm<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Zu den Indikatoren z\u00e4hlen laut Vorfallsseite die Verzeichnisse <code>litellm-1.107.0.dist-info<\/code>, die Dateien <code>cook.pth<\/code>, <code>cook.py<\/code> und <code>sitecustomize.py<\/code> in <code>site-packages<\/code> sowie passende kompilierte Dateien im Ordner <code>__pycache__<\/code>. Wer gezielt nach LiteLLM cook.pth sucht, findet in den Analysen von Wiz und JFrog zus\u00e4tzlich Hashwerte der Dateien. Diese werden hier nicht abgedruckt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dabei gilt: Ein leerer Befund beweist nichts. Container k\u00f6nnen kurzlebig sein, Aufr\u00e4umjobs leeren <code>\/tmp<\/code>, und Spuren lassen sich entfernen. Laut Vorfallsseite best\u00e4tigt der Fund einer Marker-Datei die Exfiltration, ihr Fehlen beweist aber keine Sauberkeit. Wer im Zeitfenster installiert oder gestartet hat, sollte deshalb von einem Befall ausgehen, auch wenn die Suche nichts findet.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Schritt 2 bis 4: Isolieren, bereinigen, neu aufbauen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Beim Isolieren (Schritt 2) geht es darum, den Schaden zu begrenzen. Betroffene Systeme werden vom Netz getrennt oder zumindest am ausgehenden Verkehr (Egress) gehindert. Die Domain <code>litellm[.]dev<\/code> wird blockiert. Vor jeder \u00c4nderung sind Logs zu sichern: Proxy-Logs, DNS-Logs und Audit-Protokolle der Cloud. Sie werden sp\u00e4ter f\u00fcr die Spurensuche gebraucht.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr die Bereinigung (Schritt 3) nennt die Vorfallsseite diese Ma\u00dfnahmen: <code>cook.pth<\/code>, <code>cook.py<\/code> und <code>sitecustomize.py<\/code> samt zugeh\u00f6riger Dateien in <code>__pycache__<\/code> werden aus jedem betroffenen <code>site-packages<\/code>-Verzeichnis entfernt. Der losgel\u00f6ste Python-Prozess, der die Lock-Datei h\u00e4lt, wird beendet. JFrog erg\u00e4nzt, dass auch die Marker-Dateien unter <code>\/tmp<\/code> und Prozesse mit Arbeitsverzeichnis <code>\/<\/code> zu pr\u00fcfen sind. Das L\u00f6schen geschieht mit Werkzeugen, die Python nicht starten.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Anschlie\u00dfend wird auf <code>litellm==1.106.2<\/code> oder eine ausdr\u00fccklich vor dem Vorfall ver\u00f6ffentlichte Version zur\u00fcckgegangen. LiteLLM und JFrog empfehlen aber, Images und virtuelle Umgebungen nicht zu flicken, sondern neu aufzubauen. Der Grund ist einfach: Es ist schwer zu beweisen, dass wirklich jede Start-Datei und jede nachgeladene Stufe entfernt wurde. Der Neuaufbau (Schritt 4) aus verifizierten Quellen umgeht diese Unsicherheit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Daraus folgt auch die Antwort auf eine naheliegende Frage:<\/p>\n\n\n    <div itemscope itemtype=\"http:\/\/schema.org\/FAQPage\">\n                    <div itemprop=\"mainEntity\" itemscope itemtype=\"http:\/\/schema.org\/Question\">\n                <h4 itemprop=\"name\">Reicht es, LiteLLM einfach auf 1.106.2 zur\u00fcckzusetzen?<\/h4>\n                <div itemscope itemprop=\"acceptedAnswer\" itemtype=\"http:\/\/schema.org\/Answer\">\n                    <div itemprop=\"text\">Nein. Ein Downgrade ersetzt nur das Paket. Die Schadcode-Dateien in site-packages und ein laufender Hintergrundprozess k\u00f6nnen bestehen bleiben, und bereits gestohlene Zugangsdaten bleiben nutzbar. Deshalb gilt: Umgebung neu aufbauen und alles rotieren, was der Host erreichen konnte.<\/div>\n                <\/div>\n            <\/div>\n\n                <\/div><!--\/ppa questions-->\n    \n\n\n\n<p class=\"wp-block-paragraph\">Lief die Version 1.107.0 auf einem Host mit Internetzugang, ist laut Projekt davon auszugehen, dass Daten abgeflossen sind. Der Neuaufbau betrifft deshalb nicht nur die Anwendung, sondern auch den Host, wenn dort Zugangsdaten lagen. Auf Entwicklerrechnern gilt das ebenso wie auf Servern.<\/p>\n\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"896\" src=\"https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/reihenfolge-dbb584bf-1024x896.webp\" alt=\"Sechs Schritte im Ernstfall von der Pr\u00fcfung ohne Python-Start bis zur Spurensuche mit Warnhinweis zu pip\" class=\"wp-image-5806\" srcset=\"https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/reihenfolge-dbb584bf-1024x896.webp 1024w, https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/reihenfolge-dbb584bf-300x263.webp 300w, https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/reihenfolge-dbb584bf-768x672.webp 768w, https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/reihenfolge-dbb584bf-1536x1344.webp 1536w, https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/reihenfolge-dbb584bf-640x560.webp 640w, https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/reihenfolge-dbb584bf.webp 1600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Empfohlene Reihenfolge im Ernstfall.<\/figcaption><\/figure>\n\n\n\n\n<h2 class=\"wp-block-heading\">Schritt 5 und 6: Zugangsdaten rotieren und Spuren suchen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Das Rotieren der Zugangsdaten, also das Ersetzen durch neue Werte und das Sperren der alten, ist der wichtigste und zugleich m\u00fchsamste Teil. Sinnvoll ist eine Reihenfolge nach Reichweite, beginnend mit dem, was am meisten Schaden anrichten kann:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Die IAM-Rolle der Instanz oder des Tasks. Eine IAM-Rolle ist die Identit\u00e4t, mit der ein Server in der Cloud handelt. Ihre Zugangsdaten stehen dem Prozess automatisch zur Verf\u00fcgung, deshalb kann der Stealer sie lesen.<\/li>\n\n\n\n<li>Alle Schl\u00fcssel der Modellanbieter in der LiteLLM-Proxy-Datenbank sowie Master-Key und Virtual Keys, also die vom Gateway ausgegebenen Schl\u00fcssel f\u00fcr Anwendungen.<\/li>\n\n\n\n<li>Das Passwort in der <code>DATABASE_URL<\/code>.<\/li>\n\n\n\n<li>Alles, was in der Umgebung der Prozesse auf dem Host stand: Werte aus <code>.env<\/code>-Dateien, Kubernetes- und Docker-Zugangsdaten, Git-, npm-, PyPI- und SSH-Schl\u00fcssel.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Erst danach lohnt die Spurensuche (Schritt 6). LiteLLM empfiehlt, in CloudTrail, dem Protokoll der AWS-Aktivit\u00e4ten, nach <code>GetCallerIdentity<\/code>-Aufrufen der Instanzrolle rund um den Installationszeitpunkt zu suchen. Danach ist zu pr\u00fcfen, ob die Zugangsdaten der Rolle von au\u00dferhalb der eigenen Infrastruktur verwendet wurden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wie schnell Missbrauch folgen kann, beschreibt Wiz. Rund viereinhalb Stunden nach dem Abfluss, zwischen 16:20 und 17:01 UTC, pr\u00fcften Angreifer gestohlene AWS-Rollenzug\u00e4nge mit <code>GetCallerIdentity<\/code> und fragten bei Amazon Bedrock verf\u00fcgbare Modelle ab. Dieses Muster hei\u00dft LLMjacking: Fremde Zug\u00e4nge zu KI-Diensten werden genutzt, damit die Rechnung beim Opfer landet. Daraus folgen konkrete Aufgaben:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Kosten und Nutzungsspitzen bei Modellanbietern und Cloud-Konten beobachten.<\/li>\n\n\n\n<li>Budget- und Rate-Limits setzen, falls noch nicht vorhanden.<\/li>\n\n\n\n<li>In der Verwaltung des eigenen Gateways nach unbekannten Benutzerkonten suchen. LiteLLM fand ein solches Konto im eigenen Deployment.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Wichtig ist die Ordnung: Zuerst Zugang sperren, dann Spuren lesen. Wer erst Logs auswertet und w\u00e4hrenddessen alte Schl\u00fcssel g\u00fcltig l\u00e4sst, gibt dem Angreifer Zeit.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Datenschutz und Meldung: Wann Art. 32 und 33 DSGVO ins Spiel kommen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Vorfall dieser Art ist nicht nur ein technisches, sondern kann auch ein datenschutzrechtliches Thema sein. Das gilt, wenn auf den betroffenen Hosts personenbezogene Daten lagen oder Zug\u00e4nge zu solchen Daten abgeflossen sein k\u00f6nnen. Das ist bei einem KI-Gateway schnell der Fall, weil Prompts, Antworten und Logs Namen, Kundendaten oder interne Dokumente enthalten k\u00f6nnen. Zur Protokollierung gibt es einen Beitrag \u00fcber <a href=\"https:\/\/dr-dsgvo.de\/protokollierung-von-ip-adressen-in-server-logs-erlaubt-oder-nicht\/\" target=\"_blank\" rel=\"noopener\">Server-Logs und IP-Adressen<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In diesem Fall verlangt <a href=\"https:\/\/eur-lex.europa.eu\/legal-content\/DE\/TXT\/HTML\/?uri=CELEX:32016R0679#art_32\" target=\"_blank\" rel=\"noopener\">Art. 32 DSGVO<\/a> geeignete technische und organisatorische Ma\u00dfnahmen f\u00fcr die Sicherheit der Verarbeitung. Kommt es zu einer Verletzung des Schutzes personenbezogener Daten, ist nach <a href=\"https:\/\/eur-lex.europa.eu\/legal-content\/DE\/TXT\/HTML\/?uri=CELEX:32016R0679#art_33\" target=\"_blank\" rel=\"noopener\">Art. 33 DSGVO<\/a> zu pr\u00fcfen, ob eine Meldung an die Aufsichtsbeh\u00f6rde erforderlich ist. Sie hat grunds\u00e4tzlich innerhalb von 72 Stunden zu erfolgen, nachdem die Verletzung bekannt wurde, es sei denn, sie f\u00fchrt voraussichtlich nicht zu einem Risiko f\u00fcr die Betroffenen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Praktisch hei\u00dft das: Fr\u00fch den Datenschutzbeauftragten einbinden, den Zeitpunkt der Kenntnisnahme festhalten und dokumentieren, welche Daten auf den Hosts lagen und welche Zug\u00e4nge abgeflossen sein k\u00f6nnen. Das ist ein allgemeiner Hinweis und keine Rechtsberatung. Im Einzelfall entscheiden Datenschutzbeauftragte oder Fachleute.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">KI-Gateway absichern: Lehren \u00fcber LiteLLM hinaus<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Ein KI-Gateway absichern hei\u00dft, es wie das zu behandeln, was es ist: ein Konzentrationspunkt f\u00fcr Schl\u00fcssel und Inhalte. Die folgenden Ma\u00dfnahmen sind nicht auf LiteLLM beschr\u00e4nkt. Sie gelten f\u00fcr jedes Gateway, jeden Proxy und jede Komponente, die viele Geheimnisse ber\u00fchrt.<\/p>\n\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"666\" src=\"https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/gateway-4824b892-1024x666.webp\" alt=\"Schema eines KI-Gateways zwischen Anwendungen, Modellanbietern und Logs mit vier markierten Schutzma\u00dfnahmen\" class=\"wp-image-5807\" srcset=\"https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/gateway-4824b892-1024x666.webp 1024w, https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/gateway-4824b892-300x195.webp 300w, https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/gateway-4824b892-768x499.webp 768w, https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/gateway-4824b892-1536x998.webp 1536w, https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/gateway-4824b892-640x416.webp 640w, https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/gateway-4824b892.webp 1600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Wo Schutzma\u00dfnahmen am KI-Gateway ansetzen.<\/figcaption><\/figure>\n\n\n\n\n<h3 class=\"wp-block-heading\">Versionen festlegen und pr\u00fcfen<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Kein <code>latest<\/code> in Produktion. Python-Abh\u00e4ngigkeiten geh\u00f6ren in eine Lockdatei in der Versionsverwaltung. Der Installationsmodus mit Hash-Pr\u00fcfung (<code>pip install --require-hashes<\/code>) stellt sicher, dass nur Dateien mit bekanntem Hash installiert werden. Das ist in der <a href=\"https:\/\/pip.pypa.io\/en\/stable\/topics\/secure-installs\/\" target=\"_blank\" rel=\"noopener\">pip-Dokumentation<\/a> beschrieben. Bei Containern empfiehlt die <a href=\"https:\/\/docs.docker.com\/build\/building\/best-practices\/\" target=\"_blank\" rel=\"noopener\">Docker-Dokumentation<\/a>, Basisimages \u00fcber den Digest festzulegen (<code>FROM image:tag@sha256:...<\/code>). Der Preis: Automatische Updates entfallen. Werkzeuge wie Dependabot k\u00f6nnen Aktualisierungen als Vorschlag anbieten.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wichtig ist die Grenze dieser Ma\u00dfnahme. Hash-Pinning sch\u00fctzt vor nachtr\u00e4glichen \u00c4nderungen an einer festgelegten Version. Es sch\u00fctzt nicht, wenn die festgelegte Version selbst sch\u00e4dlich war, wie im Fall von 1.107.0. Deshalb kommt ein zweiter Schritt hinzu.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Updates erst testen, dann einf\u00fchren<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Neue Versionen laufen zun\u00e4chst in einer Testumgebung ohne Produktivgeheimnisse. Eine Karenzzeit f\u00fcr frische Releases, etwa einige Tage, ist eine naheliegende Praxis. Sie ist in den Quellen nicht belegt und wird hier als Empfehlung der Redaktion genannt. Im aktuellen Fall w\u00e4re eine Version, die nur wenige Stunden online war, damit nie in Produktion gelangt.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Protokollierung klein halten<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Prompts und Antworten nur dann protokollieren, wenn es einen Grund gibt. Wo es n\u00f6tig ist, werden Geheimnisse maskiert, der Zugriff auf Logs eng begrenzt und die Aufbewahrung zeitlich beschr\u00e4nkt. Der Fall zeigt, wie ein Log zur Quelle von Tokens werden kann.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Gateway nicht offen ins Netz stellen<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Interne Instanzen geh\u00f6ren hinter eine Anmeldung und nicht ins offene Internet. Sicherheitsupdates sind z\u00fcgig einzuspielen. Ob ein Dienst von au\u00dfen erreichbar ist, l\u00e4sst sich mit den <a href=\"https:\/\/wwwschutz.de\/security-checks.html\">Web Security-Checks <\/a> pr\u00fcfen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Geheimnisse kurzlebig und eng begrenzt halten<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">IAM-Rollen mit minimalen Rechten, getrennte Schl\u00fcssel je Anwendung, Budget- und Rate-Limits beim Anbieter. So begrenzt ein einzelner Abfluss den Schaden. Kurzlebige Zugangsdaten verlieren schneller ihren Wert.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Ausgehenden Verkehr beschr\u00e4nken<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Der Stealer sendet an eine fremde Domain. Ein Gateway, das nur zu den eigenen Modellanbietern verbinden darf, h\u00e4tte diesen Abfluss erschwert. Egress-Filter sind aufwendig, aber eine der wenigen Ma\u00dfnahmen, die auch bei unbekanntem Schadcode wirken.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Release-Prozess eigener Pakete<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Wer selbst Pakete oder Images ver\u00f6ffentlicht, sollte Branch-Schutz so einrichten, dass einzelne Konten ihn nicht aushebeln k\u00f6nnen. Freigaben werden getrennt, Berechtigungen zeitlich begrenzt. LiteLLM hatte Rechte am Montag zuvor tempor\u00e4r erweitert. Das zeigt, dass auch vor\u00fcbergehende Ausnahmen ein Risiko bleiben.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Selbst betriebene Modelle<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Wer Modelle selbst betreibt, braucht weniger fremde Schl\u00fcssel. Das ersetzt die genannten Ma\u00dfnahmen nicht, verkleinert aber die Angriffsfl\u00e4che. Einen \u00dcberblick bietet der Beitrag \u00fcber <a href=\"https:\/\/dr-dsgvo.de\/lokale-ki\/\" target=\"_blank\" rel=\"noopener\">lokale KI<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Was nach dem Vorfall offen bleibt<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Mehrere Punkte sind noch nicht gekl\u00e4rt. Der Erstzugang ist unbest\u00e4tigt, die Vorfallsseite vermutet eine k\u00fcrzlich gepatchte Schwachstelle. Wie viele Systeme tats\u00e4chlich betroffen sind und welche Daten abflossen, ist offen. F\u00fcr einen Zusammenhang mit dem M\u00e4rz-Vorfall gibt es laut Socket, zitiert von <a href=\"https:\/\/thehackernews.com\/2026\/10\/litellm-suspects-github-token-leak-in.html\" target=\"_blank\" rel=\"noopener\">The Hacker News<\/a>, keinen Beleg. Auch die Zeitangaben der Quellen weichen leicht ab: Wiz nennt f\u00fcr PyPI die Quarant\u00e4ne um 13:20 UTC, LiteLLM die Entfernung um 13:23 UTC. In diesem Beitrag gelten die Zahlen von LiteLLM.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das Projekt hat weitere Updates und einen Pr\u00fcfleitfaden angek\u00fcndigt. Betreiber sollten die Vorfallsseite im Blick behalten und Angaben mit dem jeweils aktuellen Stand abgleichen. F\u00fcr den 27. Oktober ist die Offenlegung einer weiteren Schwachstelle geplant, ein Bezug zum Vorfall ist nicht belegt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Fazit: Der LiteLLM Supply-Chain-Angriff zeigt, dass ein KI-Gateway ein Konzentrationspunkt f\u00fcr Schl\u00fcssel und Inhalte ist. Wer 1.107.0 betrieben hat, muss nicht nur die Version ersetzen, sondern alles rotieren, was der Host sehen konnte. Und wer ein Gateway betreibt, sollte Versionen festlegen, Logs sparsam halten und den ausgehenden Verkehr begrenzen, bevor der n\u00e4chste Vorfall kommt.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Am 21. Oktober 2026 lieferte die Release-Pipeline von LiteLLM Schadcode aus. Beschrieben werden Betroffenheit, Pr\u00fcfung ohne Python-Start, Bereinigung, Rotation der Zugangsdaten und dauerhafte Ma\u00dfnahmen f\u00fcr KI-Gateways.<\/p>\n","protected":false},"author":4,"featured_media":5803,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[74,90],"tags":[130,131,132,133,134],"class_list":["post-5808","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-kuenstliche-intelligenz","category-security","tag-it-sicherheit","tag-ki-gateway","tag-litellm","tag-supply-chain-angriff","tag-zugangsdaten","entry"],"blocks":"<!-- wp:paragraph -->\n<p><em>Am 21. Oktober 2026 lieferte die Release-Pipeline von LiteLLM Schadcode aus: Version 1.107.0 stahl Zugangsdaten und war darauf ausgelegt, weiteren Code nachzuladen. Beschrieben wird, wer betroffen sein kann, wie sich ein Befall pr\u00fcfen und bereinigen l\u00e4sst, ohne den Schadcode erneut zu starten, und was Betreiber von KI-Gateways dauerhaft \u00e4ndern sollten.<\/em><\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Ein KI-Gateway steht zwischen Anwendungen und Modellanbietern. Es b\u00fcndelt Schl\u00fcssel, leitet Anfragen weiter und protokolliert oft Prompts und Antworten. Ger\u00e4t ein solcher Baustein unter fremde Kontrolle, ist der Schaden gr\u00f6\u00dfer als bei einer gew\u00f6hnlichen Bibliothek. Der LiteLLM Supply-Chain-Angriff vom 21. Oktober 2026 zeigt das: In wenigen Stunden gelangte Schadcode auf Server, Container und Entwicklerrechner. Wer LiteLLM kompromittiert vorfindet oder dies vermutet, braucht eine klare Reihenfolge statt Aktionismus.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Stand der Quellen: 22.10.2026. Das Projekt hat weitere Updates angek\u00fcndigt, einzelne Angaben k\u00f6nnen sich daher \u00e4ndern.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Zur Abgrenzung: Im M\u00e4rz 2026 gab es bereits einen fr\u00fcheren Vorfall mit anderen Versionen (1.82.7 und 1.82.8). Hier geht es ausschlie\u00dflich um den Oktober-Vorfall und die Version 1.107.0. Die Angaben st\u00fctzen sich auf die <a href=\"https:\/\/docs.litellm.ai\/blog\/security-incident-oct-21-2026\">Vorfallsseite von LiteLLM<\/a> und die technischen Analysen von <a href=\"https:\/\/www.wiz.io\/blog\/litellm-compromised-steals-credentials\">Wiz<\/a> und <a href=\"https:\/\/research.jfrog.com\/post\/litellm-hijacked-again\/\">JFrog<\/a>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">Was am 21. Oktober 2026 passiert ist<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Laut LiteLLM wurde die Release-Pipeline des Projekts kompromittiert. Dabei entstand eine Version 1.107.0, die nicht dem ver\u00f6ffentlichten Quelltext entsprach. Sie landete auf drei Wegen bei Nutzern:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul class=\"wp-block-list\"><!-- wp:list-item -->\n<li>als Paket <code>litellm<\/code> auf PyPI, dem zentralen Paketverzeichnis f\u00fcr Python,<\/li>\n<!-- \/wp:list-item -->\n\n<!-- wp:list-item -->\n<li>als Container-Image auf Docker Hub,<\/li>\n<!-- \/wp:list-item -->\n\n<!-- wp:list-item -->\n<li>als Container-Image in der GitHub Container Registry (GHCR).<\/li>\n<!-- \/wp:list-item --><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>Bei den Images waren die Tags <code>v1.107.0<\/code>, <code>latest<\/code>, <code>main-latest<\/code> und <code>main-stable<\/code> betroffen. Besonders t\u00fcckisch: Der offizielle Quelltext-Tag <code>v1.107.0<\/code> auf GitHub enth\u00e4lt laut Projekt keine Schaddateien. Die Artefakte stammten aus einem vom Angreifer kontrollierten Doppelg\u00e4nger-Repository. Wer den Quelltext pr\u00fcft, sieht also nichts Auff\u00e4lliges. Enterprise-Kunden, die die private Registry des Projekts nutzen, waren laut LiteLLM nicht betroffen.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Alle Zeiten stammen aus der Vorfallsseite und gelten f\u00fcr den 21. Oktober 2026 in UTC. Zur Umrechnung: In Deutschland gilt noch die Sommerzeit (UTC plus zwei Stunden), 11:39 UTC entspricht also 13:39 Uhr.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:table {\"hasFixedLayout\":false} -->\n<figure class=\"wp-block-table\"><table><thead><tr><th>Kanal<\/th><th>Betroffen<\/th><th>Zeitfenster (UTC)<\/th><\/tr><\/thead><tbody><tr><td>PyPI<\/td><td>litellm 1.107.0 (sieben Wheels, so hei\u00dfen vorgebaute Pakete)<\/td><td>11:39 bis 13:23<\/td><\/tr><tr><td>Docker Hub<\/td><td>Tags v1.107.0, latest, main-latest, main-stable<\/td><td>11:46 bis 14:20<\/td><\/tr><tr><td>GHCR<\/td><td>gleiche Tags, unter anderem litellm, litellm-database, litellm-non_root<\/td><td>ab 11:46, sp\u00e4testens bis 13:51<\/td><\/tr><\/tbody><\/table><\/figure>\n<!-- \/wp:table -->\n\n<!-- wp:html -->\n<!-- wp:image {\"id\":5804,\"sizeSlug\":\"large\",\"linkDestination\":\"none\"} -->\n<figure class=\"wp-block-image size-large\"><img src=\"https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/zeitleiste-c6371129-1024x563.webp\" alt=\"Zeitfenster der Schadversion beim LiteLLM Supply-Chain-Angriff auf PyPI, Docker Hub und GHCR am 21. Oktober 2026 in UTC\" class=\"wp-image-5804\"\/><figcaption class=\"wp-element-caption\">Zeitfenster der Schadversion 1.107.0 je Kanal (UTC, 21. Oktober 2026).<\/figcaption><\/figure>\n<!-- \/wp:image -->\n<!-- \/wp:html -->\n\n<!-- wp:paragraph -->\n<p>Die Zeitleiste der Vorfallsseite beginnt fr\u00fcher. Um 10:08 UTC wurde eine \u00c4nderung am Release-Workflow vorgenommen, um 11:08 lief die Produktions-Pipeline an, und gegen 11:40 erschienen die sieben Wheels auf PyPI. Um 13:23 UTC entfernte PyPI die Version. Die GHCR-Tags waren um 13:51 UTC nicht mehr verf\u00fcgbar, die Docker-Hub-Tags zeigten noch l\u00e4nger auf das kompromittierte Image. Erst um 14:20 UTC waren alle betroffenen Tags entfernt oder auf gepr\u00fcfte Artefakte umgestellt. Die Vorfallsseite nennt in ihrer \u00dcbersicht au\u00dferdem eine abweichende Uhrzeit f\u00fcr den Beginn. Diese Abweichung wird hier nicht aufgel\u00f6st, die Beitragsangaben folgen der UTC-Zeitleiste.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Als Meldestelle im Repository dient das <a href=\"https:\/\/github.com\/BerriAI\/litellm\/issues\/47826\">Issue 47826<\/a>. Details aus dem Issue werden hier nicht verwendet.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">Wer vom LiteLLM Supply-Chain-Angriff betroffen sein kann<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Ein Supply-Chain-Angriff, auf Deutsch Lieferkettenangriff, trifft nicht das Ziel selbst, sondern einen Baustein, dem das Ziel vertraut. Hier war es ein Softwarepaket, das automatisch bezogen wird. Betroffen sein k\u00f6nnen daher alle, die im genannten Zeitfenster Artefakte aus den drei Kan\u00e4len bezogen oder gestartet haben:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul class=\"wp-block-list\"><!-- wp:list-item -->\n<li>Installationen mit <code>pip install litellm<\/code> ohne feste Version, die im Zeitfenster liefen und 1.107.0 zogen.<\/li>\n<!-- \/wp:list-item -->\n\n<!-- wp:list-item -->\n<li>Installationen, die ausdr\u00fccklich auf <code>litellm==1.107.0<\/code> festgelegt waren. Auch das feste Pinning, also das Festlegen einer genauen Version, sch\u00fctzte hier nicht.<\/li>\n<!-- \/wp:list-item -->\n\n<!-- wp:list-item -->\n<li>Docker-Starts oder Neubauten von Images mit den genannten Tags im Zeitfenster, auch in CI-Pipelines und auf Entwicklerrechnern.<\/li>\n<!-- \/wp:list-item -->\n\n<!-- wp:list-item -->\n<li>Images, die intern auf Basis eines dieser Tags gebaut und anschlie\u00dfend weiterverwendet wurden.<\/li>\n<!-- \/wp:list-item --><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>Nicht betroffen sind laut Projekt Versionen bis einschlie\u00dflich 1.106.2. Als R\u00fcckfallstand empfiehlt LiteLLM, auf 1.106.2 oder eine ausdr\u00fccklich vor dem Vorfall ver\u00f6ffentlichte Version festzulegen.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Ein weiterer Punkt ist die Frage der Abh\u00e4ngigkeiten. Viele Werkzeuge ziehen LiteLLM als Unterpaket nach. Beim fr\u00fcheren Vorfall im M\u00e4rz wurde das laut <a href=\"https:\/\/docs.litellm.ai\/blog\/security-update-march-2026\">LiteLLM<\/a> belegt. F\u00fcr den Oktober liegt es nahe, ist aber nicht einzeln best\u00e4tigt. Deshalb lohnt sich ein Blick in Lockfiles und Abh\u00e4ngigkeitsb\u00e4ume, nicht nur in die direkt installierten Pakete.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Ein Unterschied zum M\u00e4rz-Vorfall ist wichtig. Damals galt laut der <a href=\"https:\/\/docs.litellm.ai\/blog\/security-update-march-2026\">Meldung zum M\u00e4rz-Vorfall<\/a> das offizielle Proxy-Image als nicht betroffen. Diesmal waren Images betroffen. Wer sich auf die fr\u00fchere Einsch\u00e4tzung verl\u00e4sst, k\u00f6nnte den Container-Betrieb zu Unrecht f\u00fcr sicher halten.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Die Reichweite l\u00e4sst sich nicht verl\u00e4sslich beziffern. Wiz nennt rund 49.000 Downloads laut einer Statistik-Webseite, doch wie viele davon tats\u00e4chlich zum Einsatz kamen, ist offen. Entscheidend ist ohnehin der eigene Bestand: Ein einziger betroffener Host mit Zugriff auf Cloud-Rollen gen\u00fcgt f\u00fcr den Ernstfall.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">Was der Schadcode tut: Start bei jedem Python-Aufruf<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Laut JFrog legt das Paket drei Dateien im Verzeichnis <code>site-packages<\/code> ab, in das Python seine Pakete installiert: <code>cook.pth<\/code>, <code>cook.py<\/code> und <code>sitecustomize.py<\/code>. Der Mechanismus ist unscheinbar und wirksam.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul class=\"wp-block-list\"><!-- wp:list-item -->\n<li>Eine <code>.pth<\/code>-Datei ist eigentlich daf\u00fcr gedacht, Suchpfade f\u00fcr Python zu erg\u00e4nzen. Zeilen, die mit <code>import<\/code> beginnen, werden aber beim Start des Interpreters ausgef\u00fchrt. Die Datei <code>cook.pth<\/code> importiert so <code>cook.py<\/code>.<\/li>\n<!-- \/wp:list-item -->\n\n<!-- wp:list-item -->\n<li><code>sitecustomize.py<\/code> ist ein Hook, den Python beim Start automatisch l\u00e4dt, sofern die Datei existiert. Nicht jede solche Datei ist b\u00f6sartig, sie kommt gelegentlich legitim vor. Unerwartete Fundstellen sind aber zu pr\u00fcfen.<\/li>\n<!-- \/wp:list-item --><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>Die Folge: Der Code l\u00e4uft bei jedem Start von Python, auch wenn die Anwendung <code>litellm<\/code> nie importiert. Auch ein Aufruf von <code>pip<\/code> startet Python und kann den Schadcode ausl\u00f6sen. Genau deshalb st\u00fctzen sich die Pr\u00fcfschritte weiter unten zuerst auf das Dateisystem.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Gesammelt wird laut JFrog und Wiz sehr viel. Dazu z\u00e4hlen Zugangsdaten f\u00fcr Cloud-Dienste (AWS, Azure, Google Cloud), Kubernetes- und Docker-Konfigurationen, SSH- und GPG-Schl\u00fcssel, Zugangsdaten f\u00fcr Git, npm und PyPI, Umgebungsvariablen und <code>.env<\/code>-Dateien, Browserdaten sowie Zugangsdaten von KI-Werkzeugen. Auf Systemen mit einer LiteLLM-Proxy-Datenbank liest der Code laut beiden Analysen die dort gespeicherten Schl\u00fcssel der Modellanbieter aus.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Die Daten werden verschl\u00fcsselt und per POST an eine nachgebaute Domain gesendet, die dem Original \u00e4hnelt. Sie wird hier als <code>litellm[.]dev<\/code> entsch\u00e4rft geschrieben und sollte im Netz gesperrt werden. Ohne den privaten Schl\u00fcssel des Angreifers l\u00e4sst sich mitgeschnittener Verkehr nicht entschl\u00fcsseln. Das hilft den Betroffenen aber nicht, denn die Daten sind abgeflossen, sobald sie den Host verlassen haben.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 class=\"wp-block-heading\">Der Nachlade-Loader<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Zus\u00e4tzlich enth\u00e4lt <code>sitecustomize.py<\/code> laut JFrog einen Loader. Er startet auf Linux und macOS einen losgel\u00f6sten Hintergrundprozess, der eine Lock-Datei unter <code>\/tmp\/remote_code_loader.lock<\/code> h\u00e4lt. Der Prozess ist darauf ausgelegt, st\u00fcndlich eine Adresse aus einer \u00f6ffentlichen GitHub-Datei zu lesen, den sogenannten Dead Drop. Das ist ein toter Briefkasten: Der Angreifer kann dort jederzeit eine Adresse hinterlegen, ohne selbst direkt aufzutreten. Dann w\u00fcrde der Loader weiteren Code von dort holen und ausf\u00fchren.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Zum Analysezeitpunkt war diese Datei nach JFrog leer, laut Wiz nur mit Testwerten belegt. Das Repository wurde von GitHub gegen 13:30 UTC entfernt. Ein Nachladen wurde nicht beobachtet. Ob es dazwischen Nachladungen gab, ist nicht belegt. Der Loader bleibt dennoch ein m\u00f6glicher Fernzugriffsweg und geh\u00f6rt zur Bereinigung. Auf Windows scheitert der Hintergrundteil laut JFrog, weil er auf eine Funktion angewiesen ist, die dort fehlt. Der Stealer in <code>cook.py<\/code> l\u00e4uft aber auch dort.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">Wie der Angriff m\u00f6glich wurde: Tokens in Logs<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Der Kern der Lehre liegt nicht im Schadcode, sondern im Weg, auf dem er in die Release-Pipeline gelangte. Wiedergegeben wird hier die Darstellung von LiteLLM, erg\u00e4nzt um den Hinweis, dass der Erstzugang unbest\u00e4tigt ist.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Der Angreifer verf\u00fcgte laut Projekt \u00fcber GitHub-CLI-Zugangsdaten von drei Entwicklerkonten. Eine davon hatte Berechtigungen f\u00fcr Workflows, ein weiteres Token geh\u00f6rte einem Organisations-Owner. Damit wurden Branch-Schutzregeln deaktiviert und der Release-Workflow so ge\u00e4ndert, dass er ein Doppelg\u00e4nger-Repository nutzte. Die dort gebauten Artefakte wurden anschlie\u00dfend signiert und ver\u00f6ffentlicht. Au\u00dferdem wurden laut Projekt Workflow-Protokolle gel\u00f6scht, was die Analyse erschwert. Am Montag davor waren Release-Rechte f\u00fcr zwei interne Ingenieure vor\u00fcbergehend erweitert worden.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Woher stammten die Tokens? Das Projekt vermutet: aus Logs einer \u00f6ffentlich erreichbaren internen LiteLLM-Instanz. Laut LiteLLM kann Claude Code das OAuth-Token der GitHub-CLI in einen Antworttext einbetten. Wird der Antwortk\u00f6rper protokolliert, landet das Token im Log. Das ist eine Aussage des Projekts mit den Worten \u201ekann\u201c und \u201evermutet\u201c. Sie ist kein belegter Fehler eines Herstellers und wird hier nicht als solcher dargestellt. Als Ausgangspunkt nennt die Vorfallsseite ein unbekanntes, unauff\u00e4llig benanntes Konto auf dieser Instanz. M\u00f6glicherweise f\u00fchrte eine k\u00fcrzlich geschlossene Sicherheitsl\u00fccke dorthin, best\u00e4tigt ist das nicht.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:pullquote -->\n<figure class=\"wp-block-pullquote\"><blockquote><p>Ein KI-Gateway sieht, was Anwendungen und Agenten anfragen und antworten. Landet das im Log, wird das Log zum Schl\u00fcsselbund.<\/p><\/blockquote><\/figure>\n<!-- \/wp:pullquote -->\n\n<!-- wp:paragraph -->\n<p>Diese Beobachtung gilt weit \u00fcber LiteLLM hinaus. Wer KI-Agenten oder Coding-Werkzeuge mit weitreichendem Zugriff einsetzt, sollte wissen, welche Geheimnisse in Eingaben und Antworten auftauchen k\u00f6nnen. Zur Kontrolle solcher Agenten gibt es einen Beitrag \u00fcber <a href=\"https:\/\/wwwschutz.de\/blog\/ki-agentensysteme-und-datenschutz\/\" data-type=\"post\" data-id=\"4310\">KI-Agentensysteme und Datenschutz<\/a>. Auch die den Risiken von Entwicklerwerkzeugen mit viel Handlungsspielraum \u00fcber Agentic Coding sind zu nennen.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Bemerkenswert ist auch die Rolle der Signatur. Die ver\u00f6ffentlichten Artefakte waren signiert. Das beweist nur, dass der Workflow lief, nicht, dass der Code der erwartete war. Eine Signatur ist so vertrauensw\u00fcrdig wie die Pipeline, die sie erzeugt. Die Vorfallsseite betont zudem, dass der Angreifer mehrere voneinander unabh\u00e4ngige Schutzma\u00dfnahmen \u00fcberwinden musste.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:html -->\n<!-- wp:image {\"id\":5805,\"sizeSlug\":\"large\",\"linkDestination\":\"none\"} -->\n<figure class=\"wp-block-image size-large\"><img src=\"https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/angriffsweg-6d64c163-1024x819.webp\" alt=\"F\u00fcnf Stufen des Angriffswegs von Tokens in Logs bis zu signierten Schadartefakten\" class=\"wp-image-5805\"\/><figcaption class=\"wp-element-caption\">Angriffsweg laut Vorfallsseite von LiteLLM, der Erstzugang ist unbest\u00e4tigt.<\/figcaption><\/figure>\n<!-- \/wp:image -->\n<!-- \/wp:html -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">Schritt 1: Betroffenheit pr\u00fcfen, ohne Python zu starten<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Die Vorfallsseite nennt <code>pip show litellm<\/code> als Pr\u00fcfung auf die installierte Version. Dieselbe Seite warnt davor, mit <code>pip install<\/code> zu beginnen, weil jeder Python-Start die Schaddateien erneut ausf\u00fchrt. Daraus ergibt sich eine Schlussfolgerung, die nicht ausdr\u00fccklich in der Quelle steht: In einer m\u00f6glicherweise befallenen Umgebung sollte zuerst das Dateisystem gepr\u00fcft werden. Auch <code>pip show<\/code> ist ein Python-Prozess. Es eignet sich in einer sauberen Umgebung, etwa in einem frisch gestarteten Container ohne Befall, nicht aber als erster Schritt auf dem verd\u00e4chtigen Host.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Eine sinnvolle Reihenfolge f\u00fcr die Pr\u00fcfung:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol class=\"wp-block-list\"><!-- wp:list-item -->\n<li>Bestand erfassen: Hosts, Container, CI-Runner, Entwicklerrechner und Images, auf denen LiteLLM laufen oder gebaut worden sein k\u00f6nnte.<\/li>\n<!-- \/wp:list-item -->\n\n<!-- wp:list-item -->\n<li>Image-Digests und Tags in Registries und Deployments ansehen. Ein Digest ist der Hashwert eines Images und identifiziert es unver\u00e4nderlich, im Gegensatz zu einem Tag wie <code>latest<\/code>. LiteLLM nennt f\u00fcr den Docker-Hub-Image-Index einen Digest, der mit <code>sha256:1bbcb1443bc9<\/code> beginnt. Der vollst\u00e4ndige Wert steht auf der <a href=\"https:\/\/docs.litellm.ai\/blog\/security-incident-oct-21-2026\">Vorfallsseite<\/a>. F\u00fcr die anderen GHCR-Images waren die Werte dort gek\u00fcrzt.<\/li>\n<!-- \/wp:list-item -->\n\n<!-- wp:list-item -->\n<li>Das Dateisystem durchsuchen, ohne Python zu starten.<\/li>\n<!-- \/wp:list-item -->\n\n<!-- wp:list-item -->\n<li>Laufende Prozesse und die Lock-Datei pr\u00fcfen.<\/li>\n<!-- \/wp:list-item --><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>F\u00fcr Schritt 3 und 4 reichen Standardwerkzeuge von Linux:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:html -->\n<pre><code>find \/ -xdev \\( -name 'cook.pth' -o -name 'cook.py' -o -name 'litellm-1.107.0.dist-info' \\) 2&gt;\/dev\/null\nfind \/ -xdev -name 'sitecustomize.py' 2&gt;\/dev\/null\nls -l \/tmp\/remote_code_loader.lock\nfuser -v \/tmp\/remote_code_loader.lock\ndocker images --digests | grep -i litellm<\/code><\/pre>\n<!-- \/wp:html -->\n\n<!-- wp:paragraph -->\n<p>Zu den Indikatoren z\u00e4hlen laut Vorfallsseite die Verzeichnisse <code>litellm-1.107.0.dist-info<\/code>, die Dateien <code>cook.pth<\/code>, <code>cook.py<\/code> und <code>sitecustomize.py<\/code> in <code>site-packages<\/code> sowie passende kompilierte Dateien im Ordner <code>__pycache__<\/code>. Wer gezielt nach LiteLLM cook.pth sucht, findet in den Analysen von Wiz und JFrog zus\u00e4tzlich Hashwerte der Dateien. Diese werden hier nicht abgedruckt.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Dabei gilt: Ein leerer Befund beweist nichts. Container k\u00f6nnen kurzlebig sein, Aufr\u00e4umjobs leeren <code>\/tmp<\/code>, und Spuren lassen sich entfernen. Laut Vorfallsseite best\u00e4tigt der Fund einer Marker-Datei die Exfiltration, ihr Fehlen beweist aber keine Sauberkeit. Wer im Zeitfenster installiert oder gestartet hat, sollte deshalb von einem Befall ausgehen, auch wenn die Suche nichts findet.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">Schritt 2 bis 4: Isolieren, bereinigen, neu aufbauen<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Beim Isolieren (Schritt 2) geht es darum, den Schaden zu begrenzen. Betroffene Systeme werden vom Netz getrennt oder zumindest am ausgehenden Verkehr (Egress) gehindert. Die Domain <code>litellm[.]dev<\/code> wird blockiert. Vor jeder \u00c4nderung sind Logs zu sichern: Proxy-Logs, DNS-Logs und Audit-Protokolle der Cloud. Sie werden sp\u00e4ter f\u00fcr die Spurensuche gebraucht.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>F\u00fcr die Bereinigung (Schritt 3) nennt die Vorfallsseite diese Ma\u00dfnahmen: <code>cook.pth<\/code>, <code>cook.py<\/code> und <code>sitecustomize.py<\/code> samt zugeh\u00f6riger Dateien in <code>__pycache__<\/code> werden aus jedem betroffenen <code>site-packages<\/code>-Verzeichnis entfernt. Der losgel\u00f6ste Python-Prozess, der die Lock-Datei h\u00e4lt, wird beendet. JFrog erg\u00e4nzt, dass auch die Marker-Dateien unter <code>\/tmp<\/code> und Prozesse mit Arbeitsverzeichnis <code>\/<\/code> zu pr\u00fcfen sind. Das L\u00f6schen geschieht mit Werkzeugen, die Python nicht starten.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Anschlie\u00dfend wird auf <code>litellm==1.106.2<\/code> oder eine ausdr\u00fccklich vor dem Vorfall ver\u00f6ffentlichte Version zur\u00fcckgegangen. LiteLLM und JFrog empfehlen aber, Images und virtuelle Umgebungen nicht zu flicken, sondern neu aufzubauen. Der Grund ist einfach: Es ist schwer zu beweisen, dass wirklich jede Start-Datei und jede nachgeladene Stufe entfernt wurde. Der Neuaufbau (Schritt 4) aus verifizierten Quellen umgeht diese Unsicherheit.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Daraus folgt auch die Antwort auf eine naheliegende Frage:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:shortcode -->\n[faq]Reicht es, LiteLLM einfach auf 1.106.2 zur\u00fcckzusetzen?|=|Nein. Ein Downgrade ersetzt nur das Paket. Die Schadcode-Dateien in site-packages und ein laufender Hintergrundprozess k\u00f6nnen bestehen bleiben, und bereits gestohlene Zugangsdaten bleiben nutzbar. Deshalb gilt: Umgebung neu aufbauen und alles rotieren, was der Host erreichen konnte.[\/faq]\n<!-- \/wp:shortcode -->\n\n<!-- wp:paragraph -->\n<p>Lief die Version 1.107.0 auf einem Host mit Internetzugang, ist laut Projekt davon auszugehen, dass Daten abgeflossen sind. Der Neuaufbau betrifft deshalb nicht nur die Anwendung, sondern auch den Host, wenn dort Zugangsdaten lagen. Auf Entwicklerrechnern gilt das ebenso wie auf Servern.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:html -->\n<!-- wp:image {\"id\":5806,\"sizeSlug\":\"large\",\"linkDestination\":\"none\"} -->\n<figure class=\"wp-block-image size-large\"><img src=\"https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/reihenfolge-dbb584bf-1024x896.webp\" alt=\"Sechs Schritte im Ernstfall von der Pr\u00fcfung ohne Python-Start bis zur Spurensuche mit Warnhinweis zu pip\" class=\"wp-image-5806\"\/><figcaption class=\"wp-element-caption\">Empfohlene Reihenfolge im Ernstfall.<\/figcaption><\/figure>\n<!-- \/wp:image -->\n<!-- \/wp:html -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">Schritt 5 und 6: Zugangsdaten rotieren und Spuren suchen<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Das Rotieren der Zugangsdaten, also das Ersetzen durch neue Werte und das Sperren der alten, ist der wichtigste und zugleich m\u00fchsamste Teil. Sinnvoll ist eine Reihenfolge nach Reichweite, beginnend mit dem, was am meisten Schaden anrichten kann:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list {\"ordered\":true} -->\n<ol class=\"wp-block-list\"><!-- wp:list-item -->\n<li>Die IAM-Rolle der Instanz oder des Tasks. Eine IAM-Rolle ist die Identit\u00e4t, mit der ein Server in der Cloud handelt. Ihre Zugangsdaten stehen dem Prozess automatisch zur Verf\u00fcgung, deshalb kann der Stealer sie lesen.<\/li>\n<!-- \/wp:list-item -->\n\n<!-- wp:list-item -->\n<li>Alle Schl\u00fcssel der Modellanbieter in der LiteLLM-Proxy-Datenbank sowie Master-Key und Virtual Keys, also die vom Gateway ausgegebenen Schl\u00fcssel f\u00fcr Anwendungen.<\/li>\n<!-- \/wp:list-item -->\n\n<!-- wp:list-item -->\n<li>Das Passwort in der <code>DATABASE_URL<\/code>.<\/li>\n<!-- \/wp:list-item -->\n\n<!-- wp:list-item -->\n<li>Alles, was in der Umgebung der Prozesse auf dem Host stand: Werte aus <code>.env<\/code>-Dateien, Kubernetes- und Docker-Zugangsdaten, Git-, npm-, PyPI- und SSH-Schl\u00fcssel.<\/li>\n<!-- \/wp:list-item --><\/ol>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>Erst danach lohnt die Spurensuche (Schritt 6). LiteLLM empfiehlt, in CloudTrail, dem Protokoll der AWS-Aktivit\u00e4ten, nach <code>GetCallerIdentity<\/code>-Aufrufen der Instanzrolle rund um den Installationszeitpunkt zu suchen. Danach ist zu pr\u00fcfen, ob die Zugangsdaten der Rolle von au\u00dferhalb der eigenen Infrastruktur verwendet wurden.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Wie schnell Missbrauch folgen kann, beschreibt Wiz. Rund viereinhalb Stunden nach dem Abfluss, zwischen 16:20 und 17:01 UTC, pr\u00fcften Angreifer gestohlene AWS-Rollenzug\u00e4nge mit <code>GetCallerIdentity<\/code> und fragten bei Amazon Bedrock verf\u00fcgbare Modelle ab. Dieses Muster hei\u00dft LLMjacking: Fremde Zug\u00e4nge zu KI-Diensten werden genutzt, damit die Rechnung beim Opfer landet. Daraus folgen konkrete Aufgaben:<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul class=\"wp-block-list\"><!-- wp:list-item -->\n<li>Kosten und Nutzungsspitzen bei Modellanbietern und Cloud-Konten beobachten.<\/li>\n<!-- \/wp:list-item -->\n\n<!-- wp:list-item -->\n<li>Budget- und Rate-Limits setzen, falls noch nicht vorhanden.<\/li>\n<!-- \/wp:list-item -->\n\n<!-- wp:list-item -->\n<li>In der Verwaltung des eigenen Gateways nach unbekannten Benutzerkonten suchen. LiteLLM fand ein solches Konto im eigenen Deployment.<\/li>\n<!-- \/wp:list-item --><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:paragraph -->\n<p>Wichtig ist die Ordnung: Zuerst Zugang sperren, dann Spuren lesen. Wer erst Logs auswertet und w\u00e4hrenddessen alte Schl\u00fcssel g\u00fcltig l\u00e4sst, gibt dem Angreifer Zeit.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">Datenschutz und Meldung: Wann Art. 32 und 33 DSGVO ins Spiel kommen<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Ein Vorfall dieser Art ist nicht nur ein technisches, sondern kann auch ein datenschutzrechtliches Thema sein. Das gilt, wenn auf den betroffenen Hosts personenbezogene Daten lagen oder Zug\u00e4nge zu solchen Daten abgeflossen sein k\u00f6nnen. Das ist bei einem KI-Gateway schnell der Fall, weil Prompts, Antworten und Logs Namen, Kundendaten oder interne Dokumente enthalten k\u00f6nnen. Zur Protokollierung gibt es einen Beitrag \u00fcber <a href=\"https:\/\/dr-dsgvo.de\/protokollierung-von-ip-adressen-in-server-logs-erlaubt-oder-nicht\/\">Server-Logs und IP-Adressen<\/a>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>In diesem Fall verlangt <a href=\"https:\/\/eur-lex.europa.eu\/legal-content\/DE\/TXT\/HTML\/?uri=CELEX:32016R0679#art_32\">Art. 32 DSGVO<\/a> geeignete technische und organisatorische Ma\u00dfnahmen f\u00fcr die Sicherheit der Verarbeitung. Kommt es zu einer Verletzung des Schutzes personenbezogener Daten, ist nach <a href=\"https:\/\/eur-lex.europa.eu\/legal-content\/DE\/TXT\/HTML\/?uri=CELEX:32016R0679#art_33\">Art. 33 DSGVO<\/a> zu pr\u00fcfen, ob eine Meldung an die Aufsichtsbeh\u00f6rde erforderlich ist. Sie hat grunds\u00e4tzlich innerhalb von 72 Stunden zu erfolgen, nachdem die Verletzung bekannt wurde, es sei denn, sie f\u00fchrt voraussichtlich nicht zu einem Risiko f\u00fcr die Betroffenen.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Praktisch hei\u00dft das: Fr\u00fch den Datenschutzbeauftragten einbinden, den Zeitpunkt der Kenntnisnahme festhalten und dokumentieren, welche Daten auf den Hosts lagen und welche Zug\u00e4nge abgeflossen sein k\u00f6nnen. Das ist ein allgemeiner Hinweis und keine Rechtsberatung. Im Einzelfall entscheiden Datenschutzbeauftragte oder Fachleute.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">KI-Gateway absichern: Lehren \u00fcber LiteLLM hinaus<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Ein KI-Gateway absichern hei\u00dft, es wie das zu behandeln, was es ist: ein Konzentrationspunkt f\u00fcr Schl\u00fcssel und Inhalte. Die folgenden Ma\u00dfnahmen sind nicht auf LiteLLM beschr\u00e4nkt. Sie gelten f\u00fcr jedes Gateway, jeden Proxy und jede Komponente, die viele Geheimnisse ber\u00fchrt.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:html -->\n<!-- wp:image {\"id\":5807,\"sizeSlug\":\"large\",\"linkDestination\":\"none\"} -->\n<figure class=\"wp-block-image size-large\"><img src=\"https:\/\/wwwschutz.de\/blog\/wp-content\/uploads\/gateway-4824b892-1024x666.webp\" alt=\"Schema eines KI-Gateways zwischen Anwendungen, Modellanbietern und Logs mit vier markierten Schutzma\u00dfnahmen\" class=\"wp-image-5807\"\/><figcaption class=\"wp-element-caption\">Wo Schutzma\u00dfnahmen am KI-Gateway ansetzen.<\/figcaption><\/figure>\n<!-- \/wp:image -->\n<!-- \/wp:html -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 class=\"wp-block-heading\">Versionen festlegen und pr\u00fcfen<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Kein <code>latest<\/code> in Produktion. Python-Abh\u00e4ngigkeiten geh\u00f6ren in eine Lockdatei in der Versionsverwaltung. Der Installationsmodus mit Hash-Pr\u00fcfung (<code>pip install --require-hashes<\/code>) stellt sicher, dass nur Dateien mit bekanntem Hash installiert werden. Das ist in der <a href=\"https:\/\/pip.pypa.io\/en\/stable\/topics\/secure-installs\/\">pip-Dokumentation<\/a> beschrieben. Bei Containern empfiehlt die <a href=\"https:\/\/docs.docker.com\/build\/building\/best-practices\/\">Docker-Dokumentation<\/a>, Basisimages \u00fcber den Digest festzulegen (<code>FROM image:tag@sha256:...<\/code>). Der Preis: Automatische Updates entfallen. Werkzeuge wie Dependabot k\u00f6nnen Aktualisierungen als Vorschlag anbieten.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Wichtig ist die Grenze dieser Ma\u00dfnahme. Hash-Pinning sch\u00fctzt vor nachtr\u00e4glichen \u00c4nderungen an einer festgelegten Version. Es sch\u00fctzt nicht, wenn die festgelegte Version selbst sch\u00e4dlich war, wie im Fall von 1.107.0. Deshalb kommt ein zweiter Schritt hinzu.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 class=\"wp-block-heading\">Updates erst testen, dann einf\u00fchren<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Neue Versionen laufen zun\u00e4chst in einer Testumgebung ohne Produktivgeheimnisse. Eine Karenzzeit f\u00fcr frische Releases, etwa einige Tage, ist eine naheliegende Praxis. Sie ist in den Quellen nicht belegt und wird hier als Empfehlung der Redaktion genannt. Im aktuellen Fall w\u00e4re eine Version, die nur wenige Stunden online war, damit nie in Produktion gelangt.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 class=\"wp-block-heading\">Protokollierung klein halten<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Prompts und Antworten nur dann protokollieren, wenn es einen Grund gibt. Wo es n\u00f6tig ist, werden Geheimnisse maskiert, der Zugriff auf Logs eng begrenzt und die Aufbewahrung zeitlich beschr\u00e4nkt. Der Fall zeigt, wie ein Log zur Quelle von Tokens werden kann.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 class=\"wp-block-heading\">Gateway nicht offen ins Netz stellen<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Interne Instanzen geh\u00f6ren hinter eine Anmeldung und nicht ins offene Internet. Sicherheitsupdates sind z\u00fcgig einzuspielen. Ob ein Dienst von au\u00dfen erreichbar ist, l\u00e4sst sich mit den <a href=\"https:\/\/wwwschutz.de\/security-checks.html\">Web Security-Checks <\/a> pr\u00fcfen.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 class=\"wp-block-heading\">Geheimnisse kurzlebig und eng begrenzt halten<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>IAM-Rollen mit minimalen Rechten, getrennte Schl\u00fcssel je Anwendung, Budget- und Rate-Limits beim Anbieter. So begrenzt ein einzelner Abfluss den Schaden. Kurzlebige Zugangsdaten verlieren schneller ihren Wert.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 class=\"wp-block-heading\">Ausgehenden Verkehr beschr\u00e4nken<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Der Stealer sendet an eine fremde Domain. Ein Gateway, das nur zu den eigenen Modellanbietern verbinden darf, h\u00e4tte diesen Abfluss erschwert. Egress-Filter sind aufwendig, aber eine der wenigen Ma\u00dfnahmen, die auch bei unbekanntem Schadcode wirken.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 class=\"wp-block-heading\">Release-Prozess eigener Pakete<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Wer selbst Pakete oder Images ver\u00f6ffentlicht, sollte Branch-Schutz so einrichten, dass einzelne Konten ihn nicht aushebeln k\u00f6nnen. Freigaben werden getrennt, Berechtigungen zeitlich begrenzt. LiteLLM hatte Rechte am Montag zuvor tempor\u00e4r erweitert. Das zeigt, dass auch vor\u00fcbergehende Ausnahmen ein Risiko bleiben.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":3} -->\n<h3 class=\"wp-block-heading\">Selbst betriebene Modelle<\/h3>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Wer Modelle selbst betreibt, braucht weniger fremde Schl\u00fcssel. Das ersetzt die genannten Ma\u00dfnahmen nicht, verkleinert aber die Angriffsfl\u00e4che. Einen \u00dcberblick bietet der Beitrag \u00fcber <a href=\"https:\/\/dr-dsgvo.de\/lokale-ki\/\">lokale KI<\/a>.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">Was nach dem Vorfall offen bleibt<\/h2>\n<!-- \/wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Mehrere Punkte sind noch nicht gekl\u00e4rt. Der Erstzugang ist unbest\u00e4tigt, die Vorfallsseite vermutet eine k\u00fcrzlich gepatchte Schwachstelle. Wie viele Systeme tats\u00e4chlich betroffen sind und welche Daten abflossen, ist offen. F\u00fcr einen Zusammenhang mit dem M\u00e4rz-Vorfall gibt es laut Socket, zitiert von <a href=\"https:\/\/thehackernews.com\/2026\/10\/litellm-suspects-github-token-leak-in.html\">The Hacker News<\/a>, keinen Beleg. Auch die Zeitangaben der Quellen weichen leicht ab: Wiz nennt f\u00fcr PyPI die Quarant\u00e4ne um 13:20 UTC, LiteLLM die Entfernung um 13:23 UTC. In diesem Beitrag gelten die Zahlen von LiteLLM.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Das Projekt hat weitere Updates und einen Pr\u00fcfleitfaden angek\u00fcndigt. Betreiber sollten die Vorfallsseite im Blick behalten und Angaben mit dem jeweils aktuellen Stand abgleichen. F\u00fcr den 27. Oktober ist die Offenlegung einer weiteren Schwachstelle geplant, ein Bezug zum Vorfall ist nicht belegt.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Fazit: Der LiteLLM Supply-Chain-Angriff zeigt, dass ein KI-Gateway ein Konzentrationspunkt f\u00fcr Schl\u00fcssel und Inhalte ist. Wer 1.107.0 betrieben hat, muss nicht nur die Version ersetzen, sondern alles rotieren, was der Host sehen konnte. Und wer ein Gateway betreibt, sollte Versionen festlegen, Logs sparsam halten und den ausgehenden Verkehr begrenzen, bevor der n\u00e4chste Vorfall kommt.<\/p>\n<!-- \/wp:paragraph -->","_links":{"self":[{"href":"https:\/\/wwwschutz.de\/blog\/wp-json\/wp\/v2\/posts\/5808","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wwwschutz.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wwwschutz.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wwwschutz.de\/blog\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/wwwschutz.de\/blog\/wp-json\/wp\/v2\/comments?post=5808"}],"version-history":[{"count":1,"href":"https:\/\/wwwschutz.de\/blog\/wp-json\/wp\/v2\/posts\/5808\/revisions"}],"predecessor-version":[{"id":5810,"href":"https:\/\/wwwschutz.de\/blog\/wp-json\/wp\/v2\/posts\/5808\/revisions\/5810"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/wwwschutz.de\/blog\/wp-json\/wp\/v2\/media\/5803"}],"wp:attachment":[{"href":"https:\/\/wwwschutz.de\/blog\/wp-json\/wp\/v2\/media?parent=5808"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wwwschutz.de\/blog\/wp-json\/wp\/v2\/categories?post=5808"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wwwschutz.de\/blog\/wp-json\/wp\/v2\/tags?post=5808"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}