Wie Shai-Hulud Entwickler-Rechner infizierte und den Zugriff auf eine GitHub-Organisation übernahm: Postmortem
(trigger.dev)- Ein bösartiges npm-Paket von Shai-Hulud 2.0 infizierte den Rechner eines Entwicklers und übernahm den Zugriff auf die GitHub-Organisation von Trigger.dev
- Die Infektion begann, als der Entwickler
pnpm installausführte und dabei das preinstall-Skript des Schadpakets gestartet wurde; zum Diebstahl von Zugangsdaten wurde das Tool TruffleHog verwendet - Die Angreifer klonten 669 Repositories innerhalb von 17 Stunden und versuchten anschließend innerhalb von 10 Minuten, 199 Branches per Force-Push zu überschreiben und 42 PRs zu schließen
- Pakete und Produktionssysteme wurden nicht kompromittiert; der Angriff wurde innerhalb von 4 Minuten nach der Erkennung blockiert
- Nach dem Vorfall wurde die Sicherheitsarchitektur ausgebaut, unter anderem durch das Deaktivieren von npm-Skripten, ein Upgrade auf pnpm 10, OIDC-basierte npm-Deployments und flächendeckenden Branch-Schutz
Überblick über den Angriff
- Am 25. November 2025 wurden während interner Debugging-Arbeiten in Slack ungewöhnliche Aktivitäten festgestellt: In mehreren Repositories tauchten „init“-Commits im Namen von Linus Torvalds auf
- Die Untersuchung ergab, dass der Supply-Chain-Wurm Shai-Hulud 2.0 den Rechner eines Entwicklers infiziert und GitHub-Zugangsdaten gestohlen hatte
- Berichten zufolge infizierte dieser Wurm mehr als 500 npm-Pakete und wirkte sich auf über 25.000 Repositories aus
- Die offiziellen npm-Pakete von Trigger.dev (
@trigger.dev/*, CLI) waren nicht infiziert
Zeitlicher Ablauf des Angriffs
-
- November, 04:11 UTC: Verteilung des bösartigen Pakets beginnt
- 20:27 UTC: Rechner eines Entwicklers in Deutschland infiziert
- 22:36 UTC: Erster Zugriff der Angreifer und Beginn des massenhaften Klonens von Repositories
- 15:27–15:37 UTC (25. November): Zehnminütiger destruktiver Angriff
- 15:32 UTC: Auffälligkeit erkannt und Zugriff innerhalb von 4 Minuten blockiert
- 22:35 UTC: Wiederherstellung aller Branches abgeschlossen
Infektionsablauf
- Als der Entwickler
pnpm installausführte, wurde das preinstall-Skript des bösartigen Pakets gestartet, lud TruffleHog herunter und führte es aus - TruffleHog scannte GitHub-Tokens, AWS-Zugangsdaten, npm-Tokens, Umgebungsvariablen und weitere Informationen und leitete sie nach außen weiter
- Auf dem infizierten Rechner wurden das Verzeichnis
.trufflehog-cacheund zugehörige Dateien gefunden - Das auslösende Paket wurde gelöscht und konnte daher nicht mehr zurückverfolgt werden
Aktivitäten der Angreifer
- Nach der Infektion liefen 17 Stunden lang Aufklärungsaktivitäten
- Über Infrastruktur in den USA und Indien wurden 669 Repositories geklont
- Die Entwickleraktivität wurde überwacht und der Zugriff über GitHub-Tokens aufrechterhalten
- Es wurde ein Repository mit dem Namen „Sha1-Hulud: The Second Coming“ erstellt, das vermutlich zum Speichern von Zugangsdaten diente
- Danach folgten 10 Minuten destruktiver Aktionen
- In 16 Repositories wurde versucht, 199 Branches per Force-Push zu überschreiben
- 42 PRs wurden geschlossen; einige Versuche wurden durch Branch-Schutzregeln blockiert
- Alle Commits erschienen im Format „Linus Torvalds <email> / init“
Erkennung und Reaktion
- Die Auffälligkeiten wurden in Echtzeit über Slack-Benachrichtigungen erkannt
- Innerhalb von 4 Minuten wurde der GitHub-Zugriff des kompromittierten Kontos gesperrt; anschließend wurden Zugriffe auf alle Dienste wie AWS, Vercel und Cloudflare entzogen
- Die Analyse der AWS-CloudTrail-Logs ergab, dass nur schreibgeschützte API-Aufrufe erfolgt waren; es gab keinen Zugriff auf Produktionsdaten
- AWS erkannte unabhängig davon verdächtige Aktivitäten im Zusammenhang mit Shai-Hulud und verschickte eine Warnung
Schaden und Wiederherstellung
- 669 geklonte Repositories, 199 Branches mit Force-Push, 42 geschlossene PRs
- Die Wiederherstellung war schwierig, da GitHub serverseitig kein reflog bereitstellt; mithilfe der Event API und lokaler reflogs konnte jedoch innerhalb von 7 Stunden alles wiederhergestellt werden
- npm-Pakete und Produktionsinfrastruktur wurden nicht kompromittiert
Offenlegung eines GitHub-App-Schlüssels
- Während der Untersuchung wurde im Papierkorb des Entwickler-Laptops ein privater Schlüssel einer GitHub-App gefunden
- Dieser Schlüssel hatte Lese-/Schreibrechte für Kunden-Repositories und wurde sofort rotiert
- Da die Datenbank mit den Installations-IDs nicht kompromittiert wurde, gibt es keine Belege für Zugriffe auf Kunden-Repositories; vollständig ausschließen lässt sich dies jedoch nicht
- Beim GitHub-Support wurden zusätzliche Logs angefordert und Kunden per E-Mail informiert
Technische Analyse von Shai-Hulud
- Beim Ausführen von
setup_bun.jswird die Bun-Laufzeitumgebung installiert und im Hintergrundbun_environment.jsgestartet - Mit TruffleHog werden Zugangsdaten im Verzeichnis
$HOMEgesammelt - Die gesammelten Daten (
contents.json,cloud.json,truffleSecrets.jsonusw.) werden in dreifach base64-kodierter Form in ein zufälliges GitHub-Repository hochgeladen - Falls ein npm-Token vorhanden ist, verändert und veröffentlicht der Wurm Pakete des kompromittierten Kontos erneut, um sich weiterzuverbreiten
- Falls keine Zugangsdaten vorhanden sind, wird versucht, das Home-Verzeichnis zu löschen
- Dateien als Infektionsindikatoren:
setup_bun.js,bun_environment.js,.trufflehog-cache/usw.
Maßnahmen zur Härtung der Sicherheit
- Vollständiges Deaktivieren von npm-Skripten (
ignore-scripts=true) - Upgrade auf pnpm 10: Skripte standardmäßig deaktiviert,
minimumReleaseAge(3 Tage) gesetzt, um die Installation neuer Pakete zu verzögern - Einführung von OIDC-basierten npm Trusted Publishers, um langfristige Tokens zu entfernen
- Branch-Schutz für alle Repositories aktiviert
- Granted für AWS SSO eingeführt, Sitzungs-Tokens verschlüsselt
- In GitHub Actions wurde für Workflow-Ausführungen von externen Mitwirkenden eine Freigabe verpflichtend gemacht
Lehren für andere Teams
- Die Ausführung beliebigen Codes während der npm-Installation ist selbst Teil der Angriffsfläche
ignore-scripts=truesollte gesetzt und nur notwendige Pakete per Whitelist zugelassen werden- Mit pnpm minimumReleaseAge lässt sich die Installation neuer Pakete verzögern
- Branch-Schutz und OIDC-basierte Deployments sind unverzichtbare Sicherheitsmaßnahmen
- Langfristige Zugangsdaten sollten nicht auf lokalen Rechnern gespeichert werden; Deployments sollten nur über CI erlaubt sein
- Das Rauschen der Slack-Benachrichtigungen war der Schlüssel zur Erkennung
Die menschliche Seite
- Den infizierten Entwickler trifft keine Schuld; der Schaden entstand allein dadurch, dass
npm installausgeführt wurde - Während des Angriffs wurden Spuren gefunden, dass das Konto automatisch Hunderte zufällige Repositories mit einem Stern markiert hatte
- Der Vorfall offenbart keine individuelle Nachlässigkeit, sondern eine strukturelle Schwachstelle des Ökosystems
Kennzahlen in der Zusammenfassung
- Von der ersten Infektion bis zum ersten Angriff: etwa 2 Stunden
- Dauer des aufrechterhaltenen Zugriffs durch die Angreifer: 17 Stunden
- Dauer der destruktiven Aktionen: 10 Minuten
- Bis zur Erkennung: 5 Minuten, bis zur Blockierung: 4 Minuten
- Bis zur vollständigen Wiederherstellung: 7 Stunden
- Geklonte Repositories: 669 / Betroffene Branches: 199 / Geschlossene PRs: 42
Weiterführende Ressourcen
- Socket.dev: Shai-Hulud Strikes Again V2
- Analyseberichte von PostHog, Wiz, Endor Labs und HelixGuard
- Dokumentation zu npm Trusted Publishers, pnpm
onlyBuiltDependencies,minimumReleaseAge, Granted
3 Kommentare
Bei pnpm war die Struktur wohl so, dass
post-installstandardmäßig einzeln erlaubt werden musste, aber am Ende scheint es so zu sein, dass Entwickler es auch unbewusst erlauben.Ich verstehe es so, dass
npmstandardmäßig ausgeführt wird und man zur Stärkung der Sicherheit aufpnpmumgestellt hat, um den Standard standardmäßig zu deaktivieren.Hacker-News-Kommentare
npm installauszuführen ist nicht fahrlässigDas Problem ist ein Ökosystem, das bei der Paketinstallation die Ausführung beliebigen Codes erlaubt
Das grundlegende Sicherheitsversagen ist jedoch, einen Paketmanager zu verwenden, über den Dritte ohne jede Verifikation Code in mein Produkt einschleusen können
Am Ende sind wir grenzenlos vom guten Willen und den Fähigkeiten der Paketmanager und ihrer Betreiber abhängig
Außerdem scheint OP anzudeuten, dass Zugangsdaten im Klartext im Dateisystem gespeichert wurden
Man kann auf Sprachebene Strukturen schaffen, die Code darauf beschränken, Eingaben zu lesen, Ressourcen zu verbrauchen und nur typkorrekte Ausgaben zu erzeugen
Das löst nicht das gesamte Supply-Chain-Problem, reduziert aber die Angriffsfläche erheblich
So nach dem Motto: „Es ist nicht falsch, wenn eine Person solche Tools benutzt, aber wenn es alle tun, ist das Ökosystem das Problem“
Dass viele Entwickler-Tools bereits sicherheitsanfällig sind, wurde oft genug bewiesen
Wenn es einem wirklich wichtig ist, sollte man das auch durch Handeln zeigen
Bei VS Code war es lästig, dass man für jede kleine Zusatzfunktion ein Plugin installieren musste, von dem man nicht einmal weiß, wer es gebaut hat
Am Ende läuft man doch immer auf eine Struktur hinaus, in der man zwangsläufig vertrauenswürdigen Code ausführt
netrc-DateiWenn man Git über HTTP nutzt, gibt es fast immer einen solchen Klartext-Pfad für Zugangsdaten
Der pnpm-Maintainer hat schon vor einem Jahr vorgeschlagen, Post-Install-Skripte standardmäßig zu blockieren
Für Nutzer wäre das unbequem, aber langfristig eine Änderung, für die letztlich alle dankbar wären
Zugehöriger PR: pnpm/pnpm#8897
Wieder einmal ein Fall, in dem Bequemlichkeit die Sicherheit schlägt
Es hieß, „die Datenbank wurde nicht kompromittiert“, aber wenn Angreifer Zugriff auf AWS und Secrets hatten, würde ich das bereits als Kompromittierung werten
Wenn die Möglichkeit des Zugriffs bestand, sollte es als kompromittiert gelten
Nachdem Malware einmal ausgeführt wurde, ist die Rückverfolgung der Herkunft fast unmöglich
Auch
pnpm installläuft normal durch, daher ist das schwer zu erkennenMit einem EDR wie Sentinel One oder CrowdStrike hätte man vermutlich deutlich mehr Ermittlungsansätze gehabt
Der Teil „insgesamt 669 Repos wurden geklont“ fiel mir auf
Ich frage mich, ob es normal ist, dass ein Unternehmen mit weniger als 100 Mitarbeitenden über 600 Repos besitzt
Die Anzahl der Repos hängt stärker von Unternehmensalter und Projektlebensdauer ab als von der Teamgröße
pnpm hat die automatische Ausführung von Lifecycle-Skripten wie
preinstallbereits gestoppt, also wurde wohl eine alte Version verwendetSiehe zugehöriger PR
postinstall-Skript hattepnpm blockiert Skripte von Abhängigkeiten, führt aber Skripte auf Projektebene weiterhin aus
Danke für die transparente Veröffentlichung der Post-Mortem-Analyse
Solche Fälle sind für die gesamte Branche wichtig
Ich frage mich, ob sich der Angriffsverkehr vom normalen Entwicklerverkehr unterscheiden ließ
Wir versuchen ebenfalls, die Egress-Filterung in der Dev-Umgebung zu verschärfen, aber
npm installbricht dabei ständig kaputtIch denke über die Git-Sicherheit auf meinem persönlichen Laptop nach
Derzeit pushe ich mit lokal gespeicherten SSH-Schlüsseln
Ich habe dort auch Administratorrechte, was riskant ist. Ich frage mich, wie man das sicherer machen kann
So lassen sich Signaturschlüssel und Zugriffsschlüssel trennen, und das Admin-Konto kann separat verwaltet werden
Dokumentation dazu: 1Password SSH Agent, Git Commit Signing, GitHub OAuth, GitHub CLI Login
Das hat den Vorteil, dass der Schlüssel nicht nach außen exfiltriert werden kann, aber wenn Malware auf meinem Rechner läuft, bleibt es trotzdem riskant
Linux-Beispiel, macOS-Beispiel
Mit TPM oder Yubikey kann man es etwas schwerer machen, aber vollständige Abwehr ist unmöglich
Für Admin-Aufgaben ist ein separates dediziertes Gerät sicherer
gpg-agentSSH-Authentifizierung machenBeim Push oder Commit wird dann per PIN entsperrt
main-Branch blockiert werden und MFA verpflichtend ist, wird es für Angreifer schwieriger, sofort auf den Deployment-Branch zuzugreifenDer Commit unter dem Namen Torvalds war ein nach einer Infektion häufig zu sehendes Erkennungszeichen (Signature)
Das wird auch in der offiziellen Analyse von Microsoft erwähnt
Dieser Wurm war sehr auffällig, und einige Angreifer missbrauchten offengelegte Zugangsdaten zu Werbezwecken, indem sie private Repos öffentlich machten oder Readmes veränderten
Ich frage mich, ob eine solche Erkennung überhaupt möglich gewesen wäre, wenn die Angreifer ohne Sabotage still nur Informationen exfiltriert hätten