
Scanner für den keyv/cacheable Supply-Chain-Angriff: erkennt kompromittierte npm-Pakete, verifiziert Payload-Hashes und findet Persistenz-Implantate im Repo- und Host-Modus.
Englisch | Português
Eigenständiger Scanner für den keyv/cacheable-Lieferketten-Zwischenfall („Shai-Hulud: Here We Go Again", 4. Aug. 2026) — mehr als 440 npm-Pakete, kompromittiert durch einen sich selbst verbreitenden Wurm, der Cloud-/CI-Anmeldedaten stiehlt und mit einem Dead-Man's Switch Persistenz einrichtet.
Erkennt in Minuten und ohne etwas zu installieren:
package-lock.json, npm-shrinkwrap.json, yarn.lock (v1 und Berry), pnpm-lock.yaml und bun.lock — einschließlich transitiven Abhängigkeiten, mit der vollständigen Kette (z. B. eslint → file-entry-cache → flat-cache → [email protected]);node_modules (Name + SHA-256-Hash der bekannten Artefakte);SUSPECT markiert, niemals ohne Hash bestätigt;.claude/settings.json und .vscode/tasks.json, temporäre Artefakte (bun-dl-*);keyv/cacheable-Familien wurde kompromittiert; der Angreifer veröffentlichte neue Versionen mit einem "preinstall": "node setup.mjs"-Hook — Code, der vor der Installation des Pakets läuft, mit den Rechten dessen, der npm install ausgeführt hat.setup.mjs lädt die Bun-Laufzeit von GitHub herunter und führt die Payload darin aus — eine Umgehung von Tools, die nur node-Prozesse überwachen.Math_Symbol.js (~728 KB, verschleiert) stiehlt Anmeldedaten: AWS-Instance-Metadaten, AWS/GCP/Azure-Schlüssel, Vault-Tokens, Kubernetes-Service-Accounts, GitHub-Actions-Secrets, npm-Tokens sowie einen generischen Regex-Sweep nach privaten Schlüsseln und Bearer-Tokens auf der Platte.npm install/npm ci mit aktivierten Lifecycle-Skripten ausgeführt hat. Mit --ignore-scripts lief der Hook nicht..claude/settings.json (SessionStart) und .vscode/tasks.json (folderOpen), die den Loader beim Öffnen des geklonten Ordners ausführen — kein npm install, nichts installiert. Das betrifft Personen, die das Repo zur Untersuchung des Vorfalls geklont haben, sowie KI-Coding-Agenten, die das Verzeichnis geöffnet haben — einer der ersten öffentlichen Fälle von AI-Agent-Hooks (.claude/) als Lieferketten-Vektor.Das Implantat installiert einen „Watcher" (gh-token-monitor), der von einem LaunchAgent (macOS) oder einem systemd-User-Service + loginctl enable-linger (Linux) am Leben gehalten wird. Alle 60 Sekunden validiert er das gestohlene GitHub-Token gegen die API. Solange das Token funktioniert, passiert nichts. Wenn die Antwort 4xx wird — also in dem Moment, in dem Sie das Token sperren —, führt er per eval den Inhalt von ~/.config/gh-token-monitor/handler aus: einen beliebigen Befehl, den der Angreifer remote definiert hat. Die öffentliche Analyse weiß nicht, was er enthält — es könnte Datenvernichtung, erneutes Implantieren, Ransomware oder nichts sein. Das Risiko ist nicht abschätzbar; genau deshalb ist die Reihenfolge der Maßnahmen absolut.
Drei Eigenschaften, die das Vorgehen verändern:
host-Modus davor.[email protected] wurde mit einer bestandenen SLSA-Attestierung ausgeliefert. Provenance bezeugt die Build-Integrität, nicht die Quelle: Der legitime Workflow kompilierte bereits trojanisierten Code.package.json (der preinstall-Hook) und in zwei neuen Dateien, die dem Paket hinzugefügt wurden (setup.mjs, Math_Symbol.js).eslint → file-entry-cache → flat-cache → keyv. Deshalb zeigt der Scanner die Kette bei jedem Befund.scan.mjs hat die folgenden Eigenschaften — wichtig für alle, die auf einen Lieferketten-Zwischenfall reagieren:
npm install. Prüfen Sie das gesamte scan.mjs in 15 Minuten, bevor Sie es ausführen.--update (Herunterladen eines frischen IOC-Manifests), explizit und optional.docker run --network=none oder eine isolierte Maschine: einfach scan.mjs + iocs.json kopieren.Voraussetzung: Node.js ≥ 18 (jede Maschine mit npm hat es bereits). Laden Sie die beiden Dateien herunter — scan.mjs + iocs.json — und das war's: keine Installation.
Hinweis: Wenn Sie dieses gesamte Repository geklont haben, enthält der Ordner
fixtures/inerte IOCs, die in den Tests verwendet werden (echte Namen und Versionen, Dummy-Inhalt — keine Malware). Der Scanner überspringt ihn automatisch und warnt in der Ausgabe; Befunde daraus erscheinen nur, wenn Sie ihn absichtlich scannen.
Es gibt zwei Betriebsmodi, die unterschiedliche Fragen beantworten, und das entscheidet, wo Sie ausgeführt werden:
repo-Modus liest Lockfiles und node_modules — und Lockfiles leben in Git, also kann er zentralisiert werden: Eine Person scannt jedes Repository im Unternehmen.host-Modus sucht nach dem Implantat (Watcher, LaunchAgent/systemd, IDE-Hooks), das auf der Maschine lebt, auf der der Code ausgeführt wurde — das ist nicht in Git und kann nicht zentralisiert werden.node scan.mjs repo /pfad/mit/allen/repos --json=result.json --html=report.html
Beantwortet in Minuten die Frage „welche Projekte sind exponiert", ohne jemanden einzubeziehen. Akzeptiert mehrere Pfade; durchsucht Unterverzeichnisse (Monorepos und Workspaces inklusive).
Identifizieren Sie für jedes Projekt mit einem Befund, wer es seit 2026-08-04 09:35 UTC angefasst hat (git log, CI-Logs). Diese Personen führen auf ihrer Maschine aus:
node scan.mjs # aktuelles Verzeichnis + Host, in ~30 Sekunden
Im Umfang: alle, die (a) npm install/npm ci im Zeitfenster ausgeführt haben; oder (b) das Repo lediglich geklont und den Ordner in VS Code oder einem KI-Agenten geöffnet haben — Vektor B benötigt keine Installation.
Da die Kosten bei ~30 Sekunden liegen und der Trichter lecken kann (ein versehentliches Klon, ein privates Projekt), ist die sicherste interne Anweisung: jeder Entwickler führt node scan.mjs einmal aus und sendet das --json/--html an AppSec. Das Senden ist bewusst manuell — der Scanner hat keine Telemetrie (Null Egress).
Höchste Priorität: Hier leben die wertvollsten Anmeldedaten. Hier hat der Scanner zwei verschiedene Rollen — eine für die Vergangenheit, eine für die Zukunft:
Die Vergangenheit aufarbeiten — scannen Sie den Runner NICHT, um zu entscheiden. Die Frage „wurde dieser Runner getroffen?" wird nicht durch einen Scan beantwortet: Wenn irgendein Job seit 08-04 eine betroffene Version ohne --ignore-scripts installiert hat, waren die Anmeldedaten in diesem Moment bereits gestohlen, und der Runner-Host bewahrt selten Beweise auf (ephemere Runner zerstören den Container am Ende des Jobs; der Watcher räumt sich in ~24h selbst weg). Was die Frage beantwortet, sind die Lockfiles aus Schritt 1 und die CI-Logs. Wenn die Antwort „ja, er hat installiert" lautet: den Runner neu aufbauen und seine Secrets rotieren — Runner sind ephemer, es gibt keinen Grund, sie zu bereinigen.
Prävention für die Zukunft — JA, führen Sie es in der Pipeline aus. Fügen Sie den Scanner als Build-Schritt im repo-Modus hinzu, nach dem Checkout und vor npm install. Er untersucht nicht den Runner-Host — er untersucht den Code, der gleich installiert wird, und der Exit-Code lässt den Build fehlschlagen, bevor der bösartige preinstall überhaupt eine Chance hat:
# Beispiel (GitHub Actions / GitLab CI — anpassen):
- run: node scan.mjs repo . --json # exit 0 sauber · 1 Befunde · 2 COMPROMISED
- run: npm ci --ignore-scripts # läuft nur, wenn der vorherige Schritt bestanden hat
node scan.mjs # aktuelles Verzeichnis + Host scannen
node scan.mjs repo /pfad/a /pfad/b
node scan.mjs host # nur Persistenz/Implantate auf der Maschine
node scan.mjs repo . --json=result.json --html=report.html
node scan.mjs --update # iocs.json aktualisieren (die einzige Netzwerkoperation)
--html erzeugt einen eigenständigen Bericht mit Zeitstempel, Hostname, IOC-Manifest-Version und dem SHA-256 des Scanners selbst — verwendbar als Anhang zu einer Vorfallsmeldung und als Prüfpfad (Audit-Trail).
Noch keine Anmeldedaten sperren oder rotieren — das ist der Auslöser der Falle. Die Abfolge:
1. ISOLIEREN — die Maschine vom Netzwerk trennen. Das ist sicher: Ohne HTTP-Antwort gibt es kein 4xx, die Falle löst nicht aus, und die Exfiltration stoppt. Nicht ausschalten (flüchtiger Speicher ist Beweismittel).
2. SICHERN (PRESERVE) — bevor Sie etwas löschen (der Watcher zerstört sich in ~24h selbst):
mkdir -p /tmp/evidence && cp -r ~/.config/gh-token-monitor /tmp/evidence/ 2>/dev/null
cp /tmp/gh-token-monitor.*.log /tmp/evidence/ 2>/dev/null
shasum -a 256 /tmp/evidence/* 2>/dev/null
Die Datei handler ist der Befehl des Angreifers, der ausgeführt würde — führen Sie sie nicht aus, fügen Sie sie nicht in eine Shell ein; behandeln Sie sie als inerten Text. Die Datei started_at begrenzt das Expositionsfenster (der Prüfer und die Aufsichtsbehörde werden danach fragen).
3. BESEITIGEN (ERADICATE) — zuerst den Watcher-Prozess beenden, dann:
# macOS
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.user.gh-token-monitor.plist
rm -f ~/Library/LaunchAgents/com.user.gh-token-monitor.plist
# Linux
systemctl --user disable --now gh-token-monitor.service
loginctl disable-linger "$USER"
rm -f ~/.config/systemd/user/gh-token-monitor.service
# beide
rm -rf ~/.config/gh-token-monitor ~/.local/bin/gh-token-monitor.sh /tmp/bun-dl-*
Entfernen Sie außerdem die bösartigen Hooks aus .claude/settings.json und .vscode/tasks.json, die Dateien setup.mjs/Math_Symbol.js/math_init.js und leeren Sie die Caches (~/.npm/_cacache, pnpm, yarn). Führen Sie node scan.mjs host erneut aus, bis es sauber zurückkommt.
4. ROTIEREN — erst wenn alle betroffenen Hosts bereinigt und verifiziert sind (ein einziger aktiver Watcher reicht, um den Auslöser zu feuern). Zuerst das npm-Token sperren (stoppt die Verbreitung des Wurms); dann GitHub (PATs, Deploy-Keys), AWS/GCP/Azure, Vault, Kubernetes, CI-Secrets — und jedes Secret, das auf der Platte lag, denn es gab einen Regex-Sweep.
5. PRÜFEN (AUDIT) — der Wurm handelt in Ihrem Namen: Suchen Sie in Ihren Organisationen nach Repositories mit der Beschreibung Shai-Hulud: Here We Go Again, nach npm-Versionen, die seit 08-04 unerwartet veröffentlicht wurden (deprecaten und Nutzer informieren), und nach der Verwendung der Anmeldedaten in CloudTrail/Audit-Logs innerhalb des started_at-Fensters.
Danach: node_modules löschen, aus einer sauberen Lockdatei mit --ignore-scripts neu installieren. CI-Runner und Hosts mit bestätigter Ausführung: immer von Grund auf neu aufbauen — beliebiger Code wurde ausgeführt, und die Liste der bekannten Artefakte ist keine Garantie für Vollständigkeit.
Regulierte Institute (BR): Eine bestätigte Kompromittierung mit Zugriff auf Anmeldedaten kann Meldepflichten auslösen (Res. CMN 4.893/2021, Res. BCB 85/2021; LGPD Art. 48, wenn personenbezogene Daten betroffen sind). Dokumentieren Sie die Zeitleiste in UTC —
started_at, Erkennung, Eindämmung, Beseitigung, Rotation — und bestätigen Sie Fristen mit der Rechtsabteilung/Compliance.
Der Vorfall ist aktiv und die Liste wächst. Um das neueste Manifest zu ziehen:
node scan.mjs --update # die einzige Operation, die das Netzwerk berührt
--update holt iocs.json aus diesem Repository (securest8/npm-incident-response), niemals von einem Dritten — Securest8 ist das Kuratierungs-Gate. Sie erhalten, was hier zuletzt veröffentlicht wurde.
Die Paketliste stammt aus dem öffentlichen Wiz-Feed; die Hashes, C2-Domains, Persistenz-IOCs und sicheren Versionen sind statisch und in tools/gen-iocs.mjs kuratiert. Ein Schnappschuss der Wiz-CSV liegt unter tools/keyv-packages.csv für Reproduzierbarkeit und Offline-Läufe.
node tools/gen-iocs.mjs # die neueste Wiz-CSV holen, iocs.json neu erzeugen + Schnappschuss aktualisieren
node tools/gen-iocs.mjs --offline # aus dem eingecheckten Schnappschuss neu erzeugen, kein Netzwerk
node tools/gen-iocs.mjs --allow-shrink # eine Paketanzahl unterhalb des Schnappschusses zulassen (standardmäßig geschützt)
Der Generator ist idempotent: Er behält die vorhandene manifest_version bei, schreibt die Datei nicht neu, wenn sich nichts Wesentliches geändert hat, und weigert sich, ein leeres oder geschrumpftes Manifest zu schreiben (Schutz vor einem abgeschnittenen/veränderten Upstream-Feed).
Automatisierung: .github/workflows/update-iocs.yml führt den Generator täglich (und auf Abruf) aus und committet nur dann nach main, wenn sich die IOCs tatsächlich ändern — sodass das --update der Anwender dem Wiz-Feed innerhalb von etwa einem Tag folgt, mit der vollständigen Historie in Commits nachvollziehbar. Stellen Sie es auf einen Pull-Request-Schritt um (im Workflow vermerkt), sobald sich der Vorfall abkühlt und Sie stattdessen eine manuelle Zusammenführung wünschen.
node test/run-tests.mjs # 15 Assertions gegen fixtures/demo-repo
fixtures/demo-repo ist ein Test-Repository mit inerten IOCs (echte Namen und Versionen, Dummy-Inhalt — keine Malware). Beim Scannen des eigenen Repository des Scanners wird der Ordner fixtures/ automatisch übersprungen (mit einer Warnung in der Ausgabe); die Tests scannen ihn, indem sie den Pfad explizit übergeben.
Ein Werkzeug für einen einzelnen Vorfall, gebaut für schnelle Triage während des aktiven Fensters des Angriffs — kein Ersatz für Socket, Snyk oder Ähnliches. Forschung und IOCs: Socket.dev, Wiz Research (öffentliche CSV), Kodem Security.
Gepflegt von Securest8. MIT-Lizenz.
| Stufe | Bedeutung | Aktion |
|---|
COMPROMISED | Bösartige Version in node_modules installiert, Payload per Hash bestätigt oder ein Persistenz-Implantat gefunden | Den Host als kompromittiert behandeln; der Reihenfolge der Maßnahmen folgen — das Implantat vor dem Rotieren der Anmeldedaten bereinigen |
EXPOSED | Bösartige Version in einer Lockdatei fixiert, kein Hinweis auf Ausführung | Sichere Version pinnen, node_modules löschen, mit --ignore-scripts neu installieren |
AT_RISK | Bereich (^/~) in package.json, der eine bösartige Version zulässt | Exakte Version pinnen oder am Registry-Proxy blockieren |
SUSPECT | Heuristik (Wurm-Dateiname mit abweichendem Hash, verdächtiges Lifecycle-Skript) | Manuell prüfen — könnte eine neue Variante oder ein Fehlalarm sein |
INFO | Vektor vorhanden, aber kein IOC (z. B. eine generische folderOpen-Aufgabe) | Prüfen |