Der Code war in Ordnung. Der Herausgeber nicht
Zwei Registry-Einträge, eine Version Abstand. Axios 1.14.0 wurde von GitHub Actions über einen Trusted Publisher veröffentlicht und trägt Provenance. Axios 1.14.1, am 31. März 2026 um 00:21 UTC erschienen, wurde von Hand veröffentlicht, ohne all das. Dieser Unterschied ist die gesamte Geschichte der Axios-npm-Kompromittierung, und er stand in den Registry-Metadaten. [2]
Die Fakten in Kürze. Axios 1.14.1 und 0.30.4, im Abstand von 39 Minuten veröffentlicht, fügten eine neue Abhängigkeit hinzu, plain-crypto-js@4.2.1, deren postinstall-Skript auf jedem Rechner, der die Installation ausführte, einen Fernzugriffstrojaner ablegte. Bis 03:15 UTC entfernte npm beide Versionen. [1][2] Google ordnet die Operation mit hoher Sicherheit UNC1069 zu, einer Gruppe mit Nordkorea-Bezug; Microsoft führt den Akteur als Sapphire Sleet. [3][4]
Ich nenne das einen Angriff auf die Vertrauenskette. Ein Software-Supply-Chain-Angriff verändert den Code, den Sie ausführen, ohne eine Zeile zu ändern, die Sie geprüft haben, indem er den Auslieferungsweg einer Abhängigkeit kompromittiert. Eine Schwachstelle im Axios-Quellcode wurde nicht ausgenutzt. Jedes Glied war eine Vertrauensentscheidung, und so lässt sich der Vorfall am nützlichsten lesen. Ich habe nichts davon selbst reproduziert; die Analyse stützt sich auf den Rückblick der Maintainer, Herstellerberichte und die Registry-Einträge, Stand 7. Oktober 2026.
axios@1.14.0 published by CI
_npmUser GitHub Actions
trustedPublisher github
gitHead 46bee3dea75ef53a8eae49f3b7487e6341de6074
dist.attestations SLSA provenance v1
axios@1.14.1 2026-03-31T00:21:58Z, published by hand
_npmUser the lead maintainer's account
trustedPublisher absent
gitHead absent
GitHub commit/tag none; the release exists only on npmDie Kette begann auf einem Laptop und endete auf einem Laptop
Das erste Glied war weder npm noch GitHub. Laut Rückblick der Maintainer verschaffte sich der Angreifer über eine gezielte Social-Engineering-Kampagne und RAT-Malware Zugriff auf den Rechner des leitenden Maintainers; das lieferte die npm-Zugangsdaten, mit denen veröffentlicht wurde. [1] Ein RAT auf einem Entwicklerrechner erzeugte das Token. Das Token erzeugte einen RAT auf jedem Entwicklerrechner und CI-Runner, der die vergiftete Version installierte. Dieselbe Malware-Klasse an beiden Enden.
Die Maintainer sind deutlich: Die Veröffentlichung direkt aus einem persönlichen Konto sei ein vermeidbares Risiko gewesen, und der OIDC-Ablauf sowie die unveränderlichen Releases, die sie nun einführen, hätten vor dem Vorfall bestehen sollen. [1] Diesen Satz schätze ich. Er benennt die eigentliche Lehre, und die betrifft nicht Axios, sondern den Ort, an dem Veröffentlichungsrechte liegen.
Ich lese den Vorfall als sechs Glieder, unten dargestellt. An jedem Glied hätte eine Kontrolle die Kette brechen können, und jedes hat einen anderen Verantwortlichen. Einige gehören dem Herausgeber. Die meisten gehören Ihnen. Ich gehe sie der Reihe nach durch.
1 maintainer laptop social engineering + RAT -> npm credentials
2 registry gate direct token publish accepted -> axios 1.14.1, 0.30.4
3 decoy dependency plain-crypto-js 4.2.0 -> 4.2.1 -> new line in package.json
4 your resolver fresh install, no cooldown -> poisoned tarball fetched
5 install script postinstall: node setup.js -> dropper runs
6 runner or laptop reachable secrets, open egress -> RAT, then whatever it can reachWarum stoppte OIDC Trusted Publishing ein gestohlenes Token nicht?
Weil es nicht die einzige Tür war. Trusted Publishing lässt die Registry eine Veröffentlichung nur von einem benannten CI-Workflow annehmen, mit einer kurzlebigen OIDC-Berechtigung statt eines gespeicherten Geheimnisses. Axios-1.x-Releases liefen bereits so, weshalb 1.14.0 einen Trusted Publisher und Provenance zeigt. [2][6] Das schädliche 1.14.1 wurde direkt aus einem Konto veröffentlicht: ohne Trusted Publisher, ohne gitHead und ohne passenden Commit oder Tag im Repository. [2]
Ein guter Veröffentlichungsweg beseitigt den schlechten nicht. Solange das Paket Tokens akzeptiert, umgeht jeder, der eines besitzt, den Workflow vollständig. Geschlossen wird das durch eine Paketeinstellung: Require two-factor authentication and disallow tokens. Die npm-Dokumentation empfiehlt sie, sobald Trusted Publisher konfiguriert sind, und diese laufen unverändert weiter. [6] StepSecurity liest die fehlenden Metadaten als gestohlenes langlebiges klassisches Token; die Maintainer sagen lediglich, der Angreifer habe die Kontozugangsdaten erlangt. [1][2] In jedem Fall berührte die Veröffentlichung den Workflow nie.
Meine Meinung: OIDC einzuschalten, ohne Tokens abzuschalten, ist eine halbe Kontrolle, und viele Teams setzen nach der ersten Hälfte den Haken. Wenn Sie Pakete veröffentlichen, prüfen Sie heute beide Hälften. Wenn Sie sie nutzen, sind die Registry-Metadaten ein lesbares Signal: Eine Version, die früher von einem Trusted Publisher stammte und plötzlich nicht mehr, verdient einen Halt. Auch npm zieht auf der anderen Seite an: Ab Januar 2027 verlieren granulare Tokens, die die Zwei-Faktor-Authentifizierung umgehen, die Möglichkeit, direkt zu veröffentlichen. [7]
Eine Abhängigkeit, die niemand importiert, ist ein Befund
Der Angreifer bereitete den Boden einen Tag vorher vor. plain-crypto-js@4.2.0 erschien am 30. März um 05:57 UTC als saubere Attrappe: eine Kopie des echten crypto-js-Codes ohne Install-Hook. Ihre einzige Aufgabe war, dem Paket eine Veröffentlichungshistorie zu geben, damit es nicht brandneu wirkte. Die schädliche 4.2.1 folgte um 23:59 UTC, 22 Minuten vor axios 1.14.1. [2]
Nun das verräterische Detail. StepSecurity durchsuchte alle 86 Dateien in axios@1.14.1 und stellte fest, dass plain-crypto-js nirgends importiert oder per require eingebunden wird. [2] Der ausgelieferte Axios-Code hatte für die Abhängigkeit keine Verwendung. Zudem fehlte dem Release das husky-Skript prepare, was zu einer manuellen Veröffentlichung am normalen Release-Werkzeug vorbei passt. [2] Einfach gesagt: Ein Patch-Release fügte eine Laufzeitabhängigkeit hinzu, die das Paket nie benutzt. Bei einer solchen Zeile hält ein menschlicher Reviewer an.
Diese Prüfung würde ich zuerst automatisieren. Vergleichen Sie bei jedem Upgrade das Manifest der alten und der neuen Version. Eine neue Abhängigkeit in einem Patch-Sprung geht an einen Menschen. Dann drei Fragen: Wie alt ist das Paket, wer pflegt es, und nutzt das übergeordnete Paket es überhaupt? Die Befehle unten beantworten alle drei in unter einer Minute. Ihr Update-Bot stellt diese Fragen nicht für Sie.
# what changed in the manifest between two versions
npm diff --diff=<pkg>@<old> --diff=<pkg>@<new> package.json
# does the package actually use the new dependency?
npm pack <pkg>@<new> && tar -xzf <pkg>-<new>.tgz && grep -R "<new-dep>" package/
# how old is the new dependency, and who maintains it?
npm view <new-dep> time maintainers --jsonWarum genügte npm install, um den Rechner zu übergeben?
Weil npm bei der Installation Code ausführt. Ein Lifecycle-Skript ist ein Befehl, den ein Paket in seiner package.json deklariert und den der Paketmanager automatisch ausführt; plain-crypto-js@4.2.1 deklarierte postinstall: node setup.js. [3] Das läuft, bevor Ihre Anwendung irgendetwas importiert, weshalb auch ein Projekt, das die Bibliothek zur Laufzeit nie berührte, betroffen war. StepSecurity beobachtete den ersten Aufruf des Angreifer-Servers innerhalb von zwei Sekunden nach npm install. [2]
Die Verschleierung in setup.js ist dünner, als sie wirkt. Zeichenketten werden umgekehrt, aus Base64 dekodiert und mit dem Schlüssel OrDeR_7077 und der Konstante 333 per XOR verknüpft. [5] Die Deobfuskation von StepSecurity zeigt, dass der Schlüssel durch Number() läuft: Buchstaben werden zu NaN, XOR mit null ändert nichts, und der wirksame Schlüssel lautet 0,0,0,0,0,0,7,0,7,7. [2] Das ist ein Trick gegen String-Scanner, keine Kryptografie. Die Lehre: Statische Analyse von Installationsskripten ist ein brauchbarer Auslöser, aber eine schwache Grenze.
Danach verzweigt der Dropper nach Plattform und fordert beim Angreifer-Server eine zweite Stufe an. Der POST-Body sagt dem Server, welche er liefern soll: product0 für macOS, product1 für Windows, product2 für Linux. [2] Wohin jede landet, zeigt die Tabelle. Alle drei Varianten senden alle 60 Sekunden ein Signal, Base64-kodiertes JSON, mit dem User-Agent von Internet Explorer 8 unter Windows XP. [3] Da der RAT live nachgeladen wird, steckt im Tarball keine Schadsoftware im engeren Sinn: Das Paket ist ein Downloader. Entscheidend ist daher der ausgehende Verkehr, und ein IE8-User-Agent von einem Build-Runner ist der billigste Alarm, den Sie je schreiben werden. [3]
platform stage two lands in started by
win32 %TEMP%\6202033.ps1 VBScript; powershell.exe copied to %PROGRAMDATA%\wt.exe
darwin /Library/Caches/com.apple.act.mond Mach-O binary, chmod +x, zsh in the background
linux /tmp/ld.py nohup python3
all POST http://sfrclak[.]com:8000/6202033
body: packages.npm.org/product0 (macOS), product1 (Windows), product2 (Linux)
beacon: Base64 JSON every 60 seconds
User-Agent: mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0)
win32 persistence: HKCU Run key "MicrosoftUpdate" and %PROGRAMDATA%\system.batWarum sah der Ordner danach sauber aus?
Weil der Dropper aufräumte. Nach der Ausführung löschte setup.js sich selbst und die schädliche package.json und benannte dann eine vorbereitete package.md in package.json um. [2] Dieses Ersatzmanifest deklariert Version 4.2.0 ohne Install-Hook. Wer danach node_modules/plain-crypto-js öffnet, sieht eine harmlose 4.2.0, obwohl 4.2.1 lief. [2] Die einen Tag zuvor platzierte Attrappe zahlt sich damit ein zweites Mal aus.
Deshalb sage ich Teams: Prüfen Sie das Lockfile, nicht node_modules. Ein Lockfile-Eintrag für plain-crypto-js, in Version 4.2.0 oder 4.2.1, bedeutet, dass Ihr Installationsgraph den Angriff enthielt, und nach Googles Empfehlung gilt der Host ab dann als kompromittiert. [3] Was Sie sichern und wie Sie die Wiederherstellung belegen, steht in meinem früheren Axios-Artikel zu Build-Nachweisen. Hier bleibe ich beim Mechanismus.
Was tat der RAT? Googles Analyse nennt die Nutzlast WAVESHAPER.V2; sie beherrscht Systemaufklärung, Verzeichnislisten, beliebige Befehlsausführung und das Einschleusen von Binärdateien. [3] Das ist Zugriff, kein Diebstahl. Was ein Operator mit Zugriff auf einen Entwicklerlaptop oder CI-Runner tut, ist, die dort liegenden Tokens, Schlüssel und .env-Dateien zu lesen; wie hoch der Einsatz ist, zeigt OpenAI: Ein GitHub-Actions-Workflow im macOS-Signierprozess führte axios 1.14.1 aus, mit Signaturzertifikat und Notarisierungsmaterial in Reichweite. OpenAI fand keine Hinweise auf Zugriff auf Nutzerdaten oder veränderte Software und tauschte das Zertifikat vorsorglich aus. [12]
Wer muss sich bei diesem Fall wirklich sorgen?
Um einen kleineren und konkreteren Teil Ihrer Landschaft, als die Schlagzeilen nahelegten. Die ausgelieferte Anwendung ist nicht das Opfer. Nichts importiert plain-crypto-js, ein aus axios 1.14.1 gebautes Frontend-Bundle hatte also keinen Grund, die Nutzlast zu enthalten; das Opfer ist die Maschine, die die Installation ausführte. [2] Auch das Zeitfenster war kurz: von 00:21 UTC bis zur Entfernung um 03:15 UTC, unter drei Stunden. [1]
Damit es Sie traf, mussten drei Dinge zutreffen. Eine frische Installation löste in diesem Fenster axios 1.14.1 oder 0.30.4 auf, also eine Installation ohne passendes Lockfile oder ein Update-Bot, der binnen Stunden mergte. Installationsskripte durften laufen, was die Standardeinstellung war. Und die Maschine hielt etwas, das sich zu erreichen lohnte. Während des Vorfalls riet Microsoft, exakte Versionen festzuschreiben und automatische Abhängigkeits-Bots anzuhalten. [4] Wenn Sie diese drei Fragen für jeden Runner und Laptop beantworten können, kennen Sie Ihre Exposition. Wenn nicht, ist das der eigentliche Befund.
Welche Kontrolle bricht welches Glied?
Eine pro Glied, und die Tabelle unten ist das ganze Modell. Um einen bestimmten Angriff zu stoppen, genügt es, ein Glied zu brechen. Welches Glied der nächste Angreifer heil lässt, können Sie nicht wählen, also sichern Sie mehr als eines ab.
Glied fünf hat sich dieses Jahr verändert. npm 12, seit dem 8. Juli 2026 allgemein verfügbar, führt die Skripte preinstall, install und postinstall von Abhängigkeiten nur noch aus, wenn das Projekt sie erlaubt; die Freigaben stehen in einer allowScripts-Liste, die Sie einchecken. [7][8] Unter npm 12 wäre der Hook von plain-crypto-js ohne ausdrückliche Freigabe nicht gelaufen. Prüfen Sie aber, was Ihre CI tatsächlich einsetzt: Das Image node:24-alpine liefert Stand 7. Oktober 2026 npm 11.19.0 aus, und npm 11 führt Skripte von Abhängigkeiten standardmäßig weiter aus. pnpm blockiert sie seit Version 10 standardmäßig, und selbst diese Einstellung musste geprüft werden: Vor Version 10.26.0 konnten Git-Abhängigkeiten bei der Installation trotzdem Code ausführen, eine Schwachstelle hoher Schwere. [9][10] Eine Einstellung wird zur Grenze, wenn Sie gesehen haben, dass sie etwas blockiert.
Auch für Glied vier gibt es eine günstige Kontrolle. npm 11.10.0 führte im Februar 2026 min-release-age ein, eine Wartezeit in Tagen. Bei pnpm wird minimumReleaseAge in Minuten angegeben und beträgt in pnpm 11 standardmäßig einen Tag. [10][11] Die vergifteten Axios-Versionen lebten unter drei Stunden, selbst eine eintägige Wartezeit überdauert sie also. Eine Wartezeit ist eine Wette darauf, dass jemand die Bedrohung in diesem Fenster bemerkt, und gegen einen geduldigen Angreifer hilft sie nicht. Ergänzen Sie sie um trustPolicy: no-downgrade von pnpm, das eine Version ablehnt, deren Vertrauensstufe gegenüber früheren Releases gesunken ist. [10] Das ist genau auf die Form von 1.14.1 zugeschnitten, doch ich habe diesen Angriff nicht dagegen laufen lassen, da 1.14.1 aus der Registry verschwunden ist. Testen Sie es an einem Paket, das Sie selbst kontrollieren.
link control owner
1 maintainer laptop no publish credential on a workstation publisher
2 registry gate trusted publisher + "disallow tokens" publisher
3 decoy dependency diff the manifest; question new deps in patches you
4 fresh resolution npm ci + lockfile; min-release-age / pnpm cooldown you
5 install script npm 12 allowScripts; pnpm 10+; --ignore-scripts you
6 reachable secrets short-lived creds, split jobs, egress allow-list you
+ trust downgrade pnpm trustPolicy: no-downgrade youAcht Prüfungen vor Ihrem nächsten npm install
Hier ist die Fassung dieses Artikels, nach der Sie handeln können. Beginnen Sie mit den eigenen Repositories, dann den CI-Images, dann den Paketen, die Sie veröffentlichen. Die Liste unten ist danach geordnet, wie schnell sie sich auszahlt.
Wenn ich eine Codebasis prüfe, gehören Abhängigkeitsmanifest, Lockfile und Release-Konfiguration zum Umfang, weil die entscheidenden Vertrauensgrenzen oft dort liegen und nicht im Code, den Sie geschrieben haben. Diesen Teil der Kette erreicht ein gewöhnlicher Penetrationstest in der Regel nicht. Wenn Sie den Blick eines Angreifers darauf wollen, wie Ihr Produkt seinen Abhängigkeiten und seinem Release-Weg vertraut, schildern Sie mir, was Sie ausliefern. Wenn Sie glauben, betroffen gewesen zu sein, sichern Sie Beweise vor dem Neuaufbau und holen Sie einen spezialisierten Incident Responder dazu: Das ist eine andere Aufgabe als meine.
1 lockfile grep -nE "plain-crypto-js" package-lock.json # 4.2.0 or 4.2.1 means act
2 CI install npm ci, never npm install; no bot merges within hours of a release
3 scripts npm 12: review allowScripts | older npm: --ignore-scripts, allow by name | pnpm 10+: allowBuilds
4 cooldown npm config set min-release-age 3 | pnpm: minimumReleaseAge
5 trust pnpm: trustPolicy: no-downgrade
6 publishing your packages: trusted publisher on, "disallow tokens" on, no publish token on a laptop
7 egress build runners: default-deny outbound; alert on node spawning curl, powershell or python3
8 secrets the install job holds no deploy, signing or cloud credentialsQuellen
- [1] Axios-Maintainer: Rückblick auf den Vorfall
- [2] StepSecurity: Axios auf npm kompromittiert, technische Analyse
- [3] Google Threat Intelligence: Akteur mit Nordkorea-Bezug greift axios an
- [4] Microsoft: Gegenmaßnahmen zur Axios-npm-Kompromittierung
- [5] JFrog Security Research: Analyse der Schadsoftware plain-crypto-js
- [6] npm-Dokumentation: Trusted Publishers
- [7] GitHub-Changelog: npm-Sicherheit bei der Installation und Abschaffung von 2FA-Bypass-Tokens
- [8] npm-Dokumentation: Installationsskripte in npm 12
- [9] pnpm-Sicherheitshinweis GHSA-379q-355j-w6rj: Umgehung der Lifecycle-Skript-Sperre
- [10] pnpm-Dokumentation: Sicherheitseinstellungen für die Lieferkette
- [11] npm-CLI-Release v11.10.0: min-release-age
- [12] OpenAI: Kompromittierung des Entwicklerwerkzeugs axios
Soll das auch in Ihrem Produkt geprüft werden?
Security Code-Review