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

Das Widerrufen des Tokens war der Auslöser

Am 8. Oktober landete tensorlake 0.5.144 mit einem Shai-Hulud-Wurm auf npm. Er stiehlt GitHub-, npm-, Cloud- und SSH-Zugangsdaten, legt .claude/settings.json in den Repositories des Opfers ab und installiert einen Monitor, der das gestohlene GitHub-Token alle 60 Sekunden prüft und rm -rf ~/ ausführt, sobald es nicht mehr funktioniert. Das Token zu widerrufen, der erste Schritt im üblichen Incident-Playbook, löscht das Home-Verzeichnis des Opfers. In Bromure Agentic Coding verbirgt die standardmäßige Altersschranke diese Version vor npm, und das GitHub-Token in der Maschine war nie das echte.

Der Wurm hinterließ in seinem Code eine Zeichenkette für den, der sie findet: „IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner.“ Wer dieses Token widerruft, löscht den Rechner seines Besitzers. Der Code tut, was dort steht.

Am Mittwoch, dem 8. Oktober, um 01:12 UTC veröffentlichte der Release-Workflow von tensorlakeai/tensorlake die Version 0.5.144 des tensorlake-SDK auf npm. Tensorlake ist eine serverlose Sandbox-Plattform für KI-Agenten, und sein npm-Paket kommt auf rund 12.000 Downloads pro Woche. Die neue Version stammte aus dem eigenen Repository des Projekts, gebaut vom eigenen Workflow des Projekts, und npm zeigte dafür eine Provenance-Attestierung an.

Socket meldete sie elf Minuten später. StepSecurity, Aikido und The Hacker News folgten am selben Tag. Die Version enthält eine neue Variante des Shai-Hulud-Wurms, und diese bringt eine Geisel mit.

Wie er hineinkam

StepSecurity hat den Ablauf rekonstruiert. Am 7. Oktober um 01:20 UTC pushte jemand einen Commit direkt auf den Hauptbranch des Repositorys, unter dem Namen eines Maintainers. Sieben weitere Commits folgten in den nächsten Stunden. Niemand bemerkte sie, und einen Tag später baute der Release-Workflow den Code auf main und lieferte ihn aus. Die Provenance-Attestierung war korrekt. Sie belegte, wo das Paket gebaut wurde, und dieser Ort war bereits in der Hand des Angreifers.

Die package.json des Pakets bekam eine Zeile dazu:

"preinstall": "node lib/setup.mjs"

npm führt preinstall aus, bevor das Paket überhaupt an seinen Platz entpackt ist. Das Skript lädt die Bun-Runtime herunter und führt damit lib/Math_Symbol.js aus, eine verschleierte Nutzlast von 856 KB. In CI überspringt es sich selbst. StepSecurity fasst zusammen: „Das Ziel sind Entwicklerrechner.“

Was er nimmt, und wohin es geht

Die Nutzlast sammelt GitHub- und npm-Tokens, Cloud-Schlüssel für AWS, Azure und GCP, Kubernetes- und HashiCorp-Vault-Secrets, SSH-Schlüssel, .env-Dateien, gespeicherte Browser-Logins, Krypto-Wallets und die Konfiguration von KI-Coding-Tools: .claude, .cursor, .kiro, Windsurf und Zed. Sie verschlüsselt die Beute und schickt sie an iseekaigogo.com oder, falls das scheitert, an ein öffentliches Repository, das sie im eigenen GitHub-Konto des Opfers anlegt, mit der Beschreibung „Shai-Hulud: Here We Go Again.“ Fällt die Domain aus, sucht sie über rund 30 öffentliche RPC-Endpunkte in einem Ethereum-Contract nach einer neuen.

Dann verbreitet er sich. Mit dem npm-Token des Opfers lädt er jedes seiner Pakete herunter, fügt sich selbst hinzu, erhöht die Version und veröffentlicht neu. Mit dem GitHub-Token committet er .claude/settings.json und .vscode/tasks.json in die Repositories des Opfers, als [email protected], mit der Nachricht chore: update dependencies. Der nächste Entwickler, der eines dieser Projekte in Claude Code oder VS Code öffnet, startet den Wurm erneut. Wie Ashish Kurmi von StepSecurity es formuliert, „läuft die Malware erneut, wenn jemand das Projekt in Claude Code oder VS Code öffnet“.

Die Geisel

Neu ist ein Dienst namens gh-token-monitor. Unter Linux ist er eine systemd-User-Unit in ~/.config/systemd/user/, unter macOS ein LaunchAgent in ~/Library/LaunchAgents/com.user.gh-token-monitor.plist und unter Windows eine Anmeldeaufgabe, die monitor.ps1 ausführt. StepSecurity beschreibt, was er tut:

