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

Das gestohlene Token brauchte keinen Server

Wiz Research hat Gheist nachverfolgt, eine automatisierte Kampagne, die GitHub-Tokens aus Infostealer-Logs und älteren Supply-Chain-Lecks nimmt und in jedes Repository, das ein Token erreicht, einen Workflow pflanzt, der die Secrets ausgibt: mehr als 13.500 Branches aus mehr als 850 Konten. Die Secrets verlassen GitHub über GitHubs eigene API, es gibt also keinen Angreifer-Server, den man blockieren könnte. In Bromure Agentic Coding ist das GitHub-Token, das ein Stealer auf einem Entwicklerrechner findet, ein Platzhalter, den GitHub von überall sonst ablehnt, und jeder Schreibzugriff aus der Maschine wartet auf einen Dialog auf Ihrem Mac.

Gheist hat keinen Command-Server. GitHub führt die Payload aus, GitHub schreibt die Secrets in ein Log, und GitHub liefert das Log an jeden aus, der das Token besitzt.

Am 25. Oktober hat Wiz Research seine Analyse von Gheist veröffentlicht, einer Kampagne, die das Team seit dem 15. September verfolgt. Ein automatisiertes Tool nimmt ein gestohlenes GitHub-Token, listet jedes Repository auf, das dieses Token erreicht, und pflanzt dort einen Workflow, der die Secrets des Repositorys ausgibt. Wiz hat mehr als 13.500 gepflanzte Branches aus mehr als 850 kompromittierten Konten sichergestellt, und Stand 24. Oktober lief die Kampagne noch und veränderte sich weiter. The Hacker News hat gestern darüber berichtet.

Das Tool braucht nur eine einzige Eingabe, und Wiz sagt, woher sie stammt: Die Tokens passen zu Infostealer-Logs und zu Tokens, die bei früheren Supply-Chain-Kompromittierungen offengelegt wurden, etwa bei der des Scanners Trivy im März. Die Empfehlungen von Wiz an jedes Opfer enthalten diese Zeile: Prüfen Sie die Geräte der Token-Inhaber auf Infostealer.

Wie Gheist arbeitet

Für jedes Repository legt das Tool einen Branch an, der ci-sync- plus sechs zufällige Zeichen heißt, und committet eine einzige Datei, .github/workflows/sync-check.yml, mit der Nachricht ci: sync config. Der Commit ist unsigniert und dem Inhaber des Tokens zugeordnet. Seine API-Anfragen tragen den User-Agent gheist, der der Kampagne ihren Namen gab.

Der Push startet den Workflow. Der Job schreibt seine Umgebungsvariablen und den vollständigen secrets-Kontext ins Ausführungslog, beides base32-kodiert. Wiz erklärt, warum das funktioniert: „GitHub maskiert geheime Werte in Logs nur, wenn sie wörtlich erscheinen.“ Die base32-Ausgabe stimmt nicht mit den gespeicherten Werten überein, also wird nichts maskiert. Eine Matrix über die Deployment-Umgebungen des Repositorys zieht die umgebungsspezifischen Secrets mit, und das sind oft die für die Produktion.

Danach lädt das Tool das Log über die GitHub-API herunter und löscht die Ausführung Sekunden später. In den Worten von Wiz: „Weil die Secrets über GitHubs eigene API abfließen, gibt es keine Angreifer-Domain und keine ausgehende Verbindung, die man blockieren könnte.“

Das Tool wurde ständig verbessert. Eine Variante bekam ein Timeout von sechs Minuten, die Umgebungsmatrix und repository-spezifische Änderungen an der Payload, und zwar wenige Stunden nach ihrem ersten Lauf am 28. September. Am 19. Oktober kam ein Job für selbst gehostete Runner hinzu, der nach 31 Token-Typen, nach Zugangsdaten von Cloud-Instanzen und nach dem Token des Kubernetes-Service-Accounts sucht.

Vom gestohlenen Token zu den Secrets eines Repositorys1 · Ein gestohlenes Tokenaus einem Infostealer-Logoder Supply-Chain-Leck850+ Konten2 · Branch pro Repositoryci-sync-xxxxxxfügt sync-check.yml hinzu13.500+ Branches3 · Secrets ins LogtoJSON(secrets) | base32Maskierung übersiehtkodierte Werte4 · Laden, dann löschenLog über GitHub-API geladen,Lauf sofort danach gelöscht;kein Angreifer-HostÖffentliche Repositorys · von GitHub gestopptseit 28. Juli warten markierte Läufe auf Freigabe;jeder öffentliche Gheist-Lauf, den Wiz prüfte, hingbei action_requiredPrivate Repositorys und GHES · liefenAudit-Logs zeigen abgeschlossene ci-sync-*-Läufe,danach gelöscht; CloudTrail zeigt AWS-Rollen,von diesen Branches übernommen
Die Kette von Gheist, wie Wiz sie beschreibt. Ein Token aus einem Infostealer-Log oder einem älteren Supply-Chain-Leck lässt ein automatisiertes Tool einen ci-sync--Branch mit einer einzigen Workflow-Datei in jedes Repository pushen, das das Token erreicht. Der Workflow gibt jedes Secret in base32 aus, an GitHubs Log-Maskierung vorbei, und das Tool lädt das Log über GitHubs API herunter und löscht die Ausführung. An keinem Schritt ist ein Angreifer-Server beteiligt.

