„PhantomSync“: npm kriptovaliutų paketai, slėpiantys piniginės vagystę

„PhantomSync“: aštuoni kriptovaliutų kūrėjų npm paketai slepia uždelstą, savaime išliekantį lašintuvą

Lt; DR

Vienas npm leidėjas išsiuntė aštuonis mažus paketus, kurių pavadinimai skambėjo kaip kasdieniai blokų grandinės ir piniginės kūrimo elementai. base58-utils, abi-encode, eth-dev, arb-kit, layer2-sdk, solana-key-utils, eth-wallet-helpersir crypto-validate-libKiekviename iš jų yra veikianti programa. Tačiau po tos programos pridedamas savaime iškviečiantis kodo blokas, kuris paleidžiamas importavus modulį.

Maždaug 37 sekundės po importavimo, tas blokas dekoduoja pakete esantį naudingąjį apkrovą, užmaskuotą kaip bandymo įrenginys, įrašo jį į paslėptą failą vartotojo namų kataloge, užregistruoja save paleisti iš naujo kiekvieną kartą login „Windows“, „macOS“ ir „Linux“ sistemose ir paleidžia dekoduotą scenarijų kaip atskirą procesą. „npm“ diegimo metu niekas iš to nesuveikia; laukiama, kol kodas bus importuotas ir paleistas, o tai yra ramesnis momentas nei diegimas.

Naudingoji apkrova nėra neskaidri. Ji pateikiama kaip paprasta „base64“, todėl dekoduojant „bandomąjį įrenginį“ atkuriamas visas antrasis etapas: a kriptovaliutų piniginė ir paslapčių vagis kuris laukia, kol mašina veiks nenaudojama, surenka privačius raktus ir pirmines frazes, kiekvieną radinį užšifruoja užprogramuotu RSA-4096 raktu ir išfiltruoja jį prisegdamas prie viešosios IPFS saugyklos, kas 12 valandų siųsdamas signalą.

Mes sekame klasterį kaip Fantominis sinchronizavimasAnalizės metu visi aštuoni paketai buvo paskelbti npm registre vienoje paskyroje.

ekosistemanpm
Paketaibase58-utils, abi-encode, eth-dev, arb-kit, layer2-sdk, solana-key-utils, eth-wallet-helpers, crypto-validate-lib
Tikslinės platformos„Windows“, „MacOS“, „Linux“
Pagrindinis elgesysUždelstas, importavimo laiko lašintuvas, paslėptas kaip bandymo įrenginys; įdiegia kelių platformų pastovumą
Naudingoji apkrovaKriptovaliutų piniginė ir paslapčių vagis, RSA-4096 šifravimas, išgavimas per viešą IPFS prisegimą

Atakos anatomija

Kiekviena pakuotė iš pirmo žvilgsnio atrodo niekuo neišsiskirianti. base58-utils, pavyzdžiui, yra keli kilobaitai nulinės priklausomybės „Base58“ / „Bitcoin-WIF“ pagalbinio kodo – būtent tai, ką žada pavadinimas. Atitinkamas kodas yra po modulio modulis.exports, kur skaitytojas, peržvelgęs failo viršų, vargu ar žiūrės: savarankiškai iškviečiama funkcija, kuri pati save suplanuoja su laikmačiu.

Operacijų grandinė, rekonstruota iš paketo šaltinio, atrodo taip:

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

Išsiskiria du dizaino variantai.

Naudingoji apkrova keliauja kaip bandymo įranga. Antrasis etapas nėra parašytas kaip akivaizdus kodas. Jis egzistuoja test/fixtures/keypairs.dat, „base64“ failas, kurio pavadinimas susilieja su paketu, kuris teigia, kad tvarko raktų poras. Žmogui, peržiūrinčiam tarball, tai atrodo kaip pavyzdiniai duomenys; pridėtam kodui tai yra scenarijus, kurį reikia dekoduoti ir paleisti. Pats lašintuvas neturi tinklo adreso – tie, kurie yra antrame etape – bet nėra jokio papildomo užmaskavimo: įrenginys yra vienas „base64“ sluoksnis, todėl jį dekoduojant (base64 -d) atgauna visą syncd.js ir jo tinklo elgseną. Kitame skyriuje apžvelgiama, ką veikia tas atkūrimo etapas.

Detonacija yra atidėta ir susieta su importu, o ne su diegimu. Nes gaidukas yra reikalauti () plius ~37 sekundžių laikmatis, o ne diegimo kabliukas, elgesio patikrinimai apeina tik tuos, kurie stebi npm įdiegti žingsnis, ir šis vėlavimas trunka ilgiau nei daugelis trumpalaikių „smėlio dėžės“ ir „integruotojo integravimo“ paleidimų. Kol kas nors vykdomas, diegimas, kuris įtraukė paketą, jau seniai būna baigtas.

