PhantomSync: els paquets de criptografia npm amaguen el lladre de carteres

PhantomSync: vuit paquets npm per a desenvolupadors de criptomonedes amaguen un dropper autopersistent i retardat.

TL; DR

Un únic editor de npm va enviar vuit paquets petits els noms dels quals es llegeixen com a blocs de construcció quotidians per al desenvolupament de blockchain i moneders. base58-utils, abi-encode, eth-dev, arb-kit, layer2-sdk, solana-key-utils, eth-wallet-helpersi crypto-validate-libCadascun conté una utilitat que funciona. Tanmateix, després d'aquesta utilitat s'afegeix un bloc de codi que s'autoinvoca i s'executa en el moment en què s'importa el mòdul.

Aproximadament 37 segons després de la importació, aquest bloc descodifica una càrrega útil que s'envia dins del paquet disfressada de dispositiu de prova, l'escriu en un fitxer ocult al directori principal de l'usuari, i es registra per reiniciar-se cada login a Windows, macOS i Linux, i inicia l'script descodificat com un procés separat. Res d'això s'activa durant la instal·lació de npm; espera que el codi s'importi i s'executi, que és un moment més silenciós que la instal·lació.

La càrrega útil no és opaca. S'envia com a base64 simple, de manera que descodificar el "fixture de prova" recupera la segona etapa completament: a moneders criptogràfics i lladre de secrets que espera que la màquina estigui inactiva, recol·lecta claus privades i frases llavor, xifra cada troballa amb una clau RSA-4096 codificada i l'exfiltra fixant-la a l'emmagatzematge IPFS públic, enviant una senyal cada 12 hores.

Fem un seguiment del clúster com a PhantomSyncEls vuit paquets estaven actius al registre npm en el moment de l'anàlisi, publicats sota un únic compte.

Ecosistemanpm
Paquetsbase58-utils, abi-encode, eth-dev, arb-kit, layer2-sdk, solana-key-utils, eth-wallet-helpers, crypto-validate-lib
Plataformes a les quals s'ha dirigitWindows, macOS, Linux
Comportament bàsicDropper en temps d'importació retardat ocult com a dispositiu de prova; instal·la la persistència multiplataforma
Carrega útilMoneder de criptomonedes i lladre de secrets, xifratge RSA-4096, exfiltració mitjançant pinning IPFS públic

Anatomia d'atac

Cap paquet sembla habitual a primera vista. base58-utils, per exemple, són uns quants quilobytes de codi auxiliar Base58 / Bitcoin-WIF de dependència zero, exactament el que promet el nom. El codi rellevant es troba després el mòdul mòdul.exportacions, on és poc probable que un lector que fulleja la part superior del fitxer hi miri: una funció que s'invoca a si mateixa i que es programa amb un temporitzador.

La cadena d'operacions, reconstruïda a partir del codi font del paquet, funciona així:

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

Destaquen dues opcions de disseny.

La càrrega útil viatja com a dispositiu de prova. La segona etapa no està escrita com a codi obvi. Viu a test/fixtures/keypairs.dat, un fitxer base64 el nom del qual es barreja amb un paquet que afirma gestionar parells de claus. Per a un humà que fulleja el tarball, sembla dades de mostra; per al codi afegit, és un script per descodificar i executar. El dropper en si no conté cap adreça de xarxa (les que es troben a la segona etapa), però no hi ha cap ofuscació addicional: el dispositiu és una sola capa de base64, de manera que descodificar-lo (base64 -d) recupera la totalitat syncd.js i el seu comportament de xarxa. La següent secció explica què fa aquesta etapa recuperada.

La detonació es retarda i està lligada a la importació, no a la instal·lació. Perquè el detonant és requerir () a més d'un temporitzador de ~37 segons en lloc d'un ganxo d'instal·lació, el comportament evita les comprovacions que només observen el npm instal·lar pas, i el retard dura més que moltes execucions curtes de sandbox i CI. Quan s'executa alguna cosa, la instal·lació que ha tret el paquet ja fa temps que ha acabat.

Un cop l'script descodificat estigui al disc a ~/.cache-db/.node-sync/syncd.js — una ruta escollida per llegir-se com un directori de memòria cau rutinari — el dropper fa que sobrevisqui als reinicis a les tres plataformes principals:

  • Linux: una entrada cron que rellança l'script. L'entrada s'instal·la filtrant la crontab existent a través de grep -v sincronització, que té l'efecte secundari d'ometre la nova entrada d'una llista plana que executa una funció grep pel mateix nom.
  • Windows: una tasca programada anomenada WinNodeSync, configurat per tornar a executar-se en intervals de 12 minuts.
  • macOS: una tasca launchd etiquetada com.apple.syncd, present en diversos dels paquets, una etiqueta que imita un servei de sistema legítim d'Apple.

