TL; DR
Un unicu editore npm hà speditu ottu picculi pacchetti chì i so nomi si leghjenu cum'è blocchi di custruzzione di ogni ghjornu per u sviluppu di blockchain è wallet, base58-utils, abi-encode, eth-dev, arb-kit, layer2-sdk, solana-key-utils, eth-wallet-helpers, e crypto-validate-libOgnunu cuntene un'utilità funzionale. Dopu à quell'utilità, però, hè aghjuntu un bloccu di codice autoinvucabile chì funziona in u mumentu chì u modulu hè impurtatu.
Circa 37 secondi dopu l'impurtazione, quellu bloccu decodifica un payload chì hè speditu in u pacchettu travestitu da un dispositivu di prova, u scrive in un schedariu oculatu sottu à u cartulare principale di l'utilizatore, si registra per riavvia ogni login in Windows, macOS è Linux, è lancia u script decodificatu cum'è un prucessu staccatu. Nunda di questu ùn si attiva durante l'installazione di npm; aspetta chì u codice sia impurtatu è eseguitu, chì hè un mumentu più tranquillu chè l'installazione.
U payload ùn hè micca opacu. Hè speditu cum'è base64 pianu, dunque a decifrazione di u "test fixture" recupera a seconda tappa in tuttu: a cripto-portafogliu è ladru di sicreti chì aspetta chì a macchina sia inattiva, raccoglie chjave private è frasi di seed, cripta ogni scuperta cù una chjave RSA-4096 codificata in modu hardcoded, è a esfiltra appuntendula à u almacenamentu IPFS publicu, cunnettendosi ogni 12 ore.
Seguitemu u cluster cum'è PhantomSyncTutti l'ottu pacchetti eranu dispunibili in u registru npm à u mumentu di l'analisi, publicati sottu un unicu contu.
| Ecosistema | npm |
| Packages | base58-utils, abi-encode, eth-dev, arb-kit, layer2-sdk, solana-key-utils, eth-wallet-helpers, crypto-validate-lib |
| Piattaforme mirate | Windows, macOS, Linux |
| Cumportamentu di u core | Dropper ritardatu, à tempu d'importazione, piattu cum'è un dispositivu di prova; installa a persistenza multipiattaforma |
| Carica utile | Portafogliu criptograficu è ladru di sicreti, crittografia RSA-4096, esfiltrazione via pinning IPFS publicu |
Anatomia di l'attaccu
Ogni pacchettu pare insignificante à a prima lettura. base58-utils, per esempiu, hè uni pochi di kilobyte di codice d'aiutu Base58 / Bitcoin-WIF à dipendenza zero - esattamente ciò chì u nome prumesse. U codice pertinente si trova dopu à u modulu modulu.esportazioni, induve un lettore chì scorri a cima di u schedariu ùn hè micca prubabile di vede: una funzione autoinvucante chì si programma cù un timer.
A catena di operazioni, ricustruita da a fonte di u pacchettu, funziona cusì:
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 processDui scelte di cuncepimentu si distinguenu.
U caricu viaghja cum'è un dispositivu di prova. A seconda tappa ùn hè micca scritta cum'è un codice evidente. Vive in test/fixtures/keypairs.dat, un schedariu base64 chì u so nome si mischia in un pacchettu chì pretende di gestisce e coppie di chjave. Per un umanu chì scorri u tarball, pare dati di mostra; per u codice aghjuntu, hè un script da decifrà è eseguisce. U dropper stessu ùn cuntene micca indirizzu di rete - quelli chì si trovanu in a seconda tappa - ma ùn ci hè micca offuscazione supplementaria: u fixture hè un unicu stratu di base64, dunque decifrà lu (base64 -d) recupera tuttu syncd.js è u so cumpurtamentu di rete. A prossima sezione spiega ciò chì face quella tappa recuperata.
A detonazione hè ritardata è ligata à l'impurtazione, micca à l'installazione. Perchè u trigger hè bisognu () più un timer di ~37 secondi invece di un hook d'installazione, u cumpurtamentu evita i cuntrolli chì guardanu solu u npm install passu, è u ritardu dura più di parechje esecuzioni di sandbox è CI di corta durata. À u mumentu chì qualcosa hè eseguitu, l'installazione chì hà tiratu u pacchettu hè finita dapoi tantu tempu.
Una volta chì u script decodificatu hè nant'à u discu à ~/.cache-db/.node-sync/syncd.js — un percorsu sceltu per leghje cum'è un cartulare di cache di rutina — u dropper face chì sopravviva à i riavvii nantu à tutte e trè piattaforme principali:
- Linux: una entrata cron chì riavvia u script. L'entrata hè stallata filtrendu u crontab esistente attraversu grep -v syncd, chì hà l'effettu secundariu di lascià a nova entrata fora di una lista chjara chì esegue grep per u listessu nome.
- Simon Boccanegra: un compitu pianificatu chjamatu WinNodeSync, impostu per esse eseguitu di novu à intervalli di 12 minuti.
- macOS: un travagliu lanciatu etichettatu com.apple.syncd, prisente in parechji di i pacchetti - una etichetta chì imita un serviziu di sistema Apple legittimu.
U script hè tandu iniziatu subitu cum'è un prucessu staccatu, dunque cuntinueghja à esse eseguitu dopu à a fine di u prugramma d'impurtazione.
Ciò chì face a seconda tappa
Siccomu l'apparecchiatura hè un unicu stratu base64, a seconda tappa decodifica in modu chjaru è pò esse letta in pienu. Tutti l'ottu pacchetti portanu una di e trè varianti di u listessu script, chì s'identifica in un cummentariu d'intestazione cum'è phantom syncd v3 - topo durmiente ("talpa addurmintata"). U so travagliu hè di arrubà u materiale di u portafogliu di criptovaluta è i sicreti di i sviluppatori è di spedisceli fora di a macchina. Funziona in questi passi:
- 1. Aspettate finu à chì a macchina sia inattiva. Prima di fà nunda, syncd.js verifica per quantu tempu l'utilizatore hè statu inattivu è procede solu oltre una certa soglia (intornu à 15 minuti) — xprintidle nant'à Linux, ioreg HIDIdleTime nant'à macOS, è una dumanda di tempu inattivu PowerShell nant'à Windows. A raccolta tende dunque à accade quandu nimu ùn hè à a tastiera.
- 2. Pigliate un interruttore d'attivazione cuntrullatu à distanzaU script estrae una piccula cunfigurazione da un dead-drop prima di agisce. A fonte primaria hè un URL grezzu gist di GitHub (gist.githubusercontent.com/juang55/…/cfg.txt); u script s'attiva solu s'è quella cunfigurazione leghje attivu=1Sè l'essenziale ùn hè micca dispunibile, si basa nantu à trè identificatori di cuntenutu IPFS codificati in modu fissu, recuperati per mezu di i gateway publichi. gateway.pinata.cloud, ipfs.io, e cloudflare-ipfs.comQuestu dà à l'operatore un cuntrollu di armamentu/disarmamentu dopu à u fattu è un fallback resistente à a caduta. (A variante più chjuca, spedita in crypto-validate-lib, aiutanti di portafogliu eth, e solana-key-utils, hà eliminatu u stratu essenziale è si basa solu nantu à l'identificatori IPFS.)
- 3. Raccoglie u materiale è i sicreti di u portafogliu. U script percorre u cartulare principale di l'utilizatore — ~/.config/solana, ~/.ethereum/keystore, ~/.funderia, ~/.cascu, ~ / .ssh, e Desktop/ducumenti/Downloads (cumpresi i nomi di e cartelle in lingua spagnola), più ~/.env è i fugliali rc di shell, è l'equivalente AppData locu in Windows. Hà cum'è scopu e chjave private Ethereum, e chjave WIF di Bitcoin, e frasi di seed BIP-39, e coppie di chjave Solana, u keystore JSON di Ethereum, e chjave SSH è e variabili d'ambiente chì portanu secreti. A variante più grande (in abi-encode, base58-utils, eth-dev) cuntene u dizziunariu BIP-39 cumpletu di 2048 parolle è usa espressioni regulare per strattu chjave individuali è frasi di semente validate da qualsiasi testu ch'ellu leghje; e duie varianti più chjuche invece currispondenu à i fugliali per parola chjave (sumente, mnemonicu, billetera, metamask, ensemble, sales, trezor, …) è caricate i fugliali interi.
- 4. Crittografia è esfiltrazione per mezu di un serviziu di pinning publicu. Ogni scuperta hè accumpagnata da una impronta digitale di l'ospite (nome d'utilizatore@nomeospite, piattaforma, timestamp) è criptatu cù una chjave publica RSA-4096 codificata in u script. U registru criptatu hè tandu caricatu appuntendulu à IPFS via api.pinata.cloud/pinning/pinJSONToIPFS, autenticatu cù credenziali API Pinata codificate in modu rigidu. Ùn ci hè micca un servitore C2 persunalizatu da sequestrà: i dati arrubati sò parcheggiati in un almacenamentu decentralizatu publicu, recuperabili da l'operatore utilizendu l'hash di cuntenutu risultanti. I caricamenti sò spaziati cù uni pochi di secondi di jitter aleatoriu, è un registru lucale di timestamp | tipu di chjave | hash restituitu hè tenutu à ~/.cache-db/.node-sync/.sl.
- 5. Persistite è emettite un segnale acusticu nantu à un ciclu di 12 ore. A seconda tappa ristabilisce a so propria persistenza - una entrata cron in Linux, una com.apple.syncd travagliu launchd in macOS, è un compitu pianificatu chjamatu Sync di WindowsNode nant'à Windows — impostu per esse eseguitu di novu ogni 12 ore. Nutate bè chì questu hè un diffirenti Nome di l'attività Windows da quella chì u dropper installa (WinNodeSync); tramindui valenu a pena di circà.
Timeline
L'ottu pacchetti sò stati publicati in una raffica stretta u 2026-07-13 è u 2026-07-14. Parechji portanu più di una versione; u dropper hè identicu in tutte e versioni, cù solu l'offset di linea chì si movenu quandu a dimensione di u codice di utilità benignu sopra cambia.
| Date | Event |
|---|---|
| 2026-07-13 | I primi pacchetti in u cluster cumpariscenu sottu un editore (solana-key-utils, eth-wallet-helpers, crypto-validate-lib è e prime versioni di u restu) |
| 2026-07-13 → 07-14 | I nomi rimanenti è e versioni successive sò stati publicati; e stesse navi dropper aghjunte in ogni |
| 2026-07-14 | Tutti l'ottu segnalati è analizati; ogni pacchettu hè sempre installabile da u registru |
À traversu u scoppiu, a reputazione di u registru di l'editore hè passata da più o menu neutra à l'iniziu di u cluster à fortemente negativa à misura chì e rilevazioni s'accumulavanu - un effettu secundariu osservabile di i pacchetti segnalati, micca una funzione cuncepita.
Indicatori di compromessi
Tutti l'indicatori quì sottu sò stati cunfirmati prisenti à u mumentu di l'analisi - quelli in e tabelle di "Seconda tappa" per decodificazione test/fixtures/keypairs.dat è leghjendu u recuperatu syncd.js.
File è percorsi
| pressure | rolu |
|---|---|
~/.cache-db/.node-sync/syncd.js | Seconda tappa decodificata, scritta cù u modu 0o700 |
~/.cache-db/.node-sync/.sl | Registru di esfiltrazione lucale (timestamp | tipu di chjave | hash IPFS) |
test/fixtures/keypairs.dat | Carica utile codificata in Base64 messa in scena in u tarball cum'è un "dispositivu di prova" |
Infrastruttura di rete di seconda tappa (recuperata da syncd.js)
| pressure | rolu |
|---|---|
gist.githubusercontent.com/juang55/b298754cb72942b1cdcf02ccd45cde2f/raw/cfg.txt | Attivazione dead-drop; u script funziona solu s'è a cunfigurazione leghje active=1 |
Qmcqz3w8j4qFQXDAXAxnrdc2oSX3nzBT4NqtpTqL8mr1ga | Riserva di cunfigurazione IPFS (CID) |
QmdTXoqVmTHY1i4ZWLdLkoQ9YChp5TXPh5cWXwnAYZt5iF | Riserva di cunfigurazione IPFS (CID) |
QmfJkLU5gdCpqbbqEjWYC2anXW9FmuEeSLLeLiHVJKYUjp | Riserva di cunfigurazione IPFS (CID) |
gateway.pinata.cloud, ipfs.io, cloudflare-ipfs.com | Gateway IPFS aduprati per recuperà a cunfigurazione di riserva |
api.pinata.cloud/pinning/pinJSONToIPFS | Puntu finale di esfiltrazione, dati arrubati attaccati à IPFS publicu |
| Chjave API Pinata | 13c766575b9270a9825d, credenziali di esfiltrazione codificate in modu rigidu |
Obiettivi di cullezzione di seconda tappa (recuperati da syncd.js)
| pressure | rolu |
|---|---|
~/.config/solana, ~/.ethereum/keystore, ~/.foundry, ~/.hardhat, ~/.ssh, ~/.env, schedari rc di shell | Repertori/schedarii cercati per chjave è sicreti |
AppData\Roaming\Solana, AppData\Local\ethereum\keystore | Equivalenti di Windows cercati |
| Tipi d'artefatti raccolti | Chjavi private ETH, Bitcoin WIF, frasi di seed BIP-39, coppie di chjave Solana, keystore JSON Ethereum, chjave SSH, variabili d'ambiente secrete |
Artefatti di persistenza
| piattaforma | pressure |
|---|---|
| Linux | lanciu di l'entrata cron syncd.js; installatu via crontab filtratu attraversu grep -v syncd |
| Windows | compitu pianificatu WinNodeSync (contagocce) è WindowsNodeSync (seconda tappa) |
| macOS | etichetta lanciata com.apple.syncd |
| tutte | A seconda tappa si ripete in un ciclu di 12 ore |
Comportamentale
- Funzione autoinvucante aghjunta dopu modulu.esportazioni, pianificazione a setTimeout di ~37,000 ms à l'impurtazione.
- prucessu_figliolu spawn ("nodu", ) cù l'opzione staccata impostata.
- Attivazione idle-dipendente in a seconda tappa (xprintidle / ioreg Tempu d'inattività HIDIdleTime / PowerShell; ~ soglia di 15 minuti).
- Crittografia RSA-4096 per ricerca, dopu HTTPS POST à una API di pinning IPFS publica.
Pacchetti è versioni (npm, editore solbuilder_io)
| pacchettu | versioni |
|---|---|
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[.]me, email micca verificatu, nisun contu di cuntrollu di fonte verificatu, nisun repositoriu culligatu.
Attribuzione è cumpurtamentu osservatu
L'ottu pacchetti spartenu un contu di editore è un payload. Ognunu hè un pacchettu triviale - uni pochi di kilobyte di codice di utilità reale cù u listessu dropper aghjuntu - publicatu senza un repositoriu culligatu è sottu un email di stile dispunibile micca verificatu. Quella uniformità, u percorsu di drop spartutu, l'etichette di persistenza spartute, è u spartutu keypairs.dat I fugliali di staging sò ciò chì liganu u cluster inseme.
I pacchetti sò insolitamente sinceri nantu à u so propiu cumpurtamentu. solana-key-utils porta cumenti in linea chì descrivenu u bloccu aghjuntu in termini simplici - unu l'etichetta PHANTOM: persistenza invisibile, un altru (in spagnolu) leghje Eseguisce u topu in u sfondate, "eseguite a talpa in u sfondate". Queste sò l'annotazioni di u codice di ciò chì face; u nome di a campagna in questu post hè trattu da quella prima etichetta inseme cù u nodu falsu "daemon di sincronizazione" (sincronizazione) chì a macchina di persistenza imita.
Descrivemu solu ciò chì u codice face in modu osservabile. A numinazione di i pacchetti - tutti i termini di criptovalute, portafogli è strumenti blockchain - indica u publicu di sviluppatori più prubabile di tiralli per nome; ùn stabilisce micca da per sè quale hè l'editore. A seconda tappa era cumpletamente recuperabile decifrànduli u fixture base64, è u so cumpurtamentu hè discrittu sopra: raccoglie chjave di portafogliu, frasi di seed è sicreti è li estrae, crittografati RSA, in un almacenamentu IPFS publicu via Pinata. Una scelta di cuncepimentu notevule hè l'assenza di un servitore C2 privatu - a cunfigurazione ghjunghje da un gist GitHub è IPFS, è i dati arrubati sò parcheggiati in un almacenamentu decentralizatu publicu chjave da hash di cuntenutu, tramindui più difficiuli da sequestrà chè un unicu host cuntrullatu da un attaccante. Nisun rapportu publicu precedente chì currisponde à questu cluster hè statu situatu à u mumentu di a scrittura.
Impattu, Tendenze è Guida per i Difensori
Quale hè espostu. Qualchissia chì hà aghjustatu unu di sti pacchetti à un prughjettu Node è dopu hà eseguitu u codice chì l'impurta. Siccomu a detonazione hè in tempu d'importazione piuttostu chè in tempu d'installazione, avè solu u pacchettu presente ùn hè micca abbastanza - ma qualsiasi usu nurmale chì carica u modulu ghjunghje à u payload. I nomi di l'esca sò destinati à i sviluppatori chì custruiscenu nantu à Ethereum, Solana, Arbitrum, Layer-2, è strumenti generali di portafogliu / codifica - a pupulazione chì hà più probabilità di avè esattamente l'assi chì a seconda tappa cerca. Qualchissia chì hà eseguitu unu di sti moduli nantu à una macchina chì cuntene chjave di portafogliu, frasi di seed, keystore, chjave SSH, o .env I sicreti devenu trattà quelle credenziali cum'è compromesse è rotà li.
Dui mudelli chì valenu a pena d'internalizà. Primu, carica utile cum'è fissaggio: spedizione di a seconda tappa cum'è base64 in un schedariu di dati cù un nome plausibile (test/fixtures/keypairs.dat) mantene a fonte visibile di u pacchettu pulita è spinge u cuntenutu maliziosu in un schedariu chì l'arnesi di revisione è i skim umani trattanu spessu cum'è dati inerti. Siconda, esecuzione ritardata in tempu d'importazione cù persistenza multipiattaformaSpostà u trigger da u ganciu d'installazione, aghjunghjendu un timer, è dopu persistendu in cron, attività pianificate è launchd hè un passu deliberatu luntanu da e tecniche di script d'installazione più rumorose chì a scansione automatizata di u registru osserva più da vicinu.
Guida per i difensori è i mantenitori:
- Trattà u codice aghjuntu dopu modulu.esportazioni cum'è una priorità di revisione - a logica di u dropper si piatta spessu sottu à a superficia di u modulu "reale".
- Ùn suppunite micca chì i fugliali di dati sianu inerti. Una goccia base64 sottu prova/calibri/ chì hè lettu à l'esecuzione è decodificatu hè adiacente à l'eseguibile; segnala letture in tempu d'esecuzione di i fugliali di fixture chì alimentanu un funzioni/eval/scrive-allora-spawn catina.
- Caccia à u percorsu di goccia ~/.cache-db/.node-sync/ è per l'unità di persistenza WinNodeSync (compitu pianificatu) è com.apple.syncd (launchd) nantu à e macchine di sviluppatore chì anu tiratu sti nomi.
- Avvisu nantu à i prucessi di Node chì creanu entrate cron, attività pianificate o travaglii launchd - e biblioteche legittime raramente facenu questu à l'impurtazione.
- Preferite i fugliali di serratura è e versioni appuntate, è verificate a diff di ogni nova piccula dipendenza "d'utilità", in particulare pacchetti à dipendenza zero publicati da conti micca verificati senza repositoriu culligatu.
Per i difensori di u registru, a cunclusione hè chì u monitoraghju di l'install-hook hè necessariu ma micca sufficiente: un dropper à tempu d'importazione, cù un timer ritardatu, messu in scena in un schedariu di dati passerà una verificazione solu à tempu d'installazione, è u passu di persistenza hè spessu u signale osservabile più forte chì resta da catturà.







