Die Kurzfassung
Was: Am 20. August 2026 nutzte ein Angreifer das crates.io-Konto eines legitimen Betreuers, um bösartige Versionen von „arrayref“ 0.3.10, „append-only-vec“ 0.1.9 und „internment“ 0.8.7 zu veröffentlichen. Der Bibliothekscode in allen drei Versionen war einwandfrei. Jede der Bibliotheken fügte eine Abhängigkeit von „proc-macro1“ hinzu, einem Typosquat von David Tolnays „proc-macro2“, dessen „build.rs“-Datei die Angreiferadressen aus Base64 rekonstruierte, eine Binärdatei der zweiten Stufe über TLS ohne Zertifikatsprüfung herunterlud und diese während der Kompilierung ausführte.
Auswirkungen: „arrayref“ verzeichnet auf crates.io insgesamt 245 Millionen Downloads, davon etwa 53,7 Millionen in den letzten 90 Tagen, und 403 Crates führen es als direkte Abhängigkeit auf. Die meisten Teams haben es nie ausgewählt, da es als Teil von Kryptografie-, Grafik- und Blockchain-Bibliotheken mitgeliefert wird. Die Analyse von StepSecurity beziffert das Zeitfenster, in dem „arrayref“ angreifbar war, auf 86 Minuten – von der Veröffentlichung um 07:15 Uhr UTC bis zur Entfernung aus dem Index um 08:41 Uhr UTC. „append-only-vec“ und „internment“ sind mit etwa 4,5 Millionen bzw. 14,4 Millionen Downloads insgesamt weniger verbreitet und wurden im gleichen Zeitraffel entfernt.
Status: crates.io hat sechs vom Angreifer erstellte Crates (proc-macro1, proc-macro-en, aovine, arone, aronenao, tinymember) gelöscht, die drei schädlichen Versionen entfernt und das Konto des Betreuers gesperrt. Das Rust Security Response Team gibt an, dass der „Computer oder die Anmeldedaten des Autors wahrscheinlich kompromittiert sind“. Die Bereinigung des Registers säubert Ihre Systeme nicht. Cargo-Caches, Vendor-Bäume, Container-Layer, CI-Images und Spiegel können diese weiterhin enthalten. Falls ein Host an diesem Morgen einen neuen Abhängigkeitsgraphen aufgelöst hat, der eine dieser Crates erreicht haben könnte, behandeln Sie ihn als verdächtig, bis Sie ihn überprüft haben.
Die Langfassung
Bin ich betroffen?
Hier stellen sich zwei Fragen, auf die es unterschiedliche Antworten gibt. Das Zeitfenster für den Erwerb der schädlichen Versionen schloss sich, als crates.io sie an jenem Morgen entfernte. Das Zeitfenster für ihre Ausführung hingegen nicht. Das Entfernen und Löschen beseitigt nichts, was sich bereits auf der Festplatte befindet, sodass ein Cargo-Cache, ein Vendor-Abhängigkeitsbaum, ein Registry-Mirror oder eine Container-Ebene, die eine dieser Versionen beibehalten hat, diese weiterhin in einen späteren Build einspeisen kann. Der Erwerb war zeitlich begrenzt. Die Ausführung ist eine Eigenschaft Ihrer Rechner.
Bei der Erfassung ist die betroffene Gruppe kleiner, als die Download-Zahlen vermuten lassen. Eine festgelegte `Cargo.lock`-Datei legt genaue Versionen fest, und Cargo hält die Abhängigkeiten, soweit möglich, an die Versionen in der Lockdatei gebunden. Daher blieb ein Projekt, das anhand einer unveränderten Lockdatei erstellt wurde, bei `arrayref 0.3.9` und hat `0.3.10` nie gesehen. Das gilt auch für einen sauberen Container. Ein neues Dateisystem ändert nichts, wenn der Build eine gültige Lockdatei kopiert – was dem üblichen Muster entspricht.
Was die Teams tatsächlich gefährdete, war die erneute Auflösung, während die schädlichen Versionen noch aktiv waren – und zwar in einem Abhängigkeitsgraphen, der eine der betroffenen Crates erreichen konnte. Jeder, der in dieser Situation „cargo update“ ausführte, war gefährdet, ebenso wie jeder, dessen Abhängigkeitsbot an diesem Morgen einen Pull-Request für ein Upgrade öffnete und zusammenführte. Das Gleiche galt für jeden, dessen Build ohne vorhandene Lockdatei lief – was das eigentliche Risiko bei Containern darstellt, da ein Dockerfile, das nur das Manifest kopiert, oder eine .dockerignore-Datei, die Cargo.lock ausschließt, jedes Mal von Grund auf neu auflöst. Das Gleiche gilt für jeden, dessen Manifest sich an jenem Morgen geändert und einen neuen Pfad zu „arrayref“ eingeführt hat, da Cargo den betroffenen Teil des Graphen neu auflöst und eine neue Lockdatei schreibt. Und für jeden, der „cargo install“ ausgeführt hat, was eine veröffentlichte Lockdatei ignoriert, sofern man nicht die Option „–locked“ übergibt.
Bibliotheksautoren waren stärker betroffen als Anwendungsautoren, obwohl es dafür keine entsprechende Vorschrift gab. Das Cargo-Team hat die alte Richtlinie „Binärdateien verwenden Lockfiles, Bibliotheken nicht“ im August 2023 außer Kraft gesetzt, und „cargo new“ verfolgt seitdem Cargo.lock für Bibliotheken. Viele Bibliotheks-Repositorys stammen jedoch aus der Zeit vor dieser Änderung und werden in der CI weiterhin neu aufgelöst. Ein Lockfile wird zudem nicht weitergegeben. Ihr Lockfile hat keinen Einfluss auf die Nutzer Ihrer Crate, sondern nur Ihre Cargo.toml.
Beginnen Sie mit dem lokalen Cargo-Cache. Das Rust Security Response Team hat folgende Überprüfung veröffentlicht:
find ~/.cargo/registry/cache -type f -name'append-only-vec-0.1.9.crate'-o-name'arrayref-0.3.10.crate'-o-name'internment-0.8.7.crate'-o-name'proc-macro1-*.crate'-o-name'proc-macro-en-*.crate'-o-name'aovine-*.crate'-o-name'arone-*.crate'-o-name'aronenao-*.crate'-o-name'tinymember-*.crate' -print
Überprüfen Sie anschließend Ihre Lock-Dateien auf dieselben Versionen und darauf, ob überhaupt ein Eintrag für „proc-macro1“ vorhanden ist. Eine Zeile mit „proc-macro1“ in einer Cargo.lock-Datei ist bereits ein Hinweis für sich, da „proc-macro1“ vom Angreifer speziell für diese Kampagne erstellt und anschließend aus der Registrierungsdatenbank gelöscht wurde.
Was der „arrayref- und proc-macro1“-Angriff ist
Die Vorbereitungen begannen bereits Stunden vor der eigentlichen Schadlast. Laut der Zeitleiste von StepSecurity wurde um 01:17 Uhr UTC ein GitHub-Konto namens „dtolney“ erstellt, das sich als David Tolnay ausgab, der das echte „proc-macro2“ betreut. Um 01:25 Uhr UTC folgte ein passendes crates.io-Konto. Um 01:55 Uhr UTC veröffentlichte dieses Konto „proc-macro1 1.0.106“, eine saubere Kopie von „proc-macro2“ mit gefälschten Autorenmetadaten. Diese Version hatte keine böswillige Wirkung. Sie blieb mehr als fünf Stunden lang unverändert, sammelte ein wenig Registrierungshistorie an und wirkte wie ein unauffälliges Crate.
Um 07:11 UTC veröffentlichte dasselbe Konto „proc-macro1 1.0.107“. Dabei handelte es sich um die als Angriffswerkzeug genutzte Version. Vier Minuten später, um 07:15 UTC, erschien „arrayref 0.3.10“ unter dem Konto des rechtmäßigen Eigentümers, das eine Abhängigkeit von dem Typosquat enthielt. Um 07:34 UTC folgte „internment 0.8.7“ und um 07:37 UTC „append-only-vec 0.1.9“.
Der am selben Tag veröffentlichte Bericht des Rust Security Response Teams beschreibt, dass um 07:15 Uhr UTC eine Meldung eingegangen sei, wonach „proc-macro1“ bösartig sei, und weist darauf hin, dass die beliebte „arrayref“-Crate kürzlich neu veröffentlicht worden sei und auf diese verweise. Die Entdeckung geht ursprünglich auf das Forschungsteam der Nextron Systems GmbH zurück. Unabhängig davon meldete ein Reporter mit dem Pseudonym jhobern das Problem in der RustSec-Advisory-Datenbank und gab an, auch das Rust-Sicherheitsteam per E-Mail informiert zu haben. Nach der Rekonstruktion von StepSecurity erfolgte diese Meldung an RustSec um 07:54 Uhr UTC, also 39 Minuten nach der Veröffentlichung der schädlichen Version, was darauf hindeutet, dass die beiden Berichte unterschiedliche Benachrichtigungswege beschreiben und nicht denselben.
Die Bereinigung begann um 08:03 UTC mit der Löschung von „proc-macro1“. Das Rust Security Response Team hat inzwischen die genauen Zeiten für die Entfernung der drei legitimen Crates veröffentlicht. „arrayref“ 0.3.10 wurde um 08:41 UTC nach 86 Minuten Online-Zeit entfernt, „internment“ 0.8.7 um 09:04 UTC nach 90 Minuten und „append-only-vec“ 0.1.9 um 09:25 UTC nach 107 Minuten. Der Eintrag auf crates.io zeigt, dass die vom Angreifer entfernten Versionen aller drei Crates zwischen 09:15 und 09:26 UTC wiederhergestellt wurden.
Wie der Yank zum Köder wurde
Bei früheren npm-Vorfällen gab es nichts Vergleichbares zu dem, was als Nächstes geschah. Innerhalb derselben Minute, in der Version 0.3.10 veröffentlicht wurde, nutzte der Angreifer das Konto des Eigentümers, um alle aktuellen Versionen von „arrayref“ in einer automatisierten Abfolge mit Abständen von etwa vier Sekunden zu „yanken“. Ein „Yank“ in Cargo löscht eine Version nicht. Vorhandene Lockfiles, die bereits auf eine „yanked“ Version verweisen, werden weiterhin kompiliert, und der Resolver überspringt „yanked“ Releases nur dann, wenn er neue Versionen auswählt. Die Versionshistorie auf crates.io verzeichnet, dass die Versionen 0.3.5 bis 0.3.9 zwischen 07:15:24 und 07:15:40 UTC unter dem Konto des Eigentümers entfernt und anschließend zwischen 09:15 und 09:17 UTC von einem Mitglied des crates.io-Teams wiederhergestellt wurden. Das Gleiche geschah mit „append-only-vec“ 0.1.7 und 0.1.8 um 07:38 UTC sowie mit „internment“ 0.8.3 bis 0.8.6 um 07:37 UTC.
Dadurch wird eine routinemäßige Funktion zur Registry-Pflege zu einem Social-Engineering-Hebel, und ihre begrenzte Reichweite trug gerade dazu bei, dass sie funktionierte. Das „Yanking“ benachrichtigt niemanden, und ein Projekt, das bereits an eine „yanked“-Version gebunden ist, wird weiterhin mit dieser Version kompiliert. Die Warnung vor der zurückgezogenen Version erscheint, sobald Cargo mit dem Registry-Index kommuniziert – bei Befehlen wie „cargo update“, „cargo add“ oder „cargo install –locked“ –, sodass sie in erster Linie diejenigen erreicht, die am ehesten eine erneute Auflösung vornehmen würden. Für sie wurde die böswillige Version zur neuesten Version, die Cargo akzeptieren würde, und für gängige Kompatibilitätsanforderungen wie ^0.3.5 war sie der einzige verbleibende Kandidat, der nicht zurückgezogen worden war. Ein Entwickler, der die Warnung sah und „cargo update“ ausführte, um sie zu beseitigen, könnte vernünftigerweise davon ausgehen, dass er verantwortungsbewusst handelte.
Im Build-Skript
Der Schadcode befand sich in der Datei „build.rs“ von „proc-macro1“, nicht in „arrayref“. Wer den Quellcode von „arrayref“ 0.3.10 zur Überprüfung abgerufen hätte, hätte dieselben Array-Referenz-Makros vorgefunden, die das Crate schon immer enthielt, sowie eine zusätzliche Abhängigkeitszeile in der Datei „Cargo.toml“.
Das Build-Skript stellte seine Command-and-Control-Daten aus Base64-Fragmenten zusammen, anstatt eine lesbare URL fest zu codieren, und lud anschließend eine Binärdatei der zweiten Stufe von https://23.254.165.112:9089/ über TLS herunter, wobei die Zertifikatsüberprüfung ausdrücklich deaktiviert war. Der Downloader verwendete „rustls“ mit einem „Accept-All“-Verifizierer, dessen Verifizierungsmethoden bedingungslos einen Erfolg zurückgaben, sodass die Verbindung zwar verschlüsselt war, jedoch keine wirksame Serverauthentifizierung bot. Eine zweite Adresse auf demselben Host, 23.254.165.112:443, wurde der Payload als Command-and-Control-Endpunkt übergeben. Der von StepSecurity gemeldete zugehörige Hostname lautet hwsrv-798836.hostwindsdns.com.
Die Ausführung erfolgte plattformabhängig. Auf Unix-Systemen wurde die Nutzlast in das Verzeichnis /tmp/rust-setup geschrieben, als ausführbar markiert und als eigenständiger Prozess gestartet. Unter Windows wurde die Datei %TEMP%\rust-setup.ps1 abgelegt und über %TEMP%\rust-setup-launch.vbs unter wscript.exe mit dem Flag CREATE_NO_WINDOW gestartet, sodass auf dem Bildschirm nichts sichtbar war.
Zwei Dinge haben dafür gesorgt, dass dies unbemerkt blieb. Erstens: Wie StepSecurity anmerkt, handelt es sich bei dem Bibliothekscode in „proc-macro1“ tatsächlich um „proc-macro2“, sodass die Builds erfolgreich verlaufen und in der normalen Ausgabe nichts Ungewöhnliches auftaucht. Zweitens musste in Ihrem Programm nie eine Arrayref-Funktion aufgerufen werden, und Sie mussten „cargo build“ nicht gezielt ausführen. Cargo verfügt über keinen Hook zur Installationszeit, sodass beim Herunterladen nichts ausgeführt wurde; jedoch wird bei jedem Befehl, der den Graphen kompiliert, unmittelbar vor der Kompilierung des Pakets ein Build-Skript ausgeführt. „cargo check“, „cargo test“, „cargo clippy“ und „cargo doc“ reichten jeweils aus – auf der Berechtigungsstufe dessen, wer oder was sie gestartet hat. Auf einem Entwickler-Laptop handelt es sich dabei um eine Benutzersitzung, die Zugriff auf SSH-Schlüssel, Cloud-Anmeldedaten und Browserdaten haben kann. In der CI ist es ein Runner, der Registry-Token, Signaturmaterial und Cloud-Rollen verwaltet.
In den ersten Stunden konnte niemand sagen, was die Stage-2-Binärdatei tatsächlich tat. Die Berichte von RustSec, StepSecurity und SafeDep dokumentierten den Downloader und die Command-and-Control-Adressen – mehr nicht. Diese Lücke ist nun geschlossen. Wiz Research hat die wiederhergestellten Payloads analysiert und beschreibt eine leistungsfähige Backdoor. Sie sendet über HTTPS Signale aus, übermittelt Host-Details und gestohlene Anmeldedaten als Base64-kodiertes JSON zurück, liest gespeicherte Anmeldedaten aus den SQLite-Datenbanken von Chrome, Brave und Edge aus und sorgt für Persistenz als systemd-Benutzerdienst unter Linux, als LaunchAgent unter macOS oder als Run-Schlüssel in der Registrierung unter Windows. Es benötigt vier Befehle, von denen einer beliebige Shell- oder PowerShell-Befehle abruft und ausführt, und greift auf einen Algorithmus zur Domänengenerierung zurück, falls der primäre C2-Server ausfällt. Betrachten Sie eine bestätigte Ausführung daher als Kompromittierung des Hosts und gehen Sie davon aus, dass auf alle Anmeldedaten, auf die der Host oder der Runner Zugriff hatte, zugegriffen worden sein könnte – einschließlich der im Browser gespeicherten Passwörter.
Auch in Bezug auf die Infrastruktur wurde es interessanter. Die vergleichbare Mastra-Kampagne im Juni verbreitete einen plattformübergreifenden Infostealer, der es auf Anmeldedaten, Browserdaten und Kryptowährungs-Wallets abgesehen hatte. Wiz berichtet, dass die Arrayref-Payloads Signale an denselben C2-Anforderungspfad senden, den auch Mastra nutzte, dass die Beacon-Adresse denselben TLS-Zertifikatsaussteller wie ein Mastra-Host hat und dass ein von einem Opfer gemeldeter C2-Server in diesem Vorfall in den Google Threat Intelligence-Daten auftaucht, die die Axios-Kampagne eines nordkoreanischen Akteurs abdecken. Microsoft führte die Mastra-Aktivitäten auf einen Cluster aus der DVRK zurück. Dabei handelt es sich eher um eine dokumentierte Überschneidung der Infrastruktur als um eine formelle Zuordnung für diesen Vorfall – und es geht um weit mehr als nur zwei Kampagnen, die denselben Hosting-Anbieter nutzen.
Warum das Build-Modell von Rust das Problem noch verschlimmert
Rust genießt einen hervorragenden Ruf in Sachen Sicherheit, und diesen hat sich die Sprache in den Bereichen verdient, die sie tatsächlich abdeckt. Speichersicherheit und ein strenges Typsystem verhindern nicht, dass eine Abhängigkeit absichtlich beliebigen Code ausführt, bevor Ihr Programm beendet wird.
Cargo bietet einer Abhängigkeit zwei Möglichkeiten, Code auf Ihrem Rechner während der Erstellung auszuführen. Die erste ist „build.rs“. Wenn Sie diese Datei im Stammverzeichnis eines Pakets ablegen, kompiliert Cargo das Skript und führt es „unmittelbar vor der Erstellung des Pakets“ aus. Die legitimen Anwendungszwecke sind real und weit verbreitet und umfassen die Kompilierung von C-Bibliotheken, die Codegenerierung und die Erkennung von Funktionen – genau das macht sie in einem Diff unauffällig. Die zweite Möglichkeit sind prozedurale Makros, die innerhalb des Compilers kompiliert und ausgeführt werden, während Ihr Code kompiliert wird. Die Rust-Referenz spricht die Konsequenz klar an: Proc-Makros „weisen dieselben Sicherheitsbedenken auf wie die Build-Skripte von Cargo“. Keine der beiden Ebenen ist in einer Sandbox isoliert.
Das npm-Ökosystem bietet zumindest den Befehl „npm install –ignore-scripts“ an, der hier als Abhilfemaßnahme nach dem Mastra-Angriff genannt wird. Cargo verfügt weder in der stabilen noch in der Nightly-Version über ein allgemeines Äquivalent. Es gibt kein Build-Flag, keinen Konfigurationsschlüssel und kein `-Z`-Flag, mit dem sich beliebige Build-Skripte von Abhängigkeiten einfach deaktivieren lassen, während der Rest des Builds fortgesetzt wird, und deren Sandboxing ist seit 2018 ein offenes Cargo-Problem. Die einzige dokumentierte Möglichkeit zum Ausschluss ist sehr eingeschränkt. Bei einem Paket, das einen „links“-Schlüssel deklariert, kann das Build-Skript in der Konfiguration überschrieben werden; in diesem Fall wird das Skript weder kompiliert noch ausgeführt. Dies gilt für -sys-Crates, die Metadaten für C-Bibliotheken bereitstellen, nicht jedoch für eine Crate wie „proc-macro1“. Der tiefere Grund dafür, dass es kein allgemeines Flag gibt, liegt darin, dass ein Build-Skript keine optionale Erleichterung bei der Installation ist, wie es ein „postinstall“-Hook bei npm ist. Es ist Teil der Kompilierung, und es zu deaktivieren bedeutet in der Regel, dass das Paket nicht gebaut wird.
Auch die transitive Abhängigkeit spielt eine Rolle. „arrayref“ ist normalerweise ein „Leaf“-Utility-Crate, das Makros für Array-Referenzen bereitstellt, und seine „clean“-Releases deklarieren keine Laufzeit- oder Build-Abhängigkeiten, sondern lediglich eine Entwicklungsabhängigkeit von „quickcheck“. Genau das machte die Version 0.3.10 zu einer Ausnahme, da „proc-macro1“ die erste gewöhnliche Abhängigkeit war, die das Crate jemals deklariert hatte. Zu seinen direkten Abhängigkeiten gehören tiny-skia-path, tiny-skia, libsecp256k1, multiaddr und schnorrkel, und die Überprüfung der umgekehrten Abhängigkeiten durch StepSecurity fügt revm-precompile auf der Ethereum-Seite sowie solana-runtime und spl-token auf der Solana-Seite hinzu. Eines der größten betroffenen Projekte war „blake3“ mit rund 166 Millionen Downloads seit seiner Einführung, das bis einschließlich Version 1.8.6 „arrayref ^0.3.5“ benötigte und dann am selben Morgen um 09:09 Uhr UTC die Version 1.8.7 ohne diese Abhängigkeit veröffentlichte. Über diesen Crates befinden sich weitaus mehr Projekte, die diese nutzen. Fast niemand, der diesem Angriff ausgesetzt war, hatte „arrayref“ in seiner eigenen „Cargo.toml“-Datei. Sie verfügten über eine Grafik- oder Kryptografie-Bibliothek, und diese wurde darunter, zwei oder drei Ebenen tiefer, in einem Crate bereitgestellt, zu dem sie keinen Einfluss hatten.
Der Teil, der Ihre Sichtweise auf die Aufnahme verändern sollte
Jede Prüfung, die dies plausibel hätte erkennen können, bezieht sich auf etwas anderes als das fertige Produkt.
Die Versionsbindung hat geholfen und bietet echten Schutz, aber es ist ein Schutz durch Trägheit. Sie funktioniert genau so lange, bis man ein Sicherheitsupdate benötigt; in diesem Moment löst man neue Abhängigkeiten auf und übernimmt das, was die Registry in diesem Moment bereitstellt. Signierung und Zwei-Faktor-Authentifizierung sind wertvolle Kontrollmechanismen bei der Veröffentlichung, aber keiner von beiden stellt sicher, dass der veröffentlichte Code harmlos ist. Das Rust Security Response Team hat erklärt, dass der Computer oder die Zugangsdaten des Betreuers wahrscheinlich kompromittiert wurden, ohne jedoch öffentlich zu präzisieren, was genau betroffen war. Daher lässt sich noch nicht sagen, welche Kontrollmaßnahme diesen Vorfall hätte verhindern können. Ein auf CVE-Nummern basierender Scan zur Kompositionsanalyse ergab keine Übereinstimmungen, da während des Zeitfensters der Sicherheitslücke noch keine CVE-Nummern existierten. Eine SBOM hätte helfen können, jedoch nur als Bestandsaufnahme. „proc-macro1“ wurde als gewöhnliche Abhängigkeit und nicht als reine Build-Abhängigkeit in „arrayref“ aufgenommen; daher hätte eine vor der Kompilierung generierte, Cargo-basierte oder aus der Lockfile abgeleitete SBOM „proc-macro1 1.0.107“ als neu aufgelöste Komponente aufgeführt. Was sie jedoch nicht getan hätte, wäre der Hinweis, dass diese Komponente bösartig war. Eine Ebene tiefer waren die Crates, die den Download tatsächlich durchführten – „base64“, „rustls“ und „ureq“ – Build-Abhängigkeiten von „proc-macro1“, und eine aus Artefakten abgeleitete oder auf die Laufzeit ausgerichtete SBOM hätte diese möglicherweise vollständig ausgelassen.
Die Quellcodeprüfung wäre auf der Ebene, auf der die meisten Teams tatsächlich arbeiten, fehlgeschlagen – aus demselben Grund, aus dem sie auch beim Mastra-Angriff versagt hat. Ein Prüfer, der den Bibliothekscode von arrayref 0.3.10 liest, findet saubere, vertraute Makros vor. Das böswillige Verhalten befindet sich eine Ebene tiefer, in einem Build-Skript, das zu einer Abhängigkeit gehört, die kein Mitglied des Teams ausgewählt hat, in einem Crate, dessen Bibliothekscode eine Kopie von legitimem Code ist. Ein Prüfer, der den vollständigen Release-Diff einschließlich Manifest gelesen hätte, hätte eine neue Abhängigkeit mit einem Namen entdeckt, der sich nur um eine Ziffer von „proc-macro2“ unterscheidet, und hätte damit einen Ansatzpunkt gehabt. Das ist die Prüftiefe, die es wert ist, institutionalisiert zu werden, und es ist nicht die Tiefe, auf der die meisten Aufnahmeverfahren arbeiten.
Was eine vertretbare Position von einer unvertretbaren unterscheidet, ist der Zeitpunkt, zu dem die Vertrauensentscheidung getroffen wird. Wenn man damit wartet, bis die Kompilierung bereits begonnen hat, ist es wahrscheinlich schon zu spät, da sich das Paket auf dem Build-Host befindet und dessen Build-Skript möglicherweise bereits ausgeführt wurde.
Milderung
Gehen Sie die einzelnen Schritte der Reihe nach durch, da die ersten drei Schritte darüber entscheiden, ob es sich bei den restlichen Schritten um Aufräumarbeiten oder um die Reaktion auf einen Vorfall handelt.
Finden Sie die Artefakte. Führen Sie die oben beschriebene Cargo-Cache-Prüfung auf Entwicklerrechnern, Build-Agents und allen am 20. August 2026 erstellten Container-Images durch. Durchsuchen Sie anschließend die Lockfiles aller Repositorys nach „arrayref 0.3.10“, „append-only-vec 0.1.9“, „internment 0.8.7“ sowie nach allen Vorkommen von „proc-macro1“, „proc-macro-en“, „aovine“, „arone“, „aronenao“ oder „tinymember“.
Mit bekannten Hash-Werten abgleichen. StepSecurity hat SHA-256-Werte für die Archive veröffentlicht. Der Hash-Wert für arrayref 0.3.10 lautet 25ad700976873c76af785cb99b33c48db7df8b81f21d1e9e06b3676b9a9373ae, proc-macro1 1.0.107 lautet 61198155da51b838772eecf5bfaac6cbc4dcc388dccc56658fc28a8e831b34d4, und die saubere Köderversion proc-macro1 1.0.106 lautet b5c1b5b0763a8809a644a8f92224653f0aca623a98eecc714d27f74b80fbe436. Beschränken Sie Ihre Suche auf alle drei. Version 1.0.106 enthielt keine Nutzlast, sodass ihr Auffinden nicht auf eine Ausführung oder einen Kontakt mit der Infrastruktur des Angreifers hindeutet, aber es ist ein nützlicher Hinweis darauf, dass etwas in der Umgebung den Typosquat während seiner Vorbereitungsphase aufgelöst hat.
Achten Sie auf die Ausführung, nicht nur auf das Vorhandensein. Suchen Sie auf Endgeräten unter Unix nach „/tmp/rust-setup“ und unter Windows nach „rust-setup.ps1“ oder „rust-setup-launch.vbs“ im Verzeichnis „%TEMP%“. Das sind die lokalen Namen, die der Dropper schreibt. Suchen Sie in der Proxy-, Netzwerk- oder Download-Telemetrie separat nach Anfragen mit den Namen „rust-crate_0.1.0“ bis „rust-crate_0.4.0“ – dabei handelt es sich um die zielspezifischen Dateinamen der zweiten Stufe, die der Downloader auf der Remote-Seite auswählt, und nicht um Dateien, die unter diesen Namen auf der Festplatte erscheinen. Überprüfen Sie, ob „wscript.exe“ aus einem Build-Prozess heraus gestartet wurde und ob ein untergeordneter Prozess vorhanden ist, der den Build-Schritt überdauert hat. Eine zwischengespeicherte .crate-Datei bedeutet, dass das Archiv heruntergeladen wurde. Die Dropper-Artefakte auf der Festplatte deuten darauf hin, dass das Build-Skript weit genug ausgeführt wurde, um die Payload zu schreiben, und die Prozess- oder Netzwerktelemetrie bestätigt, dass die Payload selbst gestartet wurde. Bei Verdacht auf Ausführung sollten Sie auch nach der Persistenz suchen, die die Backdoor installiert, d. h. nach einem unbekannten systemd-Benutzerdienst unter Linux, einem verirrten LaunchAgent unter macOS oder einem unerwarteten HKCU-Run-Eintrag unter Windows.
Blockieren Sie ausgehende Verbindungen und überprüfen Sie diese. Fügen Sie 23.254.165.112 sowie 23.254.167.107 und 23.254.167.216 – die beide als Command-and-Control-Adressen der Stufe 2 gemeldet wurden – zu Ihren Sperrlisten hinzu und beschränken Sie die Regeln nicht auf die beobachteten Ports 9089 und 443. Fügen Sie außerdem hwsrv-798836.hostwindsdns.com hinzu und durchsuchen Sie anschließend die Proxy- und Firewall-Protokolle rückwirkend nach Verbindungen von Build-Hosts, einschließlich HTTPS-Anfragen an den Pfad /49890878, bei dem es sich um den Stage-2-Beacon-Endpunkt handelt. Ein Build-Prozess, der eine reine IP-Adresse auf einem nicht standardmäßigen Port erreicht, ist ein Ereignis mit hohem Verdachtswert, das unabhängig von dieser Kampagne untersucht werden sollte.
Wenden Sie die Rotation auf alles an, worauf der Build Zugriff hatte. Wenn die Payload ausgeführt wurde, beschränken Sie die Rotation auf die Daten, die diese Identität enthielt, und nicht auf die Aktivitäten des Crate. Auf einem Entwicklerrechner sind das SSH-Schlüssel, Cloud-Anmeldedaten, Registry-Token, alle über die Shell-Umgebung offengelegten Geheimnisse sowie die in Chrome, Brave oder Edge gespeicherten Anmeldedaten, da die Payload diese direkt ausliest. In der CI sind das Runner-Geheimnisse, OIDC-Vertrauensbeziehungen, Token zur Paketveröffentlichung und Signaturmaterial.
Die transitive Abhängigkeit muss korrekt festgelegt werden. „arrayref 0.3.9“ ist sauber, nicht entfernt und wieder verfügbar. Da fast niemand „arrayref“ in seinem eigenen Manifest hat, bedeutet „arrayref = ‚=0.3.9‘“, dass eine direkte Abhängigkeit hinzugefügt wird, die Sie sonst nicht verwenden, und eine exakte Festlegung kann dazu führen, dass der Graph später nicht mehr erfüllbar ist. Bei einer transitiven Abhängigkeit ist der sauberere Weg „cargo update -p arrayref –precise 0.3.9“, wobei die resultierende Lockdatei festgeschrieben und durchgesetzt wird.
Machen Sie die Einhaltung der Lockfile-Regeln zur Pflicht. Führen Sie die CI mit der Option „–locked“ aus, wodurch Cargo angewiesen wird, den Vorgang abzubrechen, anstatt das Lockfile zu ändern, sodass eine unerwartete Auflösung den Build unterbricht, anstatt die Veröffentlichung zu ermöglichen. Verlangen Sie eine manuelle Überprüfung bei Pull-Anfragen des Dependency-Bots, die transitive Versionen ändern, und betrachten Sie jede Aufforderung zum Upgrade – sei es eine „Yank“-Warnung, eine Pull-Anfrage des Bots oder ein roter CI-Job – als Grund, den Diff zu lesen, anstatt die Auflösung erneut durchzuführen.
Erkennung
Beobachten Sie, was der Build macht. CVE-Feeds und Schwachstellendatenbanken enthielten um 07:11 UTC keine Informationen zu „proc-macro1“, und die Reputationsdaten umfassten lediglich fünf Stunden Registrierungshistorie. Das Verhalten war das einzige verfügbare Signal. Unerwartete ausgehende Netzwerkaktivitäten eines Build-Skripts sollten als sicherheitsrelevant behandelt werden, und ein Build-Prozess, der eine ausführbare Datei in ein temporäres Verzeichnis schreibt und diese dann trennt, ist für sich genommen bereits ein starker Indikator.
Achten Sie beim Schreiben der Regel auf die korrekte Prozessabfolge. Ein Build-Skript wird als eigene kompilierte ausführbare Datei ausgeführt, die von Cargo gestartet wird – üblicherweise „build-script-build“ im Zielverzeichnis –, sodass das Signal von einem kurzlebigen untergeordneten Prozess von Cargo ausgeht, der das Netzwerk erreicht, und nicht von Cargo selbst. Eine prozedurale Makro-Nutzlast würde stattdessen innerhalb von rustc ausgeführt werden. Geben Sie in beiden Fällen eine Warnung aus, ebenso wie bei neuen ausführbaren Dateien, die während eines Builds in /tmp oder %TEMP% erscheinen.
Auf Hosts, bei denen der Verdacht auf eine Ausführung besteht, sollten Sie die Untersuchung über den Build-Zeitpunkt hinaus ausweiten. Die Stage-2-Backdoor bleibt je nach Plattform über einen systemd-Benutzerdienst, einen LaunchAgent oder einen Run-Schlüssel in der Registrierung bestehen und sendet Signale über HTTPS aus. Daher ist eine unerwartete Persistenz, die etwa zum Zeitpunkt eines Builds entsteht, an sich schon ein Grund für eine Warnmeldung.
Achten Sie auf der Abhängigkeitsebene auf jede neue Abhängigkeit, die in einem Lockfile-Diff auftaucht, insbesondere auf solche, deren Name sich um ein Zeichen oder eine Ziffer von einer weit verbreiteten Crate unterscheidet. Der entscheidende Unterschied liegt in „proc-macro1“ gegenüber „proc-macro2“, und dies ist in einem Diff erkennbar, falls jemand die Abhängigkeits-Diffs liest. Beachten Sie, dass die Crates für den Download zur Build-Zeit in dieser Kampagne – base64, rustls und ureq – eine Ebene tiefer innerhalb von proc-macro1 deklariert wurden; daher hätte eine Regel, die nur Ihre eigenen direkten Build-Abhängigkeiten überprüft, diese übersehen.
Wie SecureChain für Rust den Ausgang verändert
Der Grund, warum dieser Angriff funktionierte, liegt darin, dass die Ausführung während der Build-Phase in fast jeder Rust-Pipeline ein als vertrauenswürdig geltender, ungeprüfter Schritt ist. Genau diese Lücke soll SecureChain for Rust schließen.
SecureChain erstellt jedes Paket aus dem Quellcode in gehärteten, isolierten Umgebungen neu und überprüft den Quellcode der Crates gezielt auf Angriffsmuster während der Erstellungsphase, darunter bösartige „build.rs“-Skripte und prozedurale Makros, wodurch betroffene Crates blockiert werden, bevor sie in Ihre Toolchain gelangen. Dieser Unterschied ist hier von größerer Bedeutung als bei einem typischen npm-Vorfall. Da es in Cargo kein Äquivalent zu „–ignore-scripts“ gibt, kann ein Rust-Team Build-Skripte nicht deaktivieren, sodass die Überprüfung bereits im Upstream erfolgen muss, anstatt innerhalb der Pipeline deaktiviert zu werden. Ein `build.rs`-Skript, das eine URL aus Base64-Fragmenten neu zusammensetzt, die Zertifikatsvalidierung deaktiviert und eine Binärdatei von einer bloßen IP-Adresse abruft, ist genau die Art von Muster während der Bauzeit, das durch die Überprüfung aufgedeckt werden soll – und es zu überprüfen, bevor das Crate den Laptop eines Entwicklers erreicht, ist weitaus sicherer, als es erst ausführen zu lassen und erst danach zu untersuchen.
Entdecken Sie SecureChain für Rust →
SecureChain fungiert als verifizierte, aus dem Quellcode neu kompilierte Alternative zum direkten Abruf von crates.io und nicht als Scanner, der am Ende der Pipeline angehängt wird. Pakete werden auf bekannte Malware, verdächtige Codemuster und Risiken in der Lieferkette überprüft, und alles, was die Prüfung nicht besteht, wird blockiert, bevor es Ihre Umgebung erreicht. Die von SecureChain genannten Rust-spezifischen Risiken sind genau diejenigen, die bei diesem Vorfall zutage getreten sind, darunter „Lookalike“-Crates, kompromittierte Herausgeber, manipulierte Veröffentlichungen, verstecktes Verhalten und die Übernahme nicht mehr gepflegter Crates, bei der das Eigentumsrecht an einer aufgegebenen Crate übertragen und anschließend genutzt wird, um ein bösartiges Update unter einem zuvor vertrauenswürdigen Namen zu verbreiten. Ein Crate, dessen Name sich um eine Ziffer von einer grundlegenden Abhängigkeit unterscheidet, ist genau das Muster eines „Lookalike“, das ein Einlassmodell als verdächtig einstuft – und dieses Risiko bestand bereits, bevor es von einem Build verwendet wurde.
All dies ändert nichts an der Arbeitsweise der Entwickler. SecureChain ist ein direkt einsetzbarer Repository-Ersatz, der mit JFrog Artifactory, Sonatype Nexus und GitHub Packages kompatibel ist, und die Teams behalten dieselben Pakete, dieselben APIs und dieselben Cargo-Befehle bei. Außerdem bietet es SLSA-Level-3-Provenienz, SBOM und VEX-Ausgabe. Eine SBOM listet Komponenten und Abhängigkeitsbeziehungen innerhalb ihres definierten Geltungsbereichs auf. Die Provenienz dokumentiert, wie und wo ein Artefakt erstellt wurde, und liefert Nachweise über dessen Herkunft und den Erstellungsprozess.
Dieses Problem hat noch weitere Auswirkungen, die sich durch „Pinning“ nicht beheben lassen. „arrayref“ ist ein kleines, stabiles Utility-Crate, wie es oft vorkommt: Es wird einmal geschrieben und dann ein Jahrzehnt lang stillschweigend von Hunderten von Abhängigkeiten weiterverwendet. Nur wenige Teams beobachten ein solches Crate genau, und sehr viele verwenden es letztendlich in ihren Produkten. Genau das macht Crates wie dieses zu attraktiven Zielen und führt dazu, dass Teams zwischen einer aufgegebenen Abhängigkeit und einer Neuprogrammierung hin- und hergerissen sind. SecureChain sorgt dafür, dass unterstützte Komponenten im Rahmen eines SLA überwacht und gepatcht werden, solange sie aktiv genutzt werden. Wenn eine Version den End-of-Life-Status erreicht, kann der „Endless Lifecycle Support“ von TuxCare weiterhin Sicherheitspatches für diese Version bereitstellen, solange Sie darauf angewiesen sind. Keine erzwungenen Upgrades und keine Neuprogrammierungen.
Warum es hier um mehr geht als nur um eine Kampagne
Anderes Ökosystem, derselbe Fehler. Bei der „Mastra“-Kampagne im Juni wurde eine einzige Zeile mit einer Typosquatting-Abhängigkeit in über 140 saubere npm-Pakete eingefügt. Die „Mini Shai-Hulud“-Welle betraf 172 npm-Pakete und zwei PyPI-Pakete, wobei sie über eine gültige SLSA-Herkunftsangabe der Stufe 3 verfügte, während die darauf folgende „Megalodon“-Welle die Veröffentlichung von Paketen gänzlich übersprang und stattdessen massenhaft GitHub-Actions-Workflows in Tausende von Repositorys einschleuste, um Cloud-Anmeldedaten, SSH-Schlüssel und Tokens zu stehlen. In diesem Fall wird dasselbe Vorgehensmodell auf Cargo übertragen, wobei ein kompromittiertes Betreuerkonto, eine „Typosquatted“-Abhängigkeit und die Cargo-eigene „Yank“-Semantik genutzt werden, um Druck für ein Upgrade zu erzeugen. Wiz hat inzwischen Infrastrukturüberschneidungen zwischen dieser Kampagne und „Mastra“ dokumentiert, und „Mastra“ wurde einem nordkoreanischen Cluster zugeschrieben – das Ökosystem ändert sich also, während der Betreiber möglicherweise derselbe bleibt.
Bei diesen Vorfällen unterschied sich die kompromittierte Fläche: Mal handelte es sich um eine transitive Abhängigkeit, mal um ein bösartiges Paket mit nachweisbarer Herkunft und mal um den CI/CD-Workflow selbst. Der gemeinsame Fehler bestand darin, einen authentifizierten Herausgeber, eine gültige Herkunftsangabe oder einen erfolgreichen Build als Beweis dafür zu betrachten, dass das während der Aufnahme und des Builds auftretende Verhalten harmlos sei. Mini Shai-Hulud verdeutlicht die Grenzen der Herkunftsangabe, da seine bösartigen Artefakte eine kryptografisch gültige SLSA-Herkunftsangabe enthielten. Das Verhalten muss bereits bei der Aufnahme, also vor Beginn des Builds, überprüft werden – und nicht erst auf einem Rechner, auf dem Ihre Zugangsdaten bereits gespeichert sind.
Entdecken Sie SecureChain für Rust →
FAQ
Schützt mich eine festgeschriebene „Cargo.lock“-Datei?
Was das Herunterladen der schädlichen Version angeht, im Großen und Ganzen ja. Eine unveränderte Lockdatei hält ein Projekt auf den bereits aufgelösten Versionen fest, was für die meisten Teams „arrayref 0.3.9“ bedeutete – auch in einem sauberen Container, in den die Lockdatei kopiert wird. Dieser Schutz endet in dem Moment, in dem Sie neue Abhängigkeiten auflösen, sei es durch „cargo update“, einen Abhängigkeits-Bot, eine Manifeständerung, die einen neuen Pfad zur Crate einführt, einen Build, der ohne vorhandene Lockdatei ausgeführt wird, oder „cargo install“ ohne die Option „–locked“. Deine Lockdatei gelangt zudem nicht zu deinen eigenen Nutzern, da ausschließlich deine `Cargo.toml` bestimmt, was diese auflösen. Und nichts davon berührt eine bösartige Version, die sich bereits im Cache oder in einem Vendor-Baum befindet.
Kann ich die Ausführung von Build-Skripten einfach unterbinden?
Realistisch gesehen nicht. Es gibt kein „Stable“- oder „Nightly“-Flag für Cargo, mit dem sich Build-Skripte überspringen lassen, und die einzige dokumentierte Möglichkeit, das Build-Skript eines Pakets zu umgehen, das einen „links“-Schlüssel deklariert, lässt sich nicht verallgemeinern. Im Gegensatz zu den Hooks von „npm install“ sind Rust-Build-Skripte Teil der Kompilierung und oft sogar erforderlich, um überhaupt ein funktionsfähiges Artefakt zu erzeugen. Aus diesem Grund muss die Überprüfung vor dem Build stattfinden und kann nicht einfach innerhalb des Builds deaktiviert werden.
War der Betreuer dafür verantwortlich?
Das Rust Security Response Team erklärte, es gehe nicht davon aus, dass der Urheber böswillig gehandelt habe, und die wahrscheinlichste Erklärung sei ein kompromittierter Rechner oder kompromittierte Anmeldedaten. Das Konto wurde vorsorglich gesperrt. Es handelt sich um dieselbe Art von Sicherheitslücke wie beim Mastra-Angriff, bei dem der Einstiegspunkt das npm-Konto eines ehemaligen Mitwirkenden war, das etwa 16 Monate lang inaktiv gewesen war, ohne dass die Zugriffsrechte widerrufen worden waren.
Warum war es wichtig, ältere Versionen zu entfernen?
Durch das „Yanking“ wird eine Version nicht gelöscht, und die Erstellung bestehender Lockfiles wird fortgesetzt. Was sich dadurch ändert, ist die neue Auflösung, da der Resolver zurückgezogene Releases überspringt, sofern sie nicht bereits in der Sperrdatei enthalten sind. Durch das Zurückziehen der korrekten Versionen blieb die bösartige Version als neuestes Release übrig, das die neue Auflösung akzeptieren würde. Cargo gibt außerdem eine Warnung bezüglich der zurückgezogenen Version aus, sobald es mit dem Index kommuniziert. Daher waren es vor allem diejenigen Entwickler, die daraufhin mit `cargo update` eine erneute Auflösung durchführten, die am ehesten auf Version 0.3.10 landeten.
Hätte ein SBOM- oder ein CVE-Scanner dies erkannt?
Nicht von selbst. Es gab keine passende CVE, solange die schädlichen Versionen aktiv waren. Eine vor dem Build erstellte SBOM hätte die unerwartete Abhängigkeit von „proc-macro1“ sichtbar machen können, da diese als gewöhnliche Abhängigkeit deklariert war, doch eine SBOM ist eher ein Bestandsverzeichnis als ein Malware-Detektor. Einige SBOMs lassen zudem Abhängigkeiten weg, die nur für den Build relevant sind – und genau das waren die vom Downloader in „proc-macro1“ erstellten Crates. Zur Erkennung war weiterhin eine Richtlinien- oder Analyseebene erforderlich, die entweder die neue Abhängigkeit oder deren Verhalten erkennen konnte.
Ist Rust in diesem Zusammenhang mehr oder weniger anfällig als npm?
Rust weist andere Schwachstellen auf. Es verfügt über weniger Pakete und hat zahlenmäßig eine kleinere Angriffsfläche, und die strikte Einhaltung der Lockfile-Regeln bei der Anwendungsentwicklung trägt ebenfalls dazu bei. Allerdings lassen sich Build-Skripte und Proc-Makros weniger leicht vermeiden als „npm install“-Hooks, sodass es für die einfache Methode von npm, die Ausführung von Lebenszyklus-Skripten zu unterdrücken, kein direktes Äquivalent in Cargo gibt.
Was ist die wichtigste langfristige Lösung?
Verlagern Sie die Vertrauensentscheidung in die Aufnahmephase. Erstellen Sie Pakete neu und prüfen Sie sie, bevor sie in Ihre Umgebung gelangen, überprüfen Sie die Unterschiede zwischen den einzelnen Versionen, behandeln Sie jede neue Abhängigkeit als sicherheitsrelevante Änderung und verlangen Sie einen Herkunftsnachweis als Beleg für die Herkunft und den Erstellungsprozess, ohne diesen als Beweis dafür anzusehen, dass das daraus resultierende Verhalten harmlos ist.