L'script s'inicia immediatament com un procés separat, de manera que continua executant-se després que el programa d'importació finalitzi.

Què fa la segona etapa

Com que el dispositiu és una sola capa base64, la segona etapa descodifica netament i es pot llegir completament. Els vuit paquets porten una de les tres variants del mateix script, que s'identifica en un comentari de capçalera com a phantom syncd v3 — topo durmiente ("talp adormit"). La seva feina és robar material de moneders de criptomonedes i secrets de desenvolupadors i enviar-los fora de la màquina. S'executa en aquests passos:

  • 1. Espereu fins que la màquina estigui inactiva. Abans de fer res, syncd.js comprova quant de temps ha estat inactiu l'usuari i només continua superant un llindar (uns 15 minuts) — xprintidle a Linux, ioreg HIDIdleTime a macOS i una consulta de temps d'inactivitat de PowerShell a Windows. Per tant, la recol·lecció de dades tendeix a produir-se quan no hi ha ningú al teclat.
  • 2. Obtenir un interruptor d'activació controlat remotament. L'script extreu una petita configuració d'un punt mort abans d'actuar. La font principal és una URL en brut de GitHub gist (gist.githubusercontent.com/juang55/…/cfg.txt); l'script només s'activa si aquesta configuració llegeix actiu=1Si el gist no està disponible, recorre a tres identificadors de contingut IPFS codificats, obtinguts a través de les passarel·les públiques. gateway.pinata.cloud, ipfs.ioi cloudflare-ipfs.comAixò proporciona a l'operador un control d'armat/desarmat posterior i un sistema de recuperació resistent a la caiguda. (La variant més petita, enviada en cripto-validar-lib, ajudants de cartera ethi solana-key-utils, ha eliminat la capa essencial i es basa només en els identificadors IPFS.)
  • 3. Recollir material i secrets de la cartera. L'script recorre el directori principal de l'usuari — ~/.config/solana, ~/.ethereum/keystore, ~/.foundry, ~/.casquet dur, ~ / .Sshi escriptori/documents/Descàrregues (inclosos els noms de carpetes en castellà), a més de ~/.env i fitxers rc de shell, i l'equivalent Dades d'aplicacions ubicacions a Windows. Té com a objectiu les claus privades d'Ethereum, les claus WIF de Bitcoin, les frases de llavor BIP-39, els parells de claus Solana, el JSON del magatzem de claus d'Ethereum, les claus SSH i les variables d'entorn que contenen secrets. La variant més gran (a abi-encode, base58-utils, eth-dev) conté el diccionari BIP-39 complet de 2048 paraules i utilitza expressions regulars per a extreure claus individuals i frases llavor validades de qualsevol text que llegeixi; les dues variants més petites, en canvi, coincideixen amb els fitxers per paraula clau (llavor, mnemotècnic, cartera, metamask, fantasma, llibre major, caixa, …) i penjar fitxers sencers.
  • 4. Xifrar i exfiltrar a través d'un servei públic de pinning. Cada troballa s'agrupa amb una empremta digital de l'amfitrió (nom d'usuari@nom d'amfitrió, plataforma, marca de temps) i xifrat amb una clau pública RSA-4096 codificada integrada a l'script. El registre xifrat es carrega fixant-lo a IPFS mitjançant api.pinata.cloud/pinning/pinJSONToIPFS, autenticat amb credencials de l'API de Pinata codificades. No hi ha cap servidor C2 a mida per confiscar: les dades robades s'emmagatzemen en un emmagatzematge públic descentralitzat, que l'operador pot recuperar mitjançant els hashes de contingut resultants. Les càrregues estan espaiades amb uns segons de jitter aleatori i un registre local de marca de temps | tipus de clau | hash retornat es manté a ~/.cache-db/.node-sync/.sl.
  • 5. Persistiu i emeteu la balisa en un cicle de 12 hores. La segona etapa restableix la seva pròpia persistència: una entrada cron a Linux, una com.apple.syncd tasca launchd a macOS i una tasca programada anomenada Sync de nodes de Windows a Windows, configurat per tornar a executar-se cada 12 hores. Tingueu en compte que això és un diferent Nom de la tasca de Windows d'aquella que instal·la el comptagotes (WinNodeSync); val la pena buscar-los tots dos.

Línia de temps

Els vuit paquets es van publicar en una ràfega ajustada els dies 13 i 14 de juliol de 2026. Diversos tenen més d'una versió; el dropper és idèntic a totes les versions, només que els desplaçaments de línia canvien a mesura que canvia la mida del codi d'utilitat benigne que hi ha a sobre.

