Das Token des Agenten gehörte dem Owner
Am 23. Oktober hat LiteLLM die Ursache seines vergifteten Releases 1.107.0 veröffentlicht. Die erste gestohlene Zugangsberechtigung stammte nicht aus einem Log. Sie stammte von LiteLLMs eigener Agentenplattform, deren API das GitHub-Token zurückgab, mit dem die Cloud-Agenten Pull Requests öffneten: das Token eines Organisations-Owners, nicht eingeschränkt. Der Schlüssel zu dieser API lag viereinhalb Monate lang in einer öffentlichen Konfigurationsdatei. In Bromure Agentic Coding hält ein Agent, der Pull Requests öffnet, einen Platzhalter, das echte Token bleibt verschlüsselt auf dem Mac, und keine API gibt es heraus.
LiteLLMs Agenten brauchten ein GitHub-Token, um Pull Requests zu öffnen. Sie bekamen eines, das einem Owner der Organisation gehörte. Ein einziger API-Aufruf mit einem geleakten Schlüssel gab es zurück.
Am 23. Oktober hat LiteLLM die Angriffskette hinter seinem Release 1.107.0 veröffentlicht, der Version, die am 21. Oktober mit einem Credential-Stealer an Bord ausgeliefert wurde. Der Beitrag bezeichnet sich als „die maßgebliche Quelle der Wahrheit“ und korrigiert LiteLLMs erste Theorie. Die gestohlenen GitHub-Tokens stammten nicht aus protokolliertem Modellverkehr. Das erste stammte aus einem experimentellen Dienst, den LiteLLM für seine eigenen Coding-Agenten betrieb.
Über das Release selbst haben wir in Das Release war zwei Stunden alt berichtet und sind dort der ersten Theorie gefolgt. Der Abschlussbericht beginnt an einer anderen Stelle. Die erste Zugangsberechtigung, die der Angreifer an sich nahm, gehörte den Agenten.
Eine Agentenplattform mit dem Token eines Owners
LiteLLM Agent Platform war nach LiteLLMs Worten „ein experimentelles Projekt für selbst gehostete Cloud-Agenten, die PRs in GitHub-Repositories einreichen“. Um Pull Requests zu öffnen, hielt die Plattform ein GitHub-Token. Dieses Token „war keinem Service-Account zugeordnet und nicht eingeschränkt“. Es war das OAuth-Token der GitHub CLI eines der beiden Owner der Organisation.
Die Zeitleiste des Berichts, in UTC:
- 27. Mai, 18:53. Eine Datei
opencode.jsonwird in das öffentliche Repository LiteLLM-Labs/lite-harness committet. Sie ist die Konfiguration eines Agenten-Harness und enthält den Master-Key zu einem Deployment der Agent Platform. Das Team bemerkt es, überschreibt den Commit per Force-Push und markiert die offengelegten Schlüssel zum Widerruf. „Für den Master-Key fand der Widerruf nicht statt.“ - 10. Oktober, 21:27. Von 69.48.229.143 aus ruft der Angreifer mit
diesem Schlüssel die API der Plattform auf.
GET /api/v1/managed_agents/agents?limit=100liefert die Konfiguration jedes verwalteten Agenten und damit das Token des Owners. - 10. Oktober, 22:11. Mit dem Token des Owners pusht der Angreifer einen
Wegwerf-Branch,
ci-tmp-a351cc, nach BerriAI/litellm. Dessen Workflow schickt sechs Repository-Secrets an webhook.site. Eines davon,GH_TOKEN, ist das GitHub-CLI-Token eines zweiten Maintainers. Vierundzwanzig Sekunden später schickt ein zweiter Branch in BerriAI/litellm-auto das Token eines dritten Maintainers an denselben Endpunkt. Beide Läufe werden innerhalb von drei Minuten gelöscht. - 12. Oktober. Weitere Workflows in privaten Repositories schicken jedes Repository-Secret ab, das sie erreichen können.
- 21. Oktober, 10:08. Ein Push in das Release-Repository mit dem Token des zweiten Maintainers wird vom Branch-Schutz abgewiesen. Fünf Sekunden später schaltet das Token des Owners den Schutz ab und force-pusht die Änderung, die den Release-Workflow auf den Doppelgänger Berri-AI/litellm umleitet.
- 21. Oktober, 11:11. Das Token des zweiten Maintainers genehmigt das Produktions-Gate. Eine Regel, die die Freigabe durch ein an ein Gerät in LiteLLMs Büro gebundenes Konto verlangte, war in jener Woche gelockert worden, sodass zwei beliebige Ingenieure veröffentlichen konnten.
- 21. Oktober, 11:39 bis 11:46. Sieben Wheels landen auf PyPI, danach die Images auf Docker Hub und GHCR.
- 21. Oktober, 11:53. Das Token des dritten Maintainers löscht die Aufzeichnungen des Release-Laufs. Um 12:24 force-pusht das Token des Owners main wieder sauber.
LiteLLMs eigene Zusammenfassung: „Dieser Vorfall hat die Risiken eindrücklich veranschaulicht, die entstehen, wenn ein möglicherweise offengelegter Schlüssel nicht rotiert wird.“ Das Unternehmen widerruft nun jedes Token, das mit einem internen System oder einer Anbieterplattform verbunden ist.
Jede Agentenflotte hält so ein Token
LiteLLM hat ein fähiges Sicherheitsteam, und sein Aufbau war ein üblicher.
Ein Agent, der Pull Requests öffnet, braucht ein Token, das pushen darf.
Das am schnellsten greifbare Token ist der eigene gh-Login, und genau
den hatten LiteLLMs Agenten bekommen.
Dann muss das Token irgendwo liegen, wo die Agenten es lesen können: in einer Umgebungsvariable, einer Konfigurationsdatei, einer Datenbankzeile hinter einer API, die die Agenten konfiguriert. Wer diesen Ort lesen kann, hat das Token. Hier reichte ein API-Schlüssel, der einmal geleakt und nie rotiert worden war.
Tokens der GitHub CLI machen es schlimmer. LiteLLM merkt an, dass sie „nach der 2FA ausgestellt werden und nicht den ‚Super-User‘-2FA-Reauth-Abläufen von GitHub für sensible Organisations- oder Repository-Aktionen unterliegen“. Mit einem davon konnte der Angreifer den Branch-Schutz abschalten, force-pushen, ein Release genehmigen und die Spuren löschen.
Suchen Sie in Ihrem eigenen Aufbau nach denselben Teilen: ein GH_TOKEN
in der Umgebung des Agenten, eine MCP-Konfiguration mit einem Bearer-Token
darin, eine Harness-Konfiguration, die eines Tages jemand committen wird.
LiteLLMs opencode.json enthielt mehr als den Plattformschlüssel. Darin
standen auch Bearer-Tokens für die MCP-Server von GitHub, Linear und Slack
sowie der Schlüssel für das Modell-Gateway, alles im Klartext.
Dieselbe Flotte auf einer Bromure-Maschine
Bromure Agentic Coding betreibt Claude Code, Codex, Grok, Kimi und omp in Linux-VMs auf Ihrem Mac. Seine Automationen tun, was LiteLLMs Agentenplattform tat: Ein Agent startet nach Zeitplan oder bei einem neuen GitHub-Issue, arbeitet in einem frischen Worktree und pusht einen Branch oder öffnet einen Pull Request. Spielen Sie LiteLLMs Kette gegen eine Maschine durch, auf der diese Automationen laufen.
Der Agent hält einen Platzhalter
Fügen Sie das GitHub-Token der Maschine unter Credentials hinzu. Beim
Start schreibt Bromure eine Fälschung in die VM: einen ghp_-Wert der
richtigen Länge, in GH_TOKEN, in die Konfiguration von gh und in
~/.git-credentials. gh pr create und git push des Agenten
funktionieren wie gewohnt. Wenn eine Anfrage die VM in Richtung github.com
verlässt, tauscht der Proxy auf dem Mac die Fälschung gegen das echte
Token, und nur für diesen Host.
Das echte Token liegt verschlüsselt auf dem Mac, unter einem Schlüssel, der im macOS-Schlüsselbund verwahrt wird. Es wird nie in die VM geschrieben, weder in eine Datei noch in eine Variable. Das Handbuch sagt über den Kanal zwischen Host und VM: „Es gibt keine Anfrage, mit der die VM ein echtes Token zurückverlangen kann.“
Der Schritt, der LiteLLMs Organisation öffnete, hat also nichts
zurückzugeben. Lesen Sie die Umgebung des Agenten aus, seine
Konfiguration, listen Sie jede Automation über die API auf: Der
GitHub-Wert ist überall der Platzhalter. Fügen Sie ihn in Ihr eigenes gh
ein, und GitHub lehnt ihn ab, denn nur der Proxy dieser Maschine weiß,
wofür er steht.
Auch die committete Konfiguration hält Platzhalter
Das Leak begann mit einer Harness-Konfiguration in einem öffentlichen
Repository. Auf einer Bromure-Maschine werden die Konfigurationsdateien,
die ein Agent liest, mit Platzhaltern geschrieben. Das Bearer-Token eines
MCP-Servers, gesetzt im Bereich MCP der Maschine, erreicht den Agenten
als brm-mcp_…-Wert, der auf der Leitung für Anfragen an den Host dieses
Servers ersetzt wird. Ein Modellschlüssel aus Preferences › Models
gelangt nie in die VM; der Agent sieht eine Fälschung im Format des
Anbieters. Jeder andere API-Schlüssel kommt unter Other API keys mit
einem Host-Filter, und die VM erhält einen brm_…-Wert in der Variable,
die Sie benennen.
Committet ein Agent seine Konfiguration aus Versehen, oder kopiert ein Entwickler sie in ein Demo-Repository, enthält die Datei Platzhalter. Nichts darin funktioniert außerhalb dieses Macs, also entscheiden weder ein Force-Push noch eine Widerrufsliste, die niemand abarbeitet, darüber, ob Sie kompromittiert werden.
Es gibt keine Plattform-API zum Aufrufen
LiteLLMs Angreifer erreichte die API der Plattform aus dem Internet.
Bromures Automationen laufen auf Ihrem Mac. In den Worten des Handbuchs:
„Es gibt keinen Cloud-Dienst und keine eingehende Netzwerkangriffsfläche.“
GitHub- und Linear-Trigger werden alle drei Minuten vom Mac aus mit dem
gespeicherten Token der Maschine abgefragt, das auf dem Host bleibt. Es
gibt keinen Webhook-Endpunkt, keinen Master-Key und keine Route
/managed_agents, die ein geleakter Schlüssel öffnen könnte.
Ein gestohlener Platzhalter löst Alarm aus
Die Workflows vom 10. Oktober schickten Secrets an webhook.site. Stellen
Sie sich denselben Zug gegen eine Bromure-Maschine vor: Eine vergiftete
Abhängigkeit oder eine per Prompt Injection eingeschleuste Anweisung liest
GH_TOKEN und schickt es an webhook.site.
Der Proxy durchsucht jede ausgehende Anfrage, Header und Body, nach
Platzhaltern, die an einen anderen Host gehen als den, für den sie erzeugt
wurden. Ein ghp_-Platzhalter auf dem Weg zu webhook.site wird mit HTTP
451 blockiert, bevor ein einziges Byte hinausgeht. Die VM pausiert, und
eine Warnung auf dem Mac meldet This environment may have been
compromised, mit der Token-Vorschau, dem Namen der Zugangsberechtigung,
dem Host, zu dem sie gehört, und dem Host, an den sie geschickt wurde. Sie
können die Maschine herunterfahren oder ihre Festplatte und ihren
Home-Ordner für die Untersuchung sichern.
Steht Unmatched traffic in der Sandbox-Richtlinie der Maschine auf Deny, ist webhook.site gar nicht erst erreichbar, und der Versuch erscheint als rote Zeile in der Security Timeline.
Ein Push wartet auf Sie
Neue Bromure-Maschinen starten mit GitHubs Schreibrichtlinie auf Prompt
before write. Lesezugriffe gehen durch. Ein git push oder jeder andere
Schreibzugriff auf github.com hält an einem Dialog auf Ihrem Mac an:
Allow write on "GitHub" from workspace "release-bot"?
POST /repos/<owner>/<repo>/git/refs
Die Züge des Angreifers waren allesamt Schreibzugriffe: die
ci-tmp--Branches, die Änderung am Release-Workflow, der Force-Push auf
main, das Löschen der Laufaufzeichnungen. Von einer Bromure-Maschine aus
hätte jeder davon an diesem Dialog angehalten. Allow once erlaubt
nichts über diesen einen Schreibzugriff hinaus, der nächste Push fragt
also erneut. Ein Force-Push ist auf der Leitung immer noch ein Push, und
Prompt before write kontrolliert ihn.
Eine unbeaufsichtigte Automation kann nicht auf einen Klick warten, also
geben Sie ihr das engste Token, das ihre Aufgabe erfüllt, unter einer
Richtlinie, die für diese Maschine gilt. Das Token, das Sie einer
release-bot-Maschine geben, kann ein feingranulares für ein einziges
Repository sein. Ihr eigener gh-Login mit Owner-Rechten auf alles hat
auf dieser Maschine nichts zu suchen.
Die Aufzeichnung liegt auf Ihrem Mac
LiteLLM glich Token-Hashes im Audit-Log von GitHub ab, um herauszufinden, welches gestohlene Token was getan hatte. Auf einer Bromure-Maschine erscheint jeder Tausch einer Zugangsberechtigung auf der Leitung als Zeile Credential brokering in der Security Timeline, mit dem maskierten Token und dem Host. Jeder Schreibzugriff, den Sie ablehnen, ist eine rote Zeile Guardrails mit Methode und Pfad. Auf einem bei bromure.io registrierten Mac erreichen dieselben Ereignisse Ihre Organisation, sodass ein Token, das von einer unerwarteten Maschine aus benutzt wird, auffällt, ohne dass jemand Audit-Logs durchsuchen muss.
Agenten bekommen Platzhalter
GH_TOKEN, die Konfiguration von gh und ~/.git-credentials halten
eine ghp_-Fälschung. Das echte Token wird auf dem Mac eingesetzt, nur
für die Hosts von GitHub.
Keine API gibt Tokens heraus
Automationen laufen auf Ihrem Mac ohne eingehende Angriffsfläche. Nichts, was die VM aufrufen kann, liefert eine echte Zugangsberechtigung.
Leaks pausieren die Maschine
Ein Platzhalter, der irgendwohin außer zu seinem eigenen Host geht, wird mit einem 451 blockiert, und die VM pausiert, bis Sie entscheiden.
Schreibzugriffe fragen zuerst
Neue Maschinen fragen vor jedem Push und jedem Schreibzugriff auf GitHub nach, Force-Pushes eingeschlossen, mit der genauen Anfrage auf dem Bildschirm.
Geben Sie dem Agenten das Token gar nicht erst
LiteLLM widerruft jedes Token und will nächste Woche seine Prozessänderungen veröffentlichen. Rotation, enge Berechtigungen und Service-Accounts helfen alle. Jede dieser Maßnahmen lässt aber ein funktionierendes Token irgendwo liegen, wo ein Agent es lesen kann, und genau danach suchte der Angreifer am 10. Oktober.
Auf einer Bromure-Maschine erledigt der Agent seine Arbeit mit einem Token, das nur über seinen eigenen Proxy funktioniert, in Richtung des einen Hosts, für den es erzeugt wurde. Was der Agent lesen kann, kann auch ein Angreifer lesen, der den Agenten erreicht, und auf einer Bromure-Maschine gehört das echte Token nie dazu.
Wie dieselben Platzhalter gegen einen Stealer bestehen, der es auf die Dateien von Coding-Agenten abgesehen hat, lesen Sie in Der Stealer packte die Platzhalter ein. Zu den Kopien, die ein Agent in seinen eigenen Transkripten hinterlässt, lesen Sie Das Transkript behielt das Token.