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.
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.
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.