Data esdeveniment
2026-07-13 Els primers paquets del clúster apareixen sota un editor (solana-key-utils, eth-wallet-helpers, crypto-validate-lib i versions primerenques de la resta)
2026-07-13 → 07-14 Noms restants i versions posteriors publicades; les mateixes naus de descàrrega afegides a cadascuna
2026-07-14 Tots vuit marcats i analitzats; tots els paquets encara es poden instal·lar des del registre

Al llarg de l'esclat, la reputació del registre de l'editor va passar de ser més o menys neutral a l'inici del clúster a ser fortament negativa a mesura que s'acumulaven deteccions, un efecte secundari observable dels paquets marcats, no una característica dissenyada.

Indicadors de compromís

Tots els indicadors següents es van confirmar presents en el moment de l'anàlisi, els de les taules de la "segona fase" mitjançant la descodificació test/fixtures/keypairs.dat i llegint el recuperat syncd.js.

Fitxers i camins

Indicador Paper
~/.cache-db/.node-sync/syncd.js Segona etapa descodificada, escrita amb el mode 0o700
~/.cache-db/.node-sync/.sl Registre d'exfiltració local (marca de temps | tipus de clau | resum IPFS)
test/fixtures/keypairs.dat Càrrega útil codificada en Base64 dins del tarball com a "fixació de prova"

Infraestructura de xarxa de segona fase (recuperada de syncd.js)

Indicador Paper
gist.githubusercontent.com/juang55/b298754cb72942b1cdcf02ccd45cde2f/raw/cfg.txt Activació sense sortida; l'script només s'executa si la configuració llegeix active=1
Qmcqz3w8j4qFQXDAXAxnrdc2oSX3nzBT4NqtpTqL8mr1ga Configuració alternativa d'IPFS (CID)
QmdTXoqVmTHY1i4ZWLdLkoQ9YChp5TXPh5cWXwnAYZt5iF Configuració alternativa d'IPFS (CID)
QmfJkLU5gdCpqbbqEjWYC2anXW9FmuEeSLLeLiHVJKYUjp Configuració alternativa d'IPFS (CID)
gateway.pinata.cloud, ipfs.io, cloudflare-ipfs.com Passarel·les IPFS utilitzades per obtenir la configuració alternativa
api.pinata.cloud/pinning/pinJSONToIPFS Punt final d'exfiltració, dades robades fixades a IPFS públic
Clau de l'API de Pinata 13c766575b9270a9825d, credencial d'exfiltració codificada

Objectius de recopilació de segona fase (recuperats de syncd.js)

Indicador Paper
~/.config/solana, ~/.ethereum/keystore, ~/.foundry, ~/.hardhat, ~/.ssh, ~/.env, fitxers rc de shell Directoris/fitxers cercats per claus i secrets
AppData\Roaming\Solana, AppData\Local\ethereum\keystore Equivalents de Windows cercats
Tipus d'artefactes recol·lectats Claus privades d'ETH, WIF de Bitcoin, frases de llavor BIP-39, parells de claus Solana, JSON del magatzem de claus Ethereum, claus SSH, variables d'entorn secretes

Artefactes de persistència

plataforma Indicador
Linux llançament d'entrada cron syncd.js; instal·lat mitjançant crontab filtrat a través de grep -v syncd
Windows tasca programada WinNodeSync (degoteig) i WindowsNodeSync (segona etapa)
macOS etiqueta llançada com.apple.syncd
All La segona etapa es repeteix en un cicle de 12 hores

Comportamental

  • Funció autoinvocadora afegida després de mòdul.exportacions, programació a setTimeout de ~37,000 ms en la importació.
  • procés_fill spawn("node", ) amb l'opció desconnectada definida.
  • Activació controlada per inactivitat en la segona etapa (xprintidle / ioreg HIDIdleTime / Temps d'inactivitat de PowerShell; llindar d'aproximadament 15 minuts).
  • Xifratge RSA-4096 per troballa, després HTTPS PAL a una API de fixació IPFS pública.

Paquets i versions (npm, editor solbuilder_io)

paquet versions
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

Editor

  • solbuilder_io - angel_lopez89[@]proton[.]me, correu electrònic sense verificar, cap compte de control de codi font verificat, cap repositori enllaçat.

Atribució i comportament observat

Els vuit paquets comparteixen un compte d'editor i una càrrega útil. Cadascun és un paquet trivial (uns quants quilobytes de codi d'utilitat real amb el mateix dropper afegit) publicat sense un repositori enllaçat i sota un correu electrònic d'estil d'un sol ús no verificat. Aquesta uniformitat, la ruta de drop compartida, les etiquetes de persistència compartides i la compartició... parells de claus.dat El fitxer de staging és el que uneix el clúster.

