TL; DR
Ein einzelner npm-Publisher lieferte acht kleine Pakete aus, deren Namen sich wie alltägliche Bausteine für die Blockchain- und Wallet-Entwicklung lesen. base58-utils, abi-encode, eth-dev, arb-kit, layer2-sdk, solana-key-utils, eth-wallet-helpers und crypto-validate-libJedes Modul enthält ein funktionierendes Hilfsprogramm. Im Anschluss an dieses Hilfsprogramm wird jedoch ein sich selbst aufrufender Codeblock angehängt, der ausgeführt wird, sobald das Modul importiert wird.
Etwa 37 Sekunden nach dem ImportDieser Block dekodiert eine im Paket als Testdatei getarnte Nutzlast, schreibt sie in eine versteckte Datei im Home-Verzeichnis des Benutzers und registriert sich selbst für einen Neustart bei jedem login Unter Windows, macOS und Linux wird das dekodierte Skript als separater Prozess gestartet. Während der Installation mit `npm install` wird davon nichts ausgelöst; es wird gewartet, bis der Code importiert und ausgeführt wurde – ein deutlich ruhigerer Vorgang als die Installation selbst.
Die Nutzdaten sind nicht undurchsichtig. Sie werden als einfaches Base64 übertragen, sodass die Dekodierung der „Testumgebung“ die zweite Stufe vollständig wiederherstellt: a Krypto-Wallet- und Geheimnisdieb Dabei wird gewartet, bis die Maschine im Leerlauf ist, private Schlüssel und Seed-Phrasen gesammelt, jeder Fund mit einem fest codierten RSA-4096-Schlüssel verschlüsselt und exfiltriert, indem er ihn an einen öffentlichen IPFS-Speicher anheftet und alle 12 Stunden ein Signal zurücksendet.
Wir verfolgen den Cluster als PhantomSyncAlle acht Pakete waren zum Zeitpunkt der Analyse im npm-Repository verfügbar und wurden unter einem einzigen Konto veröffentlicht.
| Ökosystem | npm |
| Pakete | base58-utils, abi-encode, eth-dev, arb-kit, layer2-sdk, solana-key-utils, eth-wallet-helpers, crypto-validate-lib |
| Zielplattformen | Windows, MacOS, Linux |
| Kernverhalten | Verzögerter, zum Importzeitpunkt ausgeführter Dropper, der als Testumgebung versteckt ist; installiert plattformübergreifende Persistenz. |
| Nutzlast: XNUMX Kg | Krypto-Wallet- und Geheimnisdiebstahl, RSA-4096-Verschlüsselung, Datenexfiltration über öffentliches IPFS-Pinning |
Angriffsanatomie
Jede Verpackung wirkt auf den ersten Blick unscheinbar. base58-utilsBeispielsweise handelt es sich um wenige Kilobyte an Base58-/Bitcoin-WIF-Hilfscode ohne weitere Abhängigkeiten – genau das, was der Name verspricht. Der relevante Code befindet sich hier nachdem des Moduls module.exports, wo ein Leser, der nur den Anfang der Datei überfliegt, wahrscheinlich nicht hinsieht: eine sich selbst aufrufende Funktion, die sich selbst mithilfe eines Timers plant.
Die aus dem Quellcode des Pakets rekonstruierte Abfolge von Operationen läuft wie folgt ab:
1. Package is required() by the host project
2. A self-invoking function schedules a callback ~37,000 ms later (setTimeout)
3. On fire, the callback reads test/fixtures/keypairs.dat (a base64 blob)
4. It base64-decodes that blob into a Node.js script
5. It writes the script to ~/.cache-db/.node-sync/syncd.js (mode 0o700)
6. It installs login-persistence for that script (see below)
7. It spawns "node syncd.js" as a detached process Zwei Designentscheidungen stechen besonders hervor.
Die Nutzlast wird als Testvorrichtung transportiert. Die zweite Stufe ist nicht als offensichtlicher Code geschrieben. Sie befindet sich in test/fixtures/keypairs.dat, eine Base64-Datei, deren Name in ein Paket übergeht, das vorgibt, Schlüsselpaare zu verarbeiten. Für einen Menschen, der das Tarball überfliegt, sieht es aus wie Beispieldaten; für den angehängten Code ist es ein Skript zum Dekodieren und Ausführen. Der Dropper selbst enthält keine Netzwerkadresse – diese befindet sich in der zweiten Stufe –, aber es gibt keine weitere Verschleierung: Die Fixture ist eine einzelne Base64-Ebene, sodass das Dekodieren (base64 -d) stellt die vollständige syncd.js und dessen Netzwerkverhalten. Im nächsten Abschnitt wird erläutert, was diese wiederhergestellte Phase bewirkt.
Die Detonation ist verzögert und an den Import, nicht an die Installation gekoppelt. Weil der Auslöser erfordern() Zusätzlich wird ein Timer von etwa 37 Sekunden anstelle eines Installations-Hooks verwendet, wodurch Prüfungen umgangen werden, die nur die npm installieren Dieser Schritt verzögert sich so stark, dass er viele kurzlebige Sandbox- und CI-Läufe überdauert. Bis etwas ausgeführt wird, ist die Installation, die das Paket installiert hat, längst abgeschlossen.
Sobald das dekodierte Skript auf der Festplatte liegt, ~/.cache-db/.node-sync/syncd.js — ein Pfad, der wie ein normales Cache-Verzeichnis aussieht — der Dropper sorgt dafür, dass er Neustarts auf allen drei wichtigen Plattformen übersteht:
- Linux: Ein Cron-Eintrag, der das Skript neu startet. Der Eintrag wird durch Filtern der vorhandenen Crontab installiert. grep -v syncdDies hat den Nebeneffekt, dass der neue Eintrag in einer einfachen Liste, die nach demselben Namen sucht, nicht angezeigt wird.
- Windows: eine geplante Aufgabe namens WinNodeSync, so eingestellt, dass es in einem 12-Minuten-Intervall erneut ausgeführt wird.
- macOS: ein launchd-Job mit der Bezeichnung com.apple.syncd, das in mehreren der Pakete vorhanden ist – ein Label, das einen legitimen Apple-Systemdienst imitiert.
Das Skript wird dann sofort als unabhängiger Prozess gestartet und läuft daher auch nach Beendigung des importierenden Programms weiter.
Was die zweite Phase bewirkt
Da die Fixture aus einer einzigen Base64-Ebene besteht, lässt sich die zweite Stufe fehlerfrei dekodieren und vollständig auslesen. Alle acht Pakete enthalten eine von drei Varianten desselben Skripts, das sich in einem Header-Kommentar als … identifiziert. Phantom Syncd v3 – aktueller Stand („Schlafender Maulwurf“). Seine Aufgabe ist es, Daten von Kryptowährungs-Wallets und Entwicklergeheimnisse zu stehlen und vom Rechner zu exportieren. Er läuft dabei in folgenden Schritten ab:
- 1. Warten Sie, bis die Maschine im Leerlauf ist. Bevor Sie irgendetwas tun, syncd.js prüft, wie lange der Benutzer inaktiv war und fährt erst nach Überschreiten einer bestimmten Schwelle (etwa 15 Minuten) fort. xprintidle unter Linux, ioreg HIDIdleTime unter macOS und eine PowerShell-Abfrage zur Leerlaufzeit unter Windows. Die Datenerfassung erfolgt daher tendenziell dann, wenn niemand am Computer arbeitet.
- 2. Besorgen Sie sich einen ferngesteuerten Aktivierungsschalter.Das Skript lädt vor seiner Ausführung eine kleine Konfiguration aus einem toten Ordner herunter. Die primäre Quelle ist eine Roh-URL eines GitHub-Gists (gist.githubusercontent.com/juang55/…/cfg.txtDas Skript wird nur aktiviert, wenn diese Konfiguration den Wert enthält. aktiv=1Falls das Gist nicht verfügbar ist, werden drei fest codierte IPFS-Inhaltskennungen verwendet, die über die öffentlichen Gateways abgerufen werden. gateway.pinata.cloud, ipfs.io und cloudflare-ipfs.comDies ermöglicht dem Bediener das nachträgliche Aktivieren/Deaktivieren der Waffe und bietet eine robuste Sicherheitsfunktion. (Die kleinste Variante wird ausgeliefert in …) crypto-validate-lib, eth-wallet-helpers und solana-key-utils(Die Gist-Ebene wurde entfernt, und es werden ausschließlich die IPFS-Kennungen verwendet.)
- 3. Sammeln Sie Geldbeutelmaterialien und Geheimnisse. Das Skript durchläuft das Home-Verzeichnis des Benutzers — ~/.config/solana, ~/.ethereum/keystore, ~/.foundry, ~/.hardhat, ~ / .ssh und Desktop/Unterlagen/Downloads (einschließlich spanischsprachiger Ordnernamen), plus ~/.env und Shell-RC-Dateien sowie das Äquivalent AppData Speicherorte unter Windows. Es zielt auf Ethereum-Privatschlüssel, Bitcoin-WIF-Schlüssel, BIP-39-Seed-Phrasen, Solana-Schlüsselpaare, Ethereum-Keystore-JSON, SSH-Schlüssel und geheimnishaltige Umgebungsvariablen ab. Die größere Variante (in abi-kodieren, base58-utils, eth-dev) enthält das vollständige 2048 Wörter umfassende BIP-39-Wörterbuch und verwendet reguläre Ausdrücke, um Extrakt einzelne Schlüssel und validierte Seed-Phrasen aus jedem gelesenen Text; die beiden kleineren Varianten gleichen stattdessen Dateien anhand von Schlüsselwörtern ab (Samen, mnemotechnik, Brieftasche, metamask, Phantom, Hauptbuch, Safe, …) und laden Sie ganze Dateien hoch.
- 4. Verschlüsseln und exfiltrieren Sie die Daten über einen öffentlichen Pinning-Dienst. Jeder Befund wird mit einem Wirtsfingerabdruck kombiniert (Benutzername@Hostname, Plattform, Zeitstempel) und mit einem fest im Skript eingebetteten RSA-4096-Schlüssel verschlüsselt. Der verschlüsselte Datensatz wird anschließend durch Anheften an IPFS hochgeladen. api.pinata.cloud/pinning/pinJSONToIPFSDie Authentifizierung erfolgt mit fest codierten Pinata-API-Zugangsdaten. Es gibt keinen speziellen C2-Server, der abgefangen werden könnte: Die gestohlenen Daten werden in einem öffentlichen, dezentralen Speicher abgelegt und können vom Betreiber mithilfe der resultierenden Inhalts-Hashes abgerufen werden. Die Uploads erfolgen mit einigen Sekunden zufälligem Jitter, und es wird ein lokales Protokoll geführt. Zeitstempel | Schlüsseltyp | Rückgabewert wird gehalten bei ~/.cache-db/.node-sync/.sl.
- 5. Bleiben Sie dran und senden Sie im 12-Stunden-Rhythmus ein Leuchtfeuer. Die zweite Phase stellt ihre eigene Persistenz wieder her – ein Cron-Eintrag unter Linux, ein com.apple.syncd launchd-Job auf macOS und eine geplante Aufgabe namens WindowsNodeSync Unter Windows – so eingestellt, dass es alle 12 Stunden erneut ausgeführt wird. Hinweis: Dies ist ein anders Windows-Aufgabenname derjenigen, die der Dropper installiert (WinNodeSync); beide sind die Suche wert.
Geschichte
Die acht Pakete wurden am 13.07.2026 und 14.07.2026 kurz nacheinander veröffentlicht. Einige enthalten mehr als eine Version; der Dropper ist in allen Versionen identisch, lediglich die Zeilenverschiebungen ändern sich, wenn sich die Größe des darüber liegenden harmlosen Hilfsprogrammcodes ändert.
| Datum | Event |
|---|---|
| 2026-07-13 | Die ersten Pakete im Cluster erscheinen unter einem Herausgeber (solana-key-utils, eth-wallet-helpers, crypto-validate-lib und frühe Versionen des Restes) |
| 2026-07-13 → 07-14 | Die übrigen Namen und Folgeversionen wurden veröffentlicht; in jeder Version wurden die gleichen Abwurfschiffe hinzugefügt. |
| 2026-07-14 | Alle acht Pakete wurden markiert und analysiert; jedes Paket kann weiterhin über die Registry installiert werden. |
Im Verlauf des Ausbruchs verschob sich der Ruf des Herausgebers in der Registry von annähernd neutral zu Beginn des Clusters hin zu stark negativ, als sich die Erkennungen häuften – eine beobachtbare Nebenwirkung der Kennzeichnung der Pakete, keine beabsichtigte Funktion.
Kompromissindikatoren
Alle unten aufgeführten Indikatoren wurden zum Analysezeitpunkt als vorhanden bestätigt – diejenigen in den Tabellen der „zweiten Phase“ durch Dekodierung. test/fixtures/keypairs.dat und das wiedergefundene lesen syncd.js.
Dateien und Pfade
| Indikator | Funktion / Rolle (Role) * |
|---|---|
~/.cache-db/.node-sync/syncd.js | Dekodierte zweite Stufe, geschrieben im Modus 0o700 |
~/.cache-db/.node-sync/.sl | Lokales Exfiltrationsprotokoll (Zeitstempel | Schlüsseltyp | IPFS-Hash) |
test/fixtures/keypairs.dat | Base64-kodierte Nutzdaten als „Testumgebung“ im Tarball bereitgestellt |
Zweitstufige Netzwerkinfrastruktur (wiederhergestellt aus syncd.js)
| Indikator | Funktion / Rolle (Role) * |
|---|---|
gist.githubusercontent.com/juang55/b298754cb72942b1cdcf02ccd45cde2f/raw/cfg.txt | Aktivierungs-Deaddrop; Skript wird nur ausgeführt, wenn die Konfiguration Folgendes enthält active=1 |
Qmcqz3w8j4qFQXDAXAxnrdc2oSX3nzBT4NqtpTqL8mr1ga | IPFS-Konfigurationsfallback (CID) |
QmdTXoqVmTHY1i4ZWLdLkoQ9YChp5TXPh5cWXwnAYZt5iF | IPFS-Konfigurationsfallback (CID) |
QmfJkLU5gdCpqbbqEjWYC2anXW9FmuEeSLLeLiHVJKYUjp | IPFS-Konfigurationsfallback (CID) |
gateway.pinata.cloud, ipfs.io, cloudflare-ipfs.com | IPFS-Gateways, die zum Abrufen der Fallback-Konfiguration verwendet wurden |
api.pinata.cloud/pinning/pinJSONToIPFS | Exfiltrationsendpunkt, gestohlene Daten werden an ein öffentliches IPFS-System gebunden |
| Pinata API-Schlüssel | 13c766575b9270a9825dfest codierte Exfiltrations-Anmeldeinformationen |
Sammlungsziele der zweiten Stufe (wiederhergestellt aus syncd.js)
| Indikator | Funktion / Rolle (Role) * |
|---|---|
~/.config/solana, ~/.ethereum/keystore, ~/.foundry, ~/.hardhat, ~/.ssh, ~/.env, Shell-RC-Dateien | Verzeichnisse/Dateien, die nach Schlüsseln und Geheimnissen durchsucht wurden |
AppData\Roaming\Solana, AppData\Local\ethereum\keystore | Windows-Äquivalente gesucht |
| Geerntete Artefakttypen | ETH-Privatschlüssel, Bitcoin WIF, BIP-39-Seed-Phrasen, Solana-Schlüsselpaare, Ethereum-Keystore-JSON, SSH-Schlüssel, geheime Umgebungsvariablen |
Persistenzartefakte
| Plattform | Indikator |
|---|---|
| Linux | Cron-Eintrag starten syncd.js; installiert über crontab, gefiltert durch grep -v syncd |
| Windows | geplante Aufgabe WinNodeSync (Tropfer) und WindowsNodeSync (zweite Phase) |
| macOS | launchd Label com.apple.syncd |
| Alle | Die zweite Phase wiederholt sich in einem 12-Stunden-Zyklus. |
Verhaltens-
- Selbstaufrufende Funktion angehängt nach module.exports, die Planung einer setTimeout von ca. 37,000 ms beim Import.
- child_process spawn(“node”, ) mit dem separaten Optionssatz.
- Leerlaufgesteuerte Aktivierung in der zweiten Phase (xprintidle / ioreg HIDIdleTime / PowerShell-Leerlaufzeit; Schwellenwert ~15 Minuten).
- Nach der RSA-4096-Verschlüsselung wird HTTPS verwendet. jetzt lesen zu einer öffentlichen IPFS-Pinning-API.
Pakete und Versionen (npm, Herausgeber solbuilder_io)
| Verpackung | Versionen |
|---|---|
base58-utils | 1.0.0, 1.0.1, 1.0.3 |
abi-encode | 1.0.0, 1.0.1, 1.0.2 |
eth-dev | 1.0.0, 1.0.1, 1.0.2 |
arb-kit | 1.0.0, 1.0.1 |
layer2-sdk | 1.0.0, 1.0.1 |
solana-key-utils | 1.0.0 |
eth-wallet-helpers | 1.0.0 |
crypto-validate-lib | 1.0.0 |
Publisher
- solbuilder_io - angel_lopez89[@]proton[.]meE-Mail-Adresse nicht verifiziert, kein verifiziertes Quellcodeverwaltungskonto, kein verknüpftes Repository.
Attribution und beobachtetes Verhalten
Die acht Pakete teilen sich ein Publisher-Konto und eine Payload. Jedes ist ein einfaches Paket – wenige Kilobyte nützlicher Code mit demselben Dropper –, das ohne verknüpftes Repository und unter einer nicht verifizierten Wegwerf-E-Mail-Adresse veröffentlicht wurde. Diese Einheitlichkeit, der gemeinsame Drop-Pfad, die gemeinsamen Persistenz-Labels und die gemeinsame keypairs.dat Die Staging-Dateien sind das, was den Cluster zusammenhält.
Die Pakete sind ungewöhnlich offenherzig, was ihr eigenes Verhalten betrifft. solana-key-utils enthält Inline-Kommentare, die den angehängten Block in einfachen Worten beschreiben – einer davon bezeichnet ihn. PHANTOM: Unsichtbare Persistenz, ein anderer (auf Spanisch) lautet Ejecutar topo en background„den Maulwurf im Hintergrund ausführen.“ Dies sind die eigenen Anmerkungen des Codes zu seiner Funktionsweise; der Kampagnenname in diesem Beitrag leitet sich von dieser ersten Bezeichnung zusammen mit dem fiktiven Knoten „Sync-Daemon“ ab (synchronisiert) dass der Persistenzmechanismus ihn imitiert.
Wir beschreiben lediglich die sichtbare Funktion des Codes. Die Benennung der Pakete – allesamt Begriffe aus den Bereichen Krypto, Wallets und Blockchain-Tools – deutet auf die Entwicklergruppe hin, die sie höchstwahrscheinlich anhand des Namens herunterladen wird; sie gibt jedoch nicht automatisch Aufschluss über den Herausgeber. Die zweite Phase war durch Dekodierung der Base64-Fixture vollständig rekonstruierbar, und ihr Verhalten wurde bereits beschrieben: Sie sammelt Wallet-Schlüssel, Seed-Phrasen und Geheimnisse und exfiltriert diese RSA-verschlüsselt über Pinata in einen öffentlichen IPFS-Speicher. Eine bemerkenswerte Designentscheidung ist das Fehlen eines privaten C2-Servers – die Konfiguration erfolgt über ein GitHub-Gist und IPFS, und die gestohlenen Daten werden in einem öffentlichen, dezentralen Speicher abgelegt, der über einen Content-Hash verschlüsselt ist. Beides ist deutlich schwieriger zu übernehmen als ein einzelner, vom Angreifer kontrollierter Host. Zum Zeitpunkt der Erstellung dieses Dokuments waren keine vergleichbaren öffentlichen Berichte über diesen Cluster bekannt.
Auswirkungen, Trends und Leitlinien für Verteidiger
Wer wird entlarvt? Jeder, der eines dieser Pakete zu einem Node-Projekt hinzugefügt und anschließend Code ausgeführt hat, der es importiert, ist betroffen. Da die Detonation beim Import und nicht bei der Installation erfolgt, reicht es nicht aus, das Paket lediglich zu besitzen – jede normale Nutzung, die das Modul lädt, führt jedoch zur Payload. Die Ködernamen zielen auf Entwickler ab, die auf Ethereum, Solana, Arbitrum, Layer-2 und allgemeinen Wallet-/Verschlüsselungstools arbeiten – also genau die Gruppe, die am ehesten über die Assets verfügt, nach denen die zweite Phase sucht. Jeder, der eines dieser Module auf einem Rechner ausgeführt hat, der Wallet-Schlüssel, Seed-Phrasen, Keystores, SSH-Schlüssel oder ähnliches enthält, ist betroffen. .env secrets sollte diese Zugangsdaten als kompromittiert behandeln und regelmäßig austauschen.
Zwei Muster, die es wert sind, verinnerlicht zu werden. Erstens Nutzlast als Vorrichtung: Versand der zweiten Stufe als Base64 in einer plausibel benannten Datendatei (test/fixtures/keypairs.datDadurch bleibt der sichtbare Quellcode des Pakets sauber, und der schädliche Inhalt wird in eine Datei verschoben, die von Prüfwerkzeugen und menschlichen Überfliegern oft als irrelevant eingestuft wird. Zweitens Importzeitverzögerte Ausführung mit plattformübergreifender Persistenz: Die Verlagerung des Auslösers vom Installations-Hook, das Hinzufügen eines Timers und die anschließende Persistenz über Cron, geplante Aufgaben und Launchd hinweg ist ein bewusster Schritt weg von den lauteren Installationsskripttechniken, die von der automatisierten Registry-Überprüfung am genauesten überwacht werden.
Leitfaden für Verteidiger und Instandhalter:
- Behandeln Sie den angehängten Code nach module.exports Als Priorität bei der Überprüfung gilt: Die Dropper-Logik verbirgt sich häufig unter der Oberfläche des „eigentlichen“ Moduls.
- Gehen Sie nicht davon aus, dass Datendateien inaktiv sind. Ein Base64-Blob unter Testvorrichtungen/ Das wird zur Laufzeit gelesen und dekodiert und ist ausführbaren-nah; kennzeichnen Sie Laufzeitlesevorgänge von Fixture-Dateien, die eine Datei speisen Funktion/eval/write-then-laichen Kette.
- Suche nach dem Abstiegspfad ~/.cache-db/.node-sync/ und für die Persistenzeinheiten WinNodeSync (geplante Aufgabe) und com.apple.syncd (launchd) auf Entwicklerrechnern, die diese Namen abgerufen haben.
- Warnung bei Node-Prozessen, die Cron-Einträge, geplante Aufgaben oder launchd-Jobs erstellen – legitime Bibliotheken tun dies beim Import nur selten.
- Bevorzugen Sie Sperrdateien und festgelegte Versionen und überprüfen Sie die Unterschiede jeder neuen kleinen „Hilfs“-Abhängigkeit, insbesondere veröffentlichte Pakete ohne Abhängigkeiten von nicht verifizierten Accounts ohne verknüpftes Repository.
Für Registry-Verteidiger bedeutet dies, dass die Überwachung von Installations-Hooks zwar notwendig, aber nicht ausreichend ist: Ein Dropper, der zur Importzeit mit einem Timer verzögert in einer Datendatei platziert wird, besteht eine reine Installationszeitprüfung, und der Persistenzschritt ist oft das lauteste noch zu erkennende Signal, das es abzufangen gilt.