Kai dekoduotas scenarijus yra diske adresu ~/.cache-db/.node-sync/syncd.js — kelias, pasirinktas skaityti kaip įprastą talpyklos katalogą — lašintuvas leidžia jam išlikti po perkrovimo visose trijose pagrindinėse platformose:

  • Linux: „cron“ įrašas, kuris iš naujo paleidžia scenarijų. Įrašas įdiegiamas filtruojant esamą „crontab“ per grep -v syncd, o tai turi šalutinį poveikį – naujas įrašas nepatenka į paprastą sąrašą, kuris ieško to paties pavadinimo naudodamas „grep“.
  • Langai: suplanuota užduotis, pavadinta WinNodeSync, nustatytas kartoti kas 12 minučių.
  • macOS: paleista užduotis, pažymėta com.apple.syncd, esantis keliose pakuotėse – etiketė, imituojanti teisėtą „Apple“ sistemos paslaugą.

Tada scenarijus iš karto paleidžiamas kaip atskiras procesas, todėl jis tęsia vykdymą ir uždarius importavimo programą.

Ką daro antrasis etapas

Kadangi įrenginys yra vienas „base64“ sluoksnis, antrasis etapas dekoduoja švariai ir gali būti nuskaitomas pilnai. Visuose aštuoniuose paketuose yra vienas iš trijų to paties scenarijaus variantų, kuris antraštės komentare identifikuojamas kaip phantom syncd v3 — topo durmiente („miegantis kurmis“). Jo užduotis – pavogti kriptovaliutų piniginės medžiagą ir kūrėjų paslaptis bei išsiųsti jas iš mašinos. Jis veikia šiais žingsniais:

  • 1. Palaukite, kol įrenginys veiks nenaudojamas. Prieš ką nors darydamas, syncd.js tikrina, kiek laiko vartotojas buvo neaktyvus, ir tęsia tik peržengęs nustatytą ribą (apie 15 minučių) — xprintidle Linux sistemoje ioreg „HIDIdleTime“ sistemoje „macOS“ ir „PowerShell“ užklausa dėl neaktyvaus laiko sistemoje „Windows“. Todėl duomenų rinkimas dažniausiai vyksta, kai niekas nėra prie klaviatūros.
  • 2. Gaukite nuotoliniu būdu valdomą įjungimo jungiklįPrieš pradėdamas veikti, scenarijus iš neveikiančios sistemos ištraukia nedidelę konfigūraciją. Pagrindinis šaltinis yra „GitHub“ gist neapdorotas URL (gist.githubusercontent.com/juang55/…/cfg.txt); scenarijus aktyvuojamas tik tuo atveju, jei ta konfigūracija nuskaito aktyvus=1Jei esmė nepasiekiama, naudojami trys užkoduoti IPFS turinio identifikatoriai, gauti per viešuosius šliuzus. gateway.pinata.cloud, ipfs.ioir cloudflare-ipfs.comTai suteikia operatoriui galimybę valdyti įjungimą / išjungimą po įjungimo ir atsarginį sprendimą, apsaugantį nuo numetimo. (Mažiausias variantas, pristatomas crypto-validate-lib, eth-piniginės-pagalbininkaiir solana-key-utils, pašalintas esminis sluoksnis ir jis remiasi tik IPFS identifikatoriais.)
  • 3. Surinkite piniginės medžiagą ir paslaptis. Skriptas pereina vartotojo namų katalogą — ~/.config/solana, ~/.ethereum/keystore, ~/.foundry, ~/.hardhat, ~ / .sshir Stalinis/Dokumentai/Atsisiuntimai (įskaitant ispanų kalbos aplankų pavadinimus) ir ~/.env ir „shell rc“ failus bei jų atitikmenis AppData vietos sistemoje „Windows“. Jis skirtas „Ethereum“ privatiems raktams, „Bitcoin WIF“ raktams, BIP-39 sėklos frazėms, „Solana“ raktų poroms, „Ethereum“ raktų saugyklos JSON, SSH raktams ir slaptus duomenis turintiems aplinkos kintamiesiems. Didesnis variantas ( abi-koduoti, base58-utils, eth-dev) turi visą 2048 žodžių BIP-39 žodyną ir naudoja reguliarias išraiškas išgauti atskirus raktus ir patvirtintas pirmines frazes iš bet kokio nuskaitomo teksto; du mažesni variantai vietoj to susieja failus pagal raktinį žodį (sėklos, mnemoninis, piniginė, metamask, fantomas, vadovėlis, dėžutė, ...) ir įkelkite ištisus failus.
  • 4. Užšifruokite ir išfiltruokite per viešąją prisegimo paslaugą. Kiekvienas radinys yra susietas su šeimininko piršto atspaudu (naudotojo vardas@hostname, platforma, laiko žyma) ir užšifruotas naudojant scenarijuje įterptą užkoduotą RSA-4096 viešąjį raktą. Užšifruotas įrašas tada įkeliamas prisegant jį prie IPFS per api.pinata.cloud/pinning/pinJSONToIPFS, autentifikuota naudojant užkoduotus „Pinata“ API prisijungimo duomenis. Nėra specialaus C2 serverio, kurį būtų galima konfiskuoti: pavogti duomenys saugomi viešojoje decentralizuotoje saugykloje, kurią operatorius gali atkurti naudodamas gautas turinio maišos funkcijas. Įkėlimai atliekami su kelių sekundžių atsitiktiniu virpesiu ir vietiniu žurnalu. laiko žyma | rakto-tipas | grąžinta-maiša yra laikomas ~/.cache-db/.node-sync/.sl.
  • 5. Atkakliai laikykite signalą ir siųskite signalą 12 valandų ciklu. Antrasis etapas atkuria savo paties išlikimą – „cron“ įrašą „Linux“ sistemoje, com.apple.syncd „launchd“ užduotis „macOS“ sistemoje ir suplanuota užduotis pavadinimu „Windows NodeSync“ „Windows“ sistemoje – nustatykite, kad programa būtų paleidžiama iš naujo kas 12 valandų. Atminkite, kad tai yra kitoks „Windows“ užduoties pavadinimas iš tos, kurią įdiegia lašintuvas (WinNodeSync); abu verta paieškoti.