Els paquets són inusualment sincers sobre el seu propi comportament. solana-key-utils porta comentaris en línia que descriuen el bloc afegit en termes senzills: s'etiqueta FANTASMA: persistència invisible, un altre (en castellà) diu Executar topogràfic en segon pla, «executa el talp en segon pla». Aquestes són les anotacions del codi sobre el que fa; el nom de la campanya en aquesta publicació s'extreu d'aquesta primera etiqueta juntament amb el node fals «sync daemon» (sincronització) que la maquinària de persistència imita.

Només descrivim allò que fa el codi de manera observable. La denominació dels paquets (tots termes de criptografia, cartera i eines de blockchain) indica el públic de desenvolupadors que és més probable que els obtingui pel seu nom; no estableix per si sol qui n'és l'editor. La segona etapa es va poder recuperar completament descodificant el dispositiu base64, i el seu comportament es descriu més amunt: recopila claus de cartera, frases llavor i secrets i els exfiltra, xifrats amb RSA, a l'emmagatzematge IPFS públic mitjançant Pinata. Una elecció de disseny notable és l'absència d'un servidor C2 privat: la configuració arriba d'un gist de GitHub i IPFS, i les dades robades s'emmagatzemen en un emmagatzematge públic descentralitzat amb clau hash de contingut, ambdós més difícils de confiscar que un únic host controlat per un atacant. No es va localitzar cap informe públic previ que coincidís amb aquest clúster en el moment d'escriure aquest article.

Impacte, tendències i orientació per a defensors

Qui està exposat. Qualsevol persona que hagi afegit un d'aquests paquets a un projecte de Node i després hagi executat codi que l'importa. Com que la detonació és en temps d'importació en lloc d'instal·lació, simplement tenir el paquet present no és suficient, però qualsevol ús normal que carregui el mòdul arriba a la càrrega útil. Els noms d'esquer es dirigeixen a desenvolupadors que construeixen sobre Ethereum, Solana, Arbitrum, Layer-2 i eines generals de cartera/codificació, la població amb més probabilitats de tenir exactament els actius que busca la segona etapa. Qualsevol persona que hagi executat un d'aquests mòduls en una màquina que contingui claus de cartera, frases llavor, magatzems de claus, claus SSH o .NS els secrets haurien de tractar aquestes credencials com a compromeses i rotar-les.

Dos patrons que val la pena interioritzar. En primer lloc, càrrega útil com a fixació: enviant la segona etapa com a base64 dins d'un fitxer de dades amb un nom plausible (test/fixtures/keypairs.dat) manté la font visible del paquet amb un aspecte net i envia el contingut maliciós a un fitxer que les eines de revisió i els rastrejadors humans sovint tracten com a dades inertes. En segon lloc, execució retardada en el temps d'importació amb persistència multiplataforma: allunyar el disparador del ganxo d'instal·lació, afegir un temporitzador i després persistir a través de cron, tasques programades i launchd és un pas deliberat allunyant-se de les tècniques més sorolloses d'script d'instal·lació que l'escaneig automatitzat del registre vigila més de prop.

Guia per a defensors i mantenidors:

  • Tracta el codi afegit després de mòdul.exportacions com a prioritat de revisió: la lògica del dropper sovint s'amaga sota la superfície "real" del mòdul.
  • No doneu per fet que els fitxers de dades són inerts. Un blob base64 sota prova/fixació/ que es llegeix en temps d'execució i es descodifica és adjacent a l'executable; marca les lectures en temps d'execució dels fitxers de fixació que alimenten un function/eval/write-then-fresar cadena.
  • Busca el camí de caiguda ~/.cache-db/.node-sync/ i per a les unitats de persistència WinNodeSync (tasca programada) i com.apple.syncd (launchd) en màquines de desenvolupador que han extret aquests noms.
  • Alerta sobre processos de Node que creen entrades cron, tasques programades o treballs launchd: les biblioteques legítimes poques vegades ho fan en la importació.
  • Preferiu els fitxers de bloqueig i les versions fixades, i reviseu les diferències de qualsevol nova petita dependència "d'utilitat", especialment paquets de dependència zero publicats per comptes no verificats sense un repositori vinculat.

Per als defensors del registre, la conclusió és que la supervisió del ganxo d'instal·lació és necessària però no suficient: un dropper en temps d'importació amb retard de temporitzador instal·lat dins d'un fitxer de dades passarà una comprovació només en temps d'instal·lació, i el pas de persistència sovint és el senyal observable més fort que queda per detectar.

sca-tools-software-composition-analyse-tools
Prioritzar, solucionar i protegir els riscos del programari
Obtén el teu compte gratuït.
No es requereix cap targeta de crèdit.

Assegura el desenvolupament i el lliurament del teu programari

amb el paquet de productes Xygeni