Wo GitHubs neue Regel endet

Seit dem 28. Juli hält GitHub Workflow-Ausführungen, die es in öffentlichen Repositorys als bösartig einstuft, zurück, bis ein Collaborator sie freigibt. Jede öffentliche Gheist-Ausführung, die Wiz geprüft hat, hing bei action_required fest. „Die Kontrolle hat funktioniert, und die bösartigen Payloads liefen nie.“

Die Sperre gilt nur für öffentliche Repositorys auf github.com. In privaten Repositorys und auf GitHub Enterprise Server fand Wiz Audit-Logs mit ci-sync-*-Ausführungen, die abgeschlossen und dann gelöscht wurden, sowie CloudTrail-Einträge von Organisations-Workflows, die von diesen Branches aus AWS-Rollen übernahmen.

Das Token kommt von einem Entwicklerrechner

Jeder Gheist-Branch braucht ein funktionierendes Token, und Wiz hat diese Tokens auf Stealer-Logs und geleakte Build-Tools zurückgeführt. Auf dem Laptop eines Entwicklers liegen der Login der gh-CLI, ein Personal Access Token in ~/.git-credentials, ein GH_TOKEN in einem Shell-Profil: Ein Infostealer kopiert sie alle in einem Durchgang. Coding-Agenten haben solche Tokens häufiger gemacht, denn ein Agent, der Branches pusht und Pull Requests öffnet, braucht ständig eins in Reichweite.

Der Diebstahl kann von einem Tool kommen, dem Sie vertrauen. Am 19. Oktober hat ein Angreifer, der seit Monaten das GitHub-Konto eines OVHcloud-Entwicklers kontrollierte, die Binärdateien in fünf Releases der OVHcloud-CLI, v0.11.0 bis v0.15.0, gegen Builds getauscht, die nach den Worten von OVHcloud-CEO Octave Klaba „Secrets von der Festplatte holen“. BleepingComputer berichtete, dass die Builds für macOS, Linux und Windows allesamt betroffen waren. Klabas Rat: „Wenn Sie betroffen sind, ändern Sie bitte alle Secrets auf Ihrer Festplatte.“

Ein gestohlenes Token muss nicht noch am selben Tag bei einer Kampagne wie Gheist landen. Stealer-Logs werden gehandelt, und ein Token bleibt nützlich, bis es jemand widerruft.

Dasselbe Token auf einer Bromure-Maschine

Bromure Agentic Coding führt Claude Code, Codex, Grok, Kimi und omp in Ubuntu-VMs auf Ihrem Mac aus. Echte Zugangsdaten bleiben auf dem Host, und der Agent arbeitet mit Platzhaltern. Verfolgen Sie ein GitHub-Token auf einer dieser Maschinen durch die Kette von Gheist.

Das Token auf der Festplatte ist ein Platzhalter

Fügen Sie Ihr GitHub-Token der Maschine unter Credentials hinzu. Beim Start schreibt Bromure eine Fälschung in die VM: ghp_ plus 36 Zeichen, die Form, die gh erwartet, in GH_TOKEN, in die Konfiguration von gh und in ~/.git-credentials. git push und gh pr create des Agenten funktionieren wie bisher. Wenn eine Anfrage die VM in Richtung github.com verlässt, tauscht der Proxy auf dem Mac die Fälschung gegen das echte Token aus, und zwar nur für diesen Host.

Führen Sie jetzt die trojanisierte CLI in dieser VM aus, so wie ein Agent es tun würde, nachdem er die Installationsanleitung eines Projekts befolgt hat. Sie liest die Festplatte, und jeder GitHub-Wert darauf ist der Platzhalter. Dasselbe gilt für den Rest der üblichen Beute bei Entwicklern. ~/.aws/config verweist auf ein Hilfsprogramm, das einen gefälschten Secret Key ausgibt, und der Proxy signiert AWS-Anfragen auf dem Mac mit dem echten neu. Die VM hat keine privaten SSH-Schlüssel, die man lesen könnte: SSH_AUTH_SOCK führt zu einem Agenten auf dem Host, der für die VM signiert und den Schlüssel nie herausgibt.