Chronologija

Aštuoni paketai buvo paskelbti vienu metu 2026-07-13 ir 2026-07-14. Keletas jų turi daugiau nei vieną versiją; lašintuvas yra identiškas visose versijose, tik linijų poslinkiai keičiasi priklausomai nuo virš jo esančio gerybinio kodo dydžio.

Datarenginys
2026-07-13Pirmieji klasterio paketai rodomi pas vieną leidėją (solana-key-utils, eth-wallet-helpers, crypto-validate-lib ir ankstesnės kitų versijos)
2026-07-13 → 07-14Likę pavadinimai ir vėlesnės versijos publikuotos; tie patys pridėti lašintuvų laivai kiekviename
2026-07-14Visi aštuoni pažymėti ir išanalizuoti; kiekvieną paketą vis dar galima įdiegti iš registro

Viso pliūpsnio metu leidėjo registro reputacija pasikeitė iš maždaug neutralios klasterio pradžioje iki stipriai neigiamos, kaupiantis aptikimams – tai pastebimas paketų žymėjimo šalutinis poveikis, o ne suplanuota funkcija.

Kompromiso rodikliai

Visų toliau nurodytų rodiklių buvimas analizės metu buvo patvirtintas – „Antrojo etapo“ lentelėse esančių rodiklių dekodavimas. test/fixtures/keypairs.dat ir skaitant atsigavusį syncd.js.

Failai ir keliai

rodiklisVaidmuo
~/.cache-db/.node-sync/syncd.jsDekoduotas antrasis etapas, parašytas 0o700 režimu
~/.cache-db/.node-sync/.slVietinis išfiltracijos žurnalas (laiko žyma | rakto tipas | IPFS maiša)
test/fixtures/keypairs.dat„Base64“ koduotės pagrindu sukurta naudingoji apkrova, patalpinta tarball faile kaip „bandymo įrenginys“

Antrojo etapo tinklo infrastruktūra (atkurta iš syncd.js)

rodiklisVaidmuo
gist.githubusercontent.com/juang55/b298754cb72942b1cdcf02ccd45cde2f/raw/cfg.txtAktyvinimo nebaigtas procesas; scenarijus paleidžiamas tik tada, jei konfigūracija nuskaito active=1
Qmcqz3w8j4qFQXDAXAxnrdc2oSX3nzBT4NqtpTqL8mr1gaIPFS konfigūracijos atsarginis variantas (CID)
QmdTXoqVmTHY1i4ZWLdLkoQ9YChp5TXPh5cWXwnAYZt5iFIPFS konfigūracijos atsarginis variantas (CID)
QmfJkLU5gdCpqbbqEjWYC2anXW9FmuEeSLLeLiHVJKYUjpIPFS konfigūracijos atsarginis variantas (CID)
gateway.pinata.cloud, ipfs.io, cloudflare-ipfs.comIPFS šliuzai, naudojami konfigūracijos atsarginei versijai gauti
api.pinata.cloud/pinning/pinJSONToIPFSEksfiltracijos galinis taškas, pavogti duomenys prisegti prie viešo IPFS
Pinata API raktas13c766575b9270a9825d, kietuoju kodu užkoduoti eksfiltracijos prisijungimo duomenys