Alle 60 Sekunden, bis zu 24 Stunden lang, prüft der Dienst dieses Token gegen die GitHub-API. Weist GitHub es zurück, führt der Dienst rm -rf ~/ aus.

Angenommen, Sie sind das Opfer. Sie erfahren, dass ein Paket, das Sie installiert haben, Ihr GitHub-Token gestohlen hat. Das übliche Playbook sagt: sofort widerrufen. Sie widerrufen es, und innerhalb einer Minute ist Ihr Home-Verzeichnis weg: Ihre Checkouts, Ihre nicht committete Arbeit, Ihre Dotfiles und die Spuren dessen, was der Wurm getan hat. Socket fand die Drohung ausgeschrieben im Code, als literale Zeichenkette: IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner.

Die Advisories kommen deshalb jetzt mit einer Reihenfolgeregel. Schritt 3 bei StepSecurity lautet, den Monitor zu finden und zu entfernen und vorher alles zu sichern, was Sie nicht verlieren dürfen. Die Warnung, fett gedruckt: „Widerrufen Sie kein GitHub-Token, bevor Schritt 3 erledigt ist.“ Socket sagt dasselbe. Jede Stunde, die Sie mit der Suche nach dem Monitor verbringen, ist eine Stunde, in der der Angreifer ein funktionierendes Token mit Ihren Push-Rechten hält.

Auf einem Laptop: Der erste Schritt des Playbooks löst das Löschen aus1. npm install[email protected]preinstall: node lib/setup.mjsStiehlt die echtenGitHub-Token (Push-Rechte)npm-Token (Publish-Rechte)~/.aws ~/.kube ~/.sshInstalliertgh-token-monitor (LaunchAgent).claude/settings.json in Repos2. Der Monitor, alle 60 sGET api.github.com/userAuthorization: echtes Token200 OK → weiter wartender Angreifer hält weiterIhre Push-Rechte401 → Handler ausführen3. Sie widerrufen das Tokenwie jedes Playbook es sagtrm -rf ~/Checkouts, offene Arbeit,Dotfiles und die Spuren
Auf einem Laptop ist das gestohlene Token echt, also ist das Widerrufen dringend. Der Monitor macht aus diesem Widerruf ein Löschen des Home-Verzeichnisses des Entwicklers.

Dieselbe Installation in einer Bromure-Maschine

In Bromure Agentic Coding laufen Claude Code, Codex und die anderen Agenten in einer Linux-VM auf Ihrem Mac, eine VM pro Maschine. Jede Verbindung, die die VM öffnet, auch jeder npm-Abruf, läuft über einen Proxy auf dem Mac. Spielen Sie den 8. Oktober in einer dieser Maschinen nach: Der Agent fügt einem Projekt tensorlake hinzu und führt npm install aus.

Die Version gab es noch nicht

Jede neue Maschine hat die Altersschranke der Lieferkette eingeschaltet, mit zwei Tagen Mindestalter. Wenn npm die Registry nach der Versionsliste von tensorlake fragt, entfernt der Proxy jede Version, die vor weniger als zwei Tagen veröffentlicht wurde, und lässt latest auf die neueste zeigen, die alt genug ist. Am 8. Oktober war das 0.5.143. npm installierte sie und machte weiter. Aus Sicht der Maschine hat es 0.5.144 nie gegeben, und bis sie die Schwelle überschritten hätte, war sie von npm verschwunden.

Hätte ein Lockfile oder der Agent 0.5.144 namentlich angefordert, hätte der Proxy den Download mit HTTP 451 abgelehnt, samt einer Begründung, die npm unverändert ausgibt:

Bromure Supply-Chain Security blocked this request: npm package
[email protected] published 3 hours ago — policy requires 2 days minimum

Der Agent liest diese Zeile ebenfalls und pinnt von sich aus die vorherige Version.

Zwei weitere Schichten sitzen im selben Bereich Supply Chain. Mit ausgewähltem socket.dev und eingeschaltetem Block compromised packages wird der Download abgelehnt, sobald socket.dev das Paket markiert hat, was am 8. Oktober elf Minuten dauerte. Mit eingeschaltetem Strip preinstall / install / postinstall / prepare from npm tarballs on the fly schreibt der Proxy das Tarball um, und die Zeile "preinstall": "node lib/setup.mjs" ist verschwunden, bevor npm sie sieht. Diese Zeile ist der einzige Weg des Wurms hinein.

Angenommen, er läuft trotzdem