Gheist kann mit dem Platzhalter nichts anfangen

Das Gheist-Tool läuft auf der eigenen Infrastruktur des Angreifers. Es ruft api.github.com mit dem gekauften Token auf und erwartet, Repositorys zu sehen. Bei einem Bromure-Platzhalter antwortet GitHub mit 401 Bad credentials. Bromure leitet den Platzhalter mit einem Salt, das auf Ihrem Mac bleibt, aus dem echten Token ab, und nur der Proxy dieses Macs weiß, für welches echte Token er steht. Das Tool kann kein einziges Repository auflisten, also legt es nie einen Branch an.

Die ganze Methode von Gheist besteht darin, zu fragen, was ein Token erreicht. Außerhalb der VM, in die er geschrieben wurde, erreicht ein Bromure-Platzhalter nichts.

Ein Platzhalter auf dem Weg zum Stealer löst Alarm aus

Der Stealer muss seinen Fund noch irgendwohin schicken, und aus einer Bromure-VM läuft jede HTTPS-Verbindung über den Proxy auf dem Mac. Der Proxy durchsucht jede ausgehende Anfrage, Header und Body, nach Platzhaltern, die an einen Host gehen, für den sie nicht erzeugt wurden. Ein ghp_-Platzhalter, der an etwas anderes als GitHub geht, wird mit HTTP 451 blockiert, bevor auch nur ein Byte den Mac verlässt. Die VM wird angehalten, und eine Warnung zeigt This environment may have been compromised, mit dem Namen der Zugangsdaten, dem Host, zu dem sie gehören, und dem Host, an den sie geschickt wurden.

Sie können die Maschine herunterfahren oder ihre Festplatte und ihren Home-Ordner zur Untersuchung sichern. Eine als kompromittiert markierte Maschine startet erst wieder, wenn Festplatte und Home-Ordner gelöscht sind, der Stealer überlebt den Neustart also nicht.

Stellen Sie Unmatched traffic im Guardrails-Bereich der Maschine auf Deny und erlauben Sie die Hosts, die das Projekt braucht, dann wird der Upload-Host des Stealers direkt abgewiesen, ob mit verschlüsseltem Archiv oder ohne. Die abgewiesene Verbindung erscheint als Zeile in der Security Timeline.

Schreibzugriffe aus der Maschine warten auf Sie

Angenommen, die Malware verzichtet auf den Diebstahl und nutzt den Platzhalter dort, wo er funktioniert: aus der VM heraus, über den Proxy. Sie müsste tun, was Gheist tut: einen Branch anlegen und einen Workflow committen.

Neue Bromure-Maschinen stellen die Schreibrichtlinie für GitHub auf Prompt before write. Lesezugriffe gehen durch. Ein git push oder ein REST-Schreibzugriff auf github.com hält an einem Dialog auf Ihrem Mac an, der die Operation wörtlich zeigt:

Allow write on "GitHub" from workspace "web-app"?
POST /repos/<owner>/<repo>/git/refs

Einen ci-sync--Branch, um den Sie nicht gebeten haben und der auftaucht, während Sie sich einen Pull Request ansehen, lehnt man leicht ab. Don't allow schickt dem Agenten denselben Fehler, den eine blockierende Richtlinie liefern würde, und Allow once erlaubt nichts über diesen einen Schreibzugriff hinaus.

Laptop: ein gültiges Token auf der PlatteStealer liest die Festplattegh-Login, ~/.git-credentials, GH_TOKEN: das echte TokenStealer-Log, dann das gheist-Toolfunktioniert von jedem Rechner im Internetapi.github.com akzeptiert esci-sync-Branches in jedem erreichten Repository;Secrets fließen über GitHubs eigene API abBromure: ein Platzhalter in der VMAgenten-VM (Ubuntu)gh-Config, ~/.git-credentials, GH_TOKEN = ghp_-PlatzhalterHost-Proxy auf dem Macechtes Token nur für github.com eingesetzt;Platzhalter anderswohin: 451, VM angehalten; Writes fragenVon der Infrastruktur des Angreifersder Platzhalter ist kein GitHub-Token;api.github.com lehnt ihn ab, nichts wird gepflanzt
Gheist auf einem Laptop und auf einer Bromure-Maschine. Auf dem Laptop kopiert ein Stealer ein gültiges GitHub-Token von der Festplatte, und das gheist-Tool nutzt es von überall. Auf einer Bromure-Maschine hält die VM einen ghp_-Platzhalter: GitHub lehnt ihn von der Infrastruktur des Angreifers ab, der Proxy blockiert ihn auf dem Weg zum Host des Stealers und hält die VM an, und jeder Schreibzugriff aus der VM wartet auf einen Dialog auf dem Mac.