Antrojo etapo rinkimo tikslai (atkurti iš syncd.js)

rodiklisVaidmuo
~/.config/solana, ~/.ethereum/keystore, ~/.foundry, ~/.hardhat, ~/.ssh, ~/.env, apvalkalo rc failaiKatalogai / failai, kuriuose ieškoma raktų ir paslapčių
AppData\Roaming\Solana, AppData\Local\ethereum\keystoreIeškomi atitikmenys „Windows“ sistemoje
Surinktų artefaktų tipaiETH privatūs raktai, Bitcoin WIF, BIP-39 pradinės frazės, „Solana“ raktų poros, Ethereum raktų saugykla JSON formatu, SSH raktai, slapti aplinkos kintamieji

Išlikimo artefaktai

Platformarodiklis
Linux„cron“ įėjimo paleidimas syncd.js; įdiegta per „crontab“, filtruojama per grep -v syncd
Windowssuplanuota užduotis WinNodeSync (lašintuvas) ir WindowsNodeSync (antrasis etapas)
macOSlaunched etiketė com.apple.syncd
VisiAntrasis etapas kartojasi 12 valandų ciklu.

Elgsenos

  • Savaime iškviečiama funkcija pridedama po modulis.exports, planavimas a setTimeout ~37 000 ms importuojant.
  • vaikas_procesas spawn(„mazgas“, ) su atskiru parinkčių rinkiniu.
  • Antrojo etapo aktyvavimas tuščiosios eigos režimu (xprintidle / ioreg HIDIdleTime / PowerShell neveikos laikas; ~15 minučių riba).
  • RSA-4096 šifravimo paieška, tada HTTPS POST prie viešos IPFS prisegimo API.

Paketai ir versijos (npm, leidėjas solbuilder_io)

Paketasversijos
base58-utils1.0.0, 1.0.1, 1.0.3
abi-encode1.0.0, 1.0.1, 1.0.2
eth-dev1.0.0, 1.0.1, 1.0.2
arb-kit1.0.0, 1.0.1
layer2-sdk1.0.0, 1.0.1
solana-key-utils1.0.0
eth-wallet-helpers1.0.0
crypto-validate-lib1.0.0

Leidėjas

  • solbuilder_io - angel_lopez89[@]proton[.]me, el. pašto adresas nepatvirtintas, nėra patvirtintos šaltinio kontrolės paskyros, nėra susietos saugyklos.

Priskyrimas ir stebimas elgesys

Aštuoni paketai dalijasi viena leidėjo paskyra ir viena apkrova. Kiekvienas iš jų yra trivialus paketas – keli kilobaitai tikro kodo su tuo pačiu pridėtu „dropper“ elementu, publikuojamas be susietos saugyklos ir naudojant nepatvirtintą vienkartinio naudojimo el. pašto adresą. Tas vienodumas, bendras „drop“ kelias, bendros patvarumo etiketės ir bendras... raktų poros.dat Parengiamieji failai sujungia klasterį.

Paketai neįprastai atvirai kalba apie savo elgesį. solana-key-utils pateikia vidinius komentarus, kurie paprastai apibūdina pridėtą bloką – vienas jį pažymi FANTOMAS: nematomas atkaklumas, kitas (ispanų kalba) skelbia Išstumkite viršų į foną, „paleisti kurmį fone“. Tai yra paties kodo anotacijos apie tai, ką jis daro; kampanijos pavadinimas šiame įraše paimtas iš tos pirmosios etiketės kartu su netikru mazgu „sinchronizavimo demonas“ (sinchronizuoti), kurį imituoja atkaklumo mechanizmas.