Nehmen Sie alle Schichten weg und lassen Sie die Nutzlast laufen. Sie läuft als Benutzer der VM, im eigenen Home-Verzeichnis der Maschine, und sucht nach den Dingen auf ihrer Liste.

Die Tokens sind Platzhalter. Keine Datei, keine Umgebungsvariable und kein Prozess in der VM enthält ein echtes GitHub-Token, ein Cloud-Secret oder einen privaten SSH-Schlüssel. GH_TOKEN und ~/.git-credentials enthalten ein ghp_-Token in der richtigen Form, und der Proxy auf dem Mac setzt das echte erst ein, wenn eine Anfrage in Richtung GitHub hinausgeht. ~/.ssh enthält keinen privaten Schlüssel: SSH_AUTH_SOCK ist eine Brücke zu einem ssh-agent auf dem Mac, der signiert und den Schlüssel nie herausgibt. Das Home-Verzeichnis Ihres Macs, sein Schlüsselbund und seine Browserprofile sind gar nicht in der VM. Der Wurm verschlüsselt und verschickt ein Bündel Platzhalter, die GitHub, AWS und Kubernetes überall ablehnen, außer über diesen Proxy.

Der Weg nach draußen führt über den Mac. Eine Maschine, bei der Unmatched traffic im Bereich Guardrails auf Deny steht, mit Regeln für ihren Modellanbieter, ihre Registries und GitHub, verbindet sich nie mit iseekaigogo.com oder einem der 30 Ethereum-RPC-Endpunkte. Unabhängig von den Einstellungen erscheint jede dieser Verbindungen in der Security Timeline, die auf jeder Maschine immer eingeschaltet ist.

Schreibzugriffe warten auf einen Klick. Neue Maschinen verwenden Prompt before write für jede Git-Forge. Der GitHub-Ausweichweg des Wurms legt ein öffentliches Repository in Ihrem Konto an: Das ist ein POST /user/repos, ein Schreibzugriff, also zeigt macOS Allow write on "github.com" from workspace "…"? mit der Anfrage, wie sie auf der Leitung erschien. Bei seiner Verbreitung committet er .claude/settings.json in Ihre Repositories und pusht: derselbe Dialog, diesmal mit POST /acme/app.git/git-receive-pack. Sie haben den Agenten nicht gebeten, ein Repository namens „Shai-Hulud: Here We Go Again.“ anzulegen. Sie klicken auf Don't allow, und die Verbreitung des Wurms endet auf Ihrem Mac.

Die Geisel ist ein Platzhalter

Die Unit gh-token-monitor installiert sich trotzdem in ~/.config/systemd/user/ der VM und ruft alle 60 Sekunden api.github.com/user mit dem gefundenen Token auf. Dieses Token ist der Platzhalter. Der Proxy setzt für GitHub das echte ein, GitHub antwortet mit 200, und der Monitor wartet.

Damit kehrt sich die Reihenfolge der Reaktion um. Auf einem Laptop ist das echte Token in der Hand des Angreifers, und das Rennen besteht darin, es zu widerrufen, bevor er es nutzt, ohne das Löschen auszulösen. In Bromure ist das echte Token nie in die VM gelangt, also hat der Angreifer nichts, womit er gegen Sie antreten könnte. Nichts außerhalb Ihres Proxys akzeptiert den ghp_-Wert, den er erbeutet hat.

Fahren Sie die Maschine herunter, und der Monitor stoppt mit ihr. Öffnen Sie Rewind home… und wählen Sie den Checkpoint vom Boot vor der Installation: Bromure legt bei jedem erfolgreichen Boot einen an. Die Unit-Datei, das Skript in ~/.local/bin und die in den Checkouts der Maschine abgelegten .claude/settings.json-Dateien kehren alle in ihren vorherigen Zustand zurück. Das Home-Verzeichnis, wie es eben noch war, wird zuerst als neuer Rücksprungpunkt aufbewahrt, sodass sich auch das Zurückspulen rückgängig machen lässt. Für einen kompletten Neuanfang klont Reset disk die Systemplatte neu aus dem signierten Basis-Image.

Und wenn der Schalter trotzdem auslöst, löscht rm -rf ~/ das Verzeichnis /home/ubuntu in einer einzigen VM. Das Home-Verzeichnis Ihres Macs liegt außerhalb seiner Reichweite, und Rewind home… holt das der Maschine aus dem letzten Checkpoint zurück.

