← Zurück zu allen Beiträgen
Veröffentlicht am · von Renaud Deraison

Der gelöschte Schlüssel stand noch in der History

Am 8. Oktober hat eine Kampagne namens GhostAction zwei gekaperte Maintainer-Konten genutzt, um einen Workflow namens security-audit.yml in 346 Repositories zu committen, darunter Ubers athenadriver und pyxel mit 18.000 Sternen. Am 9. Oktober zählte Socket mehr als 500 Konten und Zehntausende Repositories. Der Workflow führt git log -p --all aus und durchsucht jeden jemals gemachten Commit nach Schlüsseln für Anthropic, OpenAI, OpenRouter, GitHub und AWS, auch nach denen, die einen Commit später gelöscht wurden. In Bromure Agentic Coding hält der Agent immer nur Platzhalter, also sind die Schlüssel, die er in die History eines Repositorys schreibt, für den Finder nichts wert.

Einen Schlüssel aus einer Datei zu löschen kostet einen Commit. Der Commit davor behält den Schlüssel, solange das Repository existiert.

Am Donnerstag, dem 8. Oktober, um 13:40 UTC begann das GitHub-Konto von Takashi Kitao, Autor der Game-Engine pyxel, eine Datei namens .github/workflows/security-audit.yml zu committen. Vier Minuten später lag sie in 27 seiner Repositories. Um 21:10 UTC startete das Konto henrywoo, ursprünglicher Autor von Ubers athenadriver, denselben Durchlauf. Um 21:26 hatte es 318 Repositories erreicht, darunter 279 Forks und uber/athenadriver selbst, wo das Konto Schreibrechte hat.

Socket veröffentlichte seine Analyse am 9. Oktober und ordnete die Commits GhostAction zu, einer Kampagne zum Diebstahl von Zugangsdaten, die erstmals im September 2025 auftauchte. Bis zum Ende dieses Tages zählte Socket in einem Update mehr als 500 GitHub-Konten, die den Workflow seit dem 7. Oktober in „Zehntausende Repositories“ committet hatten. The Hacker News und StepSecurity berichteten über dieselbe Welle. Socket stellte fest, dass der Workflow in betroffenen Repositories bereits gelaufen war und zum Zeitpunkt der Veröffentlichung noch auf den Default-Branches von kitao/pyxel und uber/athenadriver lag.

Was security-audit.yml tut

Der Workflow heißt „Security Audit“. Er läuft bei workflow_dispatch und bei jedem Push auf einen beliebigen Branch oder Tag, ohne Filter. Sein Checkout-Schritt verwendet fetch-depth: 0 und holt damit jeden Branch, jeden Tag und die vollständige History. Dann erledigt ein einziger Schritt zwei Aufgaben.

Die erste zielt auf die GitHub-Actions-Secrets des Repositorys. Bevor der Angreifer den Workflow committete, las er die vorhandenen Workflows des Repositorys und schrieb die Namen der dort verwendeten Secrets in den Payload. Bei pyxel waren das CARGO_REGISTRY_TOKEN, PERSONAL_ACCESS_TOKEN, PYPI_PASSWORD und PYPI_USERNAME.

Die zweite ist neu in dieser Welle. Der Schritt durchsucht den Arbeitsbaum mit grep nach 13 Mustern für Zugangsdaten, führt dann git log -p --all aus und durchsucht bis zu 200.000 Zeilen History nach denselben Mustern:

  • KI-Dienste: Anthropic (sk-ant-), OpenAI-Projektschlüssel (sk-proj-), OpenRouter (sk-or-)
  • Versionsverwaltung: klassische GitHub-Tokens (ghp_), Fine-grained Tokens (github_pat_), GitLab-Tokens (glpat-)
  • AWS: Access-Key-IDs, temporäre Key-IDs, Secret Keys und Session-Tokens. Der Schritt behält außerdem zwei Zeilen Kontext um jede Key-ID herum, sodass der daneben committete Secret Key mitkommt.
  • SaaS und Cloud: Google-API-Schlüssel, Slack-Tokens, SendGrid-Schlüssel

Alles geht in einem einzigen curl-POST über unverschlüsseltes HTTP an 193.32.204.199. Jeder Lauf meldet den Namen des Repositorys, ob er etwas gefunden hat oder nicht.