Die Rotation passiert an einer Stelle

Der erste Schritt der Reaktion laut Wiz ist, jedes Secret zu rotieren, das ein betroffenes Repository erreichen konnte, und die Tokens zu widerrufen, die die Commits gepusht haben. „Das Löschen der Ausführung oder des Branches macht die Offenlegung nicht rückgängig.“

Auf einem Bromure-Mac ist die Entwicklerseite dieser Rotation ein einziges Feld. Das echte GitHub-Token liegt in einem einzigen verschlüsselten Eintrag auf dem Host. Fügen Sie das neue im Bereich Credentials der Maschine ein, und der nächste Start schreibt den passenden Platzhalter; es gibt keine Kopien, die man in Shell-Profilen, .env-Dateien oder Agent-Konfigurationen suchen müsste. Bei einem Schlüssel, der als 1Password-Referenz gespeichert ist (op://vault/item/field unter Other API keys), löst Bromure ihn alle zwei Minuten neu auf, sodass eine Rotation in 1Password laufende Maschinen ohne Neustart erreicht.

Der Tresor selbst liegt unter einem Schlüssel im Data Protection Keychain von macOS, gebunden an die Signaturidentität von Bromure und nie synchronisiert. Ein Stealer, der Bromures Ordner vom Mac kopiert, bekommt nur Chiffretext.

Lassen Sie einen Agenten suchen, nur lesend

Die Suchschritte von Wiz sind mühsame Arbeit über viele Repositorys hinweg, und damit gute Arbeit für einen Agenten: jeden Branch auflisten, der auf ^ci-sync-[0-9a-z]{6}$ passt, die Commits finden, die nur sync-check.yml mit der Nachricht ci: sync config hinzufügen, im Audit-Log nach Ausführungen suchen, die weniger als eine Minute nach ihrem Ende gelöscht wurden, und nach dem User-Agent gheist.

Geben Sie die Aufgabe einer Bromure-Maschine, deren GitHub-Schreibrichtlinie auf Read-only steht. Der Agent kann jedes Repository und jedes Audit-Log lesen, das das Token erlaubt, und jeder Push oder REST-Schreibzugriff wird auf dem Mac abgewiesen, bevor er GitHub erreicht. Checkt der Agent einen ci-sync--Branch aus, um ihn anzusehen, auch einen der Branches, die Wiz mit PolinRider-Loadern erneut gepusht sah, läuft alles, was dabei ausgeführt wird, in einer VM voller Platzhalter. Jeder Austausch von Zugangsdaten erscheint als Zeile Credential brokering in der Security Timeline, sodass Sie danach sehen, welche Hosts die Suche berührt hat.

Stealer finden Platzhalter

GH_TOKEN, die Konfiguration von gh und ~/.git-credentials enthalten eine ghp_-Fälschung. Das echte Token bleibt verschlüsselt auf dem Mac.

Gheist bekommt eine 401

Außerhalb der Maschine ist der Platzhalter kein GitHub-Token. Das Tool kann keine Repositorys auflisten, also pflanzt es nichts.

Lecks halten die VM an

Ein Platzhalter, der irgendwohin außer zu GitHub geht, wird mit einer 451 blockiert, und die VM bleibt angehalten, bis Sie über sie entscheiden.

Schreibzugriffe fragen zuerst

Neue Maschinen fragen vor jedem Push und jedem REST-Schreibzugriff auf GitHub nach, mit der genauen Anfrage auf dem Bildschirm. Eine Suche kann nur lesend laufen.

Holen Sie das Token vom Laptop

GitHubs Freigabesperre hat bei öffentlichen Repositorys funktioniert, und die Härtungsliste von Wiz ist die richtige: verpflichtende Reviewer für Produktionsumgebungen, OIDC-Vertrauen beschränkt auf geschützte Branches, GitHub Apps oder kurzlebige Fine-grained Tokens statt Personal Access Tokens. All das schränkt ein, was ein gestohlenes Token tun kann, sobald jemand es besitzt.

Bromure Agentic Coding setzt einen Schritt früher an. Ihre Agenten pushen den ganzen Tag Branches und öffnen Pull Requests, und das Token, das sie nutzen, liegt nie auf einer Festplatte, die ein Stealer lesen kann. Gheist kann Workflows nur mit Tokens pflanzen, die noch funktionieren, und das auf einer Bromure-Maschine funktioniert nur über den Proxy genau dieser Maschine.

Wie dieselben Platzhalter einem Stealer standhalten, der für die Dateien von Coding-Agenten gebaut wurde, lesen Sie in Der Stealer packte die Platzhalter ein. Wie ein einzelnes gestohlenes GitHub-Token in einem vergifteten Release endete, lesen Sie in Das Token des Agenten gehörte dem Owner.