In Bromure: Die Version ist verborgen, und die Geisel ist ein PlatzhalterMaschinen-VM (Ubuntu)$ npm install tensorlake+ [email protected]Wenn die Nutzlast trotzdem läuftGH_TOKEN=ghp_… (Platzhalter)~/.ssh: keine privaten Keysnur das Home dieser Maschinegh-token-monitor fragt GitHub abmit dem Platzhalterein Löschen trifft /home/ubuntu in dieser VM,und Rewind home holt es zurückProxy, auf dem MacAltersschranke: latest → 0.5.1430.5.144 namentlich → 451, mit GrundFirewall: iseekaigogo.com → ablehnenEthereum-RPC-Endpunkte → ablehnenjedes Urteil in der Security TimelinemacOS-Dialog: Allow write on …?POST /user/reposPOST /acme/app.git/git-receive-packEchtes Token nur für GitHub eingesetztInternetregistry.npmjs.orggithub.comModellanbieterC2 des Angreifers:nie kontaktiertAuf dem Mac verwahrt, außerhalb der VMechtes GitHub-Token · Cloud-Schlüssel · private SSH-Schlüssel · Ihr Home-Ordner · Checkpoints · Security Timeline
Dieselbe Installation in einer Bromure-Maschine. Die Altersschranke verbirgt 0.5.144 vor npm. Läuft die Nutzlast trotzdem, stiehlt sie Platzhalter, ihre Pushes und das Anlegen eines Repositorys warten auf einen Dialog auf dem Mac, und der Geisel-Monitor hält ein Token, das GitHub nur über den Proxy von Bromure akzeptiert.

Wer hat es installiert?

Die erste Frage bei einem Vorfall wie diesem lautet, ob irgendeine Maschine im Team die schlechte Version gezogen hat. Auf einem Laptop durchsucht man Lockfiles, npm-Caches und die Shell-History und hofft, dass der Wurm nicht hinter sich aufgeräumt hat.

In Bromure ist jeder Paketabruf eine Zeile in der Security Timeline, ob eine Lieferketten-Schicht eingeschaltet ist oder nicht: npm · [email protected], mit Maschine, Uhrzeit und Entscheidung. Die Zeilen werden 90 Tage lang auf dem Mac aufbewahrt, wo nichts in einer VM sie ändern kann. Filtern Sie nach Supply chain, suchen Sie die Version, und Sie haben die Antwort. Export… speichert sie als CSV für das Incident-Ticket. Auf Macs, die bei bromure.io registriert sind, geht derselbe Abruf als Ereignis supply_chain.fetch in den Ereignisstrom der Organisation, sodass ein Security-Team die Frage einmal für alle Entwickler stellen kann.

Die schlechte Version tauchte nie auf

Die Altersschranke, standardmäßig an mit zwei Tagen Mindestalter, entfernt frische Versionen aus der Versionsliste von npm. Am 8. Oktober löste npm install tensorlake auf 0.5.143 auf.

Das gestohlene Token war ein Platzhalter

GitHub-, npm-, Cloud- und SSH-Zugangsdaten bleiben auf dem Mac. Die Geisel des Wurms war ein ghp_-Wert, der nur über den Proxy von Bromure funktioniert.

Verbreitung braucht Ihren Klick

Ein Repository anlegen und einen Commit pushen sind Schreibzugriffe. Mit Prompt before write, dem Standard auf neuen Maschinen, warten beide auf einen macOS-Dialog, den die VM nicht beantworten kann.

Das Löschen trifft eine VM

rm -rf ~/ erreicht das Home-Verzeichnis der Maschine, nicht Ihres. Rewind home stellt es aus dem Checkpoint vom letzten Boot wieder her.

Das nächste Playbook

Shai-Hulud ist dieses Jahr immer wieder zurückgekehrt, und seine Autoren gestalten jede Version danach, wie die Verteidiger mit der letzten umgegangen sind. Die Verteidiger haben Tokens innerhalb von Stunden widerrufen, also bestraft diese Version das Widerrufen. Die nächste wird etwas anderes bestrafen: ein Aufräumskript auszuführen, einen LaunchAgent zu löschen, das Projekt erneut zu öffnen.

Auf einem Laptop funktioniert jede dieser Wendungen, weil der Angreifer etwas Echtes auf genau der Maschine hält, die Sie zu säubern versuchen. In einer Bromure-Maschine liegen die echten Zugangsdaten, Ihr Home-Verzeichnis und die Aufzeichnung dessen, was passiert ist, alle auf dem Mac, hinter dem Proxy. Aufräumen ist ein Zurückspulen, und Sie können es erledigen, bevor Sie irgendetwas rotieren. Mehr über die Grenze auf der Leitung dahinter lesen Sie in Die Installation war sauber und Vierzigtausend Dollar pro Ausbruch.