Socket benennt die Zielgruppe. Zwei der Repositories von henrywoo sind pyllama und chatllama, Machine-Learning-Projekte, deren Mitwirkende und Forker „wahrscheinlich genau diese Schlüsseltypen besitzen“. Ein Anthropic- oder OpenAI-Schlüssel lässt sich als abrechenbare Inferenz weiterverkaufen, und jeder Prompt, der darüber läuft, geht durch die Hände des Käufers.

Die History behält alles

Socket gibt bei der Bereinigung einen Rat, der jeden beunruhigen sollte, der einen geleakten Schlüssel schon einmal durch das Bearbeiten einer Datei beseitigt hat: „Rotieren Sie alle Zugangsdaten, die in irgendeinem Commit gefunden werden, ob sie noch im aktuellen Baum stehen oder nicht.“ Ein Schlüssel, der im nächsten Commit entfernt wird, steht weiterhin im Commit davor. git log -p --all liest beide.

Coding-Agents füllen diese History schnell. GitGuardians State of Secrets Sprawl 2026 ergab, dass öffentliche Commits, die Claude Code mitverfasst hatte, in 3,2 % der Fälle Secrets leakten, gegenüber einem Grundwert von 1,5 % auf dem gesamten öffentlichen GitHub. Derselbe Bericht zählte für 2025 1.275.105 geleakte Secrets, die an KI-Dienste gebunden waren, 81 % mehr als im Vorjahr.

Das Muster kennt jeder, der einem Agenten dabei zugesehen hat, wie er einen fehlschlagenden Integrationstest grün macht. Der Agent hat den Schlüssel in seiner Umgebung. Er schreibt ihn in eine Konfigurationsdatei, eine Test-Fixture oder eine .env, die noch nicht ignoriert wird, weil der Test dann durchläuft. Er committet. Später verschiebt der Entwickler oder der Agent den Schlüssel in eine Umgebungsvariable und committet erneut. Der Baum sieht sauber aus. Die History enthält den Schlüssel, jeder Fork hat die History, und ein Workflow mit fetch-depth: 0 liest sie.

Das Token, das den Workflow schrieb

Socket hat nicht ermittelt, wie die beiden Konten übernommen wurden. The Hacker News nennt die wahrscheinlichste Quelle: ein Personal Access Token aus Infostealer-Logs oder Credential-Dumps. Infostealer-Logs stammen von Entwicklerrechnern. Auf einem Laptop liegt das GitHub-Token in ~/.config/gh/hosts.yml, in ~/.git-credentials und oft in GH_TOKEN, lesbar für alles, was unter dem Konto des Entwicklers läuft, einschließlich allem, was ein Coding-Agent installiert oder ausführt.

Ein Token genügt. Damit kann der Angreifer die Repositories auflisten, in die das Konto schreiben darf, ihre Workflows nach Secret-Namen durchlesen und in jedes davon committen. Der Durchlauf von henrywoo erfasste 318 Repositories in 16 Minuten.

Ein gestohlenes Token, ein Workflow, jeder CommitLaptop des Maintainers~/.config/gh/hosts.yml~/.git-credentialsGH_TOKEN=ghp_… (echt)Infostealer-Log→ Token im Verkaufwahrsch. Quelle (THN)security-audit.yml: 318 Repos, 16 Min.on: push (any branch), workflow_dispatchfetch-depth: 0git log -p --all | grep 13 patternsHistory von config/test.pya1f3 + KEY="sk-ant-api03-…" ← gefundenc92e - KEY="sk-ant-api03-…" + KEY=os.environ["…"]Sauberer Baum, Schlüssel in der History.193.32.204.199POST (reines HTTP)REPO=…PYPI_PASSWORD=…sk-ant-api03-…AKIA_CTX_START…aktuelle Schlüsselund gelöschteJeder Fork trägt dieselbe History. Socket zählte allein bei henrywoo 279 Forks.
Die GhostAction-Kette. Ein Token von einem Entwicklerrechner committet den Workflow, der Workflow liest jeden jemals gemachten Commit, und jeder Schlüssel in dieser History, aktuell oder gelöscht, geht in einem einzigen POST hinaus.

Dasselbe Repository, geschrieben von einer Bromure-Maschine

In Bromure Agentic Coding läuft der Agent in einer Linux-VM auf Ihrem Mac, und jede HTTP-Anfrage der VM passiert einen Proxy auf dem Host. Ihr Anthropic-Schlüssel, Ihr GitHub-Token und Ihre AWS-Schlüssel bleiben verschlüsselt auf dem Mac. Lassen Sie GhostAction nun auf ein Repository los, das eine Bromure-Maschine geschrieben hat, und auf den Entwickler, der es geschrieben hat.