Aprašome tik tai, ką kodas daro pastebimai. Paketų pavadinimai – visi kriptovaliutų, piniginės ir blokų grandinės įrankių terminai – nurodo kūrėjų auditoriją, kuri greičiausiai juos ištrauks pagal pavadinimą; tai savaime nenustato, kas yra leidėjas. Antrasis etapas buvo visiškai atkuriamas dekoduojant „base64“ įrenginį, o jo elgesys aprašytas aukščiau: jis renka piniginės raktus, sėklų frazes ir paslaptis ir jas, RSA užšifruotas, išfiltruoja į viešąją IPFS saugyklą per „Pinata“. Svarbus dizaino pasirinkimas yra privataus C2 serverio nebuvimas – konfigūracija gaunama iš „GitHub“ gist ir IPFS, o pavogti duomenys saugomi viešojoje decentralizuotoje saugykloje, užšifruotoje turinio maišos kodu, kurį abu sunkiau perimti nei vieną užpuoliko kontroliuojamą pagrindinį kompiuterį. Rašymo metu nebuvo rasta jokių ankstesnių viešų ataskaitų, atitinkančių šį klasterį.

Poveikis, tendencijos ir gairės gynėjams

Kas yra demaskuotas. Kiekvienas, kuris pridėjo vieną iš šių paketų prie „Node“ projekto ir tada paleido kodą, kuris jį importuoja. Kadangi detonacija vyksta importavimo, o ne diegimo metu, vien paketo buvimo nepakanka – bet koks įprastas naudojimas, kuris įkelia modulį, pasiekia naudingąją apkrovą. Masalų pavadinimai skirti kūrėjams, kuria „Ethereum“, „Solana“, „Arbitrum“, „Layer-2“ ir bendrus piniginės / kodavimo įrankius – populiacijai, kuri greičiausiai turi būtent tuos išteklius, kurių ieško antrasis etapas. Kiekvienas, kuris paleido vieną iš šių modulių mašinoje, kurioje saugomi piniginės raktai, sėklos frazės, raktų saugyklos, SSH raktai arba... .NS slapti asmenys turėtų laikyti šiuos prisijungimo duomenis pažeistais ir juos keisti.

Du modeliai, kuriuos verta įsisavinti. Pirma, naudingoji apkrova kaip įranga: antrojo etapo siuntimas kaip base64 patikimai pavadintame duomenų faile (test/fixtures/keypairs.dat) išlaiko matomą paketo šaltinį švarų ir kenkėjišką turinį įkelia į failą, kurį peržiūros įrankiai ir žmonių atliekamos peržiūros dažnai traktuoja kaip inertiškus duomenis. Antra, importavimo laiko atidėtas vykdymas su kelių platformų išlikimuPerkėlus trigerį iš diegimo kabliuko, pridėjus laikmatį ir išlaikant jį „cron“, suplanuotose užduotyse ir „launchd“ yra sąmoningas žingsnis tolstant nuo triukšmingesnių diegimo scenarijų metodų, kuriuos automatinis registro nuskaitymas stebi atidžiausiai.

Gairės gynėjams ir palaikytojams:

  • Apdoroti pridėtą kodą po modulis.exports kaip peržiūros prioritetas – lašintuvo logika dažnai slepiasi po „tikruoju“ modulio paviršiumi.
  • Nemanykite, kad duomenų failai yra inertiški. „base64“ blobas, esantis bandymas/rungtynės/ kuris nuskaitomas vykdymo metu ir dekoduojamas, yra gretimas vykdomajam failui; žymi vykdymo metu nuskaitytus tvirtinimo failus, kurie maitina Funkcija/eval/rašyti-tada-neršti grandinė.
  • Medžiokite kritimo kelią ~/.cache-db/.node-sync/ ir atkaklumo vienetams WinNodeSync (suplanuota užduotis) ir com.apple.syncd (launchd) kūrėjų kompiuteriuose, kurie ištraukė šiuos pavadinimus.
  • Įspėjimas apie mazgų procesus, kurie sukuria „cron“ įrašus, suplanuotas užduotis arba paleistas užduotis – teisėtos bibliotekos retai tai daro importavimo metu.
  • Pirmenybę teikite užrakinimo failams ir prisegtoms versijoms ir peržiūrėkite bet kokių naujų mažų „įrankių“ priklausomybių skirtumus, ypač išleisti nulinės priklausomybės paketai nepatvirtintų paskyrų be susietos saugyklos.

Registro gynėjams svarbiausia yra tai, kad diegimo stebėjimas yra būtinas, bet nepakankamas: importavimo metu veikiantis, laikmačiu uždelstas lašintuvas, esantis duomenų faile, praeis tik diegimo metu atliekamą patikrinimą, o išlikimo etapas dažnai yra garsiausias stebimas signalas, kurį dar reikia pagauti.

sca-tools-software-composition-analyses-tools
Prioritetizuoti, pašalinti ir apsaugoti savo programinės įrangos rizikas
Gaukite nemokamą paskyrą.
Nebūtina kreditinės kortelės.

Apsaugokite savo programinės įrangos kūrimą ir tiekimą

su „Xygeni“ produktų rinkiniu