Die History enthält Platzhalter

In der VM ist jedes Zugangsdatum ein Platzhalter in der Form des echten: ANTHROPIC_API_KEY ist sk-ant-api03-brm-…, ein OpenRouter-Schlüssel ist sk-or-v1-brm-…, GH_TOKEN und ~/.git-credentials enthalten ghp_ plus 36 Zeichen. Jeder wird mit HKDF-SHA256 aus dem echten Wert und einem Salt pro Installation abgeleitet und lässt sich nicht in ihn zurückverwandeln. Der Proxy setzt den echten Wert erst auf der Leitung ein, und nur für eine Anfrage an den Host, zu dem das Zugangsdatum gehört.

Wenn der Agent also seinen Schlüssel in config/test.py schreibt, damit der Test durchläuft, schreibt er, was er hat. Wenn er committet, enthält der Commit sk-ant-api03-brm-…. Wenn später der Aufräum-Commit kommt, behält die History genau diesen Platzhalter.

GhostActions grep findet ihn. sk-ant- passt auf sk-ant-api03-brm-, und ghp_ passt auf einen GitHub-Token-Platzhalter. Der POST an 193.32.204.199 nimmt sie mit, und der Käufer probiert sie aus. Anthropic lehnt den Schlüssel ab und GitHub das Token, denn das Einzige, was sie in funktionierende Zugangsdaten verwandelt, ist der Proxy auf Ihrem Mac, für Anfragen, die Ihre VM verlassen. Socket rät, jeden in irgendeinem Commit gefundenen Schlüssel zu rotieren. Für dieses Repository ist die Antwort kurz: Keiner dieser Schlüssel hat außerhalb Ihres Macs je funktioniert, und die Platzhalter für KI-Anbieter tragen -brm- im Namen, sodass Sie sie bei einem Scan der History erkennen.

Auch das Token auf dem Laptop ist ein Platzhalter

Die andere Hälfte der Kampagne ist das Konto selbst. Lassen Sie Ihre Agents in einer Bromure-Maschine laufen, und das GitHub-Token, das sie verwenden, liegt auf dem Mac. Alles, was in der VM läuft, ob das Installationsskript eines Pakets, ein Stealer oder eine Prompt-Injection, findet in ~/.git-credentials und GH_TOKEN einen ghp_-Platzhalter. Ein Angreifer, der ihn von seinem eigenen Rechner aus abspielt, bekommt von GitHub eine 401.

Der Stealer muss seine Beute auch irgendwohin schicken, und diese Anfrage passiert den Proxy. Ein Platzhalter hat genau ein legitimes Ziel: den Host, für den er erzeugt wurde. Der Proxy durchsucht jede ausgehende Anfrage, Header und Body, nach jedem Platzhalter, der woandershin unterwegs ist. Ein ghp_-Token auf dem Weg zum Server eines Stealers bekommt HTTP 451, nichts wird weitergeleitet, die VM pausiert, und auf dem Mac öffnet sich This environment may have been compromised mit einer Vorschau des Tokens, dem Namen des Zugangsdatums, dem Host, zu dem es gehört, und dem Host, an den es gesendet wurde.

Ein Workflow-Push wartet auf einen Klick

Würmer dieser Familie warten nicht immer auf einen Käufer. Der tensorlake-Payload hat letzte Woche Dateien direkt vom Rechner des Entwicklers in die Repositories seiner Opfer committet. Neue Bromure-Maschinen verwenden Prompt before write für jede Git-Forge. Ein Push ist ein Schreibvorgang, also hält das Committen von security-audit.yml in ein Repository aus der VM an einem macOS-Dialog an, Allow write on "github.com" from workspace "…"?, der POST /owner/repo.git/git-receive-pack so zeigt, wie es über die Leitung ging. Der Durchlauf von henrywoo machte 318 solcher Schreibvorgänge in 16 Minuten. Von einer Bromure-Maschine aus wartet der erste auf Sie, und Sie haben Ihren Agenten nicht gebeten, 318 Repositories ein Security-Audit hinzuzufügen.

Jede Verwendung des Tokens wird protokolliert

Die Security Timeline protokolliert jeden Austausch eines Zugangsdatums als Credential brokering-Zeile: welches Token, welcher Host, welche Maschine, zu welcher Zeit. Wenn Sie wissen müssen, ob Ihr GitHub-Token am 8. Oktober zwischen 21:10 und 21:26 etwas gepusht hat, liegt die Antwort auf Ihrem Mac, in einem Protokoll, das der Agent nicht bearbeiten kann. Mit aktiviertem Tracing listet bromure-cli trace leaks jede Anfrage auf, in der ein Token beim falschen Host aufgetaucht ist.

In Bromure: Platzhalter in der History, echte Schlüssel auf dem MacVM der Maschine (Ubuntu)GH_TOKEN=ghp_… (Platzh.)ANTHROPIC_API_KEY=sk-ant-api03-brm-…Commit des Agenten+ KEY="sk-ant-api03-brm-…"Stealer sendet ghp_ hinausgit push security-audit.ymlkein echtes Secret in der VMProxy, auf dem MacEchter Wert nur für den Hostdes ZugangsdatumsPlatzhalter woandershin → 451,VM pausiert, Alarm auf dem Macgit-receive-pack → Prompt before writeSecurity Timeline: jeder Tausch,Token, Host, Maschine, ZeitGitHub-Historysk-ant-api03-brm-…GhostActions grepfindet sk-ant- undschickt es rausAnthropic: 401GitHub: 401der Käufer bekommtwertlose StringsAuf dem Mac, außerhalb der VMAnthropic-, OpenAI-, OpenRouter-Schlüssel · GitHub-Token · AWS · SSH-Schlüssel · Traces · Security Timeline
Dieselbe Kette gegen eine Bromure-Maschine. Die History, die der Workflow liest, enthält Platzhalter, das Token, das ein Stealer in der VM findet, ist ein Platzhalter, und jede Anfrage, die einen davon zum falschen Host trägt, endet auf dem Mac.

Aufräumen nach einem Schlüssel

Der übliche Rat nach einem geleakten Schlüssel lautet: rotieren, die History mit git filter-repo umschreiben, force-pushen und hoffen, dass kein Fork und kein Mirror die alten Commits vorher kopiert hat. GhostAction zeigt, warum der letzte Teil scheitert. Socket zählte 279 Forks in einem einzigen Namespace, jeder mit der History und inzwischen auch dem Workflow. Ein Schlüssel, der einen öffentlichen Commit erreicht hat, ist verloren, egal was der aktuelle Baum zeigt.

Eine Bromure-Maschine ändert, was überhaupt im Commit landet. Der Agent darf so nachlässig mit Schlüsseln umgehen, wie GitGuardians Zahlen es Agents nachsagen. Er kann einen Schlüssel in eine Fixture kopieren, eine .env committen und sie vergessen. Er hatte nie einen funktionierenden Schlüssel, also kann Ihnen nichts in dieser History schaden.

Der Commit enthält einen Platzhalter

Agents in der VM sehen nur Platzhalter in der Form der echten Schlüssel. Ein in eine Datei geschriebener und committeter Schlüssel ist ein -brm--String, der nur über den Proxy auf Ihrem Mac funktioniert.

Die Ernte bringt 401er

GhostActions Muster finden die Platzhalter und schicken sie hinaus. Anthropic, OpenAI und GitHub lehnen sie überall außerhalb Ihres Macs ab.

Ein gestohlenes Token stoppt die Maschine

Auch das GitHub-Token in der VM ist ein Platzhalter. Wird es anderswohin als zu GitHub geschickt, bekommt es eine 451, die VM pausiert, und ein Alarm nennt das Token und den Host.

Pushes warten auf einen Klick

Prompt before write hält jeden Push aus der VM an einem macOS-Dialog an, der die Anfrage zeigt, und die Security Timeline protokolliert jede Verwendung des Tokens.

Das nächste grep

Das ist die dritte Welle von GhostAction in dreizehn Monaten. Die erste, im September 2025, erbeutete 3.325 CI-Secrets. Die zweite, vom 31. August bis 30. September dieses Jahres, zielte auf 2.577 Secrets in 772 Repositories. Diese hier hat die vollständige History und drei Muster für KI-Anbieter hinzugefügt. Die nächste Welle wird weitere Muster hinzufügen, und die History, die sie liest, wächst nur.

Lassen Sie Ihre Agents dort laufen, wo die Schlüssel, die sie anfassen, Platzhalter sind, und die History, die sie hinterlassen, enthält nichts, was ein Käufer verwenden kann. Mehr dazu, wie Platzhalter funktionieren und was passiert, wenn einer leakt, lesen Sie in Der Agent leakte seinen eigenen Schlüssel und Der Schlüssel ist nie abgelaufen.