PhantomSync: npm kryptopakker skjuler tegnebogstjæler

PhantomSync: otte kryptoudvikler-npm-pakker skjuler en forsinket, selvvedvarende dropper

TL; DR

En enkelt npm-udgiver sendte otte små pakker, hvis navne lyder som hverdagsbyggesten til blockchain- og tegnebogsudvikling. base58-utils, abi-encode, eth-dev, arb-kit, layer2-sdk, solana-key-utils, eth-wallet-helpersog crypto-validate-libHver af dem indeholder et fungerende værktøj. Efter dette værktøj er der dog tilføjet en selvkaldende kodeblok, der kører i det øjeblik, modulet importeres.

Cirka 37 sekunder efter import, den blok afkoder en nyttelast, der sendes inde i pakken forklædt som en testfixtur, skriver den til en skjult fil under brugerens hjemmemappe, registrerer sig selv til at genstarte hver gang login på tværs af Windows, macOS og Linux, og starter det afkodede script som en frakoblet proces. Intet ved dette udløses under npm-installationen; det venter på, at koden importeres og køres, hvilket er et mere stille øjeblik end installationen.

Nyttelasten er ikke uigennemsigtig. Den sendes som almindelig base64, så afkodning af "testfixturen" gendanner det andet trin fuldt ud: a krypto-wallet og hemmelighedsstyver der venter på, at maskinen sidder inaktiv, høster private nøgler og seed-fraser, krypterer hvert fund med en hardcoded RSA-4096-nøgle og eksfiltrerer den ved at fastlåse den til et offentligt IPFS-lager og sender beacon tilbage hver 12. time.

Vi sporer klyngen som PhantomSyncAlle otte pakker var aktive i npm-registret på analysetidspunktet og offentliggjort under en enkelt konto.

EcosystemNPM
Pakkerbase58-utils, abi-encode, eth-dev, arb-kit, layer2-sdk, solana-key-utils, eth-wallet-helpers, crypto-validate-lib
Målrettede platformeWindows, macOS, Linux
KerneadfærdForsinket importtidsdropper skjult som testfixtur; installerer persistens på tværs af platforme
payloadKrypto-wallet og hemmelighedsstyveri, RSA-4096-kryptering, eksfiltrering via offentlig IPFS-pinning

Angrebsanatomi

Hver pakke ser ubemærkelsesværdig ud ved første læsning. base58-utilser for eksempel et par kilobyte Base58 / Bitcoin-WIF-hjælpekode uden afhængighed – præcis hvad navnet lover. Den relevante kode sidder efter modulets modul.eksport, hvor en læser, der skimmer toppen af ​​filen, sandsynligvis ikke vil se: en selvkaldende funktion, der planlægger sig selv med en timer.

Operationskæden, rekonstrueret fra pakkekilden, kører således:

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

To designvalg skiller sig ud.

Nyttelasten bevæger sig som en testanordning. Den anden fase er ikke skrevet som åbenlys kode. Den lever i test/fixtures/keypairs.dat, en base64-fil hvis navn blandes ind i en pakke, der hævder at håndtere nøglepar. For et menneske, der skimmer tar-filen, ligner det eksempeldata; for den tilføjede kode er det et script, der skal afkodes og køres. Selve dropperen indeholder ingen netværksadresse - dem, der findes i andet trin - men der er ingen yderligere forvirring: fixturet er et enkelt lag af base64, så afkodning af det (base64 -d) genvinder det fulde beløb syncd.js og dens netværksadfærd. I næste afsnit gennemgås, hvad den genoprettede fase gør.

Detonation er forsinket og knyttet til import, ikke installation. Fordi udløseren er kræver () plus en timer på ~37 sekunder i stedet for en installationshook, omgår adfærden kontroller, der kun ser npm installere trin, og forsinkelsen varer længere end mange kortvarige sandkasse- og CI-kørsler. Når noget kører, er installationen, der hentede pakken, for længst færdig.

Når det afkodede script er på disken kl. ~/.cache-db/.node-sync/syncd.js — en sti valgt til at læse som en rutinemæssig cache-mappe — dropperen gør det muligt at overleve genstart på alle tre større platforme:

  • Linux: en cron-post, der genstarter scriptet. Posten installeres ved at filtrere den eksisterende crontab igennem grep -v synkroniseret, hvilket har den bivirkning, at den nye post udelades af en almindelig liste, der bruger greps for det samme navn.
  • Windows: en planlagt opgave med navnet WinNodeSync, indstillet til at blive gentaget med 12 minutters interval.
  • MacOS: et launchd-job mærket com.apple.syncd, der findes i flere af pakkerne – en etiket, der efterligner en legitim Apple-systemtjeneste.

Scriptet startes derefter med det samme som en frakoblet proces, så det fortsætter med at køre, efter at importprogrammet afsluttes.

Hvad den anden fase gør

Fordi fixturet er et enkelt base64-lag, afkodes det andet trin rent og kan læses fuldt ud. Alle otte pakker indeholder en af ​​tre varianter af det samme script, som identificerer sig selv i en headerkommentar som phantom syncd v3 — topo durmiente (“sovende muldvarp”). Dens opgave er at stjæle kryptovaluta-wallet-materiale og udviklerhemmeligheder og sende dem fra maskinen. Den kører i disse trin:

  • 1. Vent, indtil maskinen er inaktiv. Før du gør noget, syncd.js kontrollerer, hvor længe brugeren har været inaktiv, og fortsætter kun efter en grænse (ca. 15 minutter) — xprintidle på Linux, ioreg HIDIdleTime på macOS og en PowerShell-forespørgsel om inaktivitetstid på Windows. Høstning har derfor en tendens til at ske, når der ikke er nogen ved tastaturet.
  • 2. Hent en fjernbetjent aktiveringskontaktScriptet henter en lille konfigurationsfil fra en dead-drop, før det reagerer. Den primære kilde er en GitHub gist raw URL (gist.githubusercontent.com/juang55/…/cfg.txt); scriptet aktiveres kun, hvis den konfiguration læser aktiv=1Hvis hovedteksten ikke er tilgængelig, falder den tilbage til tre hardcodede IPFS-indholdsidentifikatorer, der hentes via de offentlige gateways. gateway.pinata.cloud, ipfs.ioog cloudflare-ipfs.comDette giver operatøren en efterfølgende aktiverings-/afvæbningskontrol og et nedtagningssikkert reservesystem. (Den mindste variant, leveret i kryptovalideringsbibliotek, eth-wallet-hjælpereog solana-key-utils, har fjernet gist-laget og er udelukkende afhængig af IPFS-identifikatorerne.)
  • 3. Indsaml tegnebogsmateriale og hemmeligheder. Scriptet går gennem brugerens hjemmemappe — ~/.config/solana, ~/.ethereum/nøglebutik, ~/.støberi, ~/.sikkerhedshjelm, ~ / .sshog desktop/Dokumenter/Downloads (inklusive spansktalende mappenavne) plus ~/.env og shell rc-filer og tilsvarende AppData placeringer på Windows. Den er rettet mod Ethereum private nøgler, Bitcoin WIF-nøgler, BIP-39 seed-fraser, Solana-nøglepar, Ethereum keystore JSON, SSH-nøgler og hemmelighedsbærende miljøvariabler. Den større variant (i abi-encode, base58-utils, eth-udvikling) indeholder den fulde BIP-39-ordbog på 2048 ord og bruger regulære udtryk til at ekstrakt individuelle nøgler og validerede frøfraser fra enhver tekst, den læser; de to mindre varianter matcher i stedet filer efter nøgleord (frø, huskeregel, pung, metamask, fantom, hovedbog, kasse, …) og uploade hele filer.
  • 4. Krypter og eksfiltrer via en offentlig fastlåsningstjeneste. Hvert fund er bundtet med et værtsfingeraftryk (brugernavn@værtsnavn, platform, tidsstempel) og krypteret med en hardcodet RSA-4096 offentlig nøgle, der er indlejret i scriptet. Den krypterede post uploades derefter ved at fastgøre den til IPFS via api.pinata.cloud/pinning/pinJSONToIPFS, autentificeret med hardcodede Pinata API-legitimationsoplysninger. Der er ingen skræddersyet C2-server at beslaglægge: de stjålne data parkeres i offentligt decentraliseret lager, som operatøren kan hente ved hjælp af de resulterende indholdshashes. Uploads er fordelt med et par sekunders tilfældig jitter, og en lokal log over tidsstempel | nøgletype | returneret hash holdes ved ~/.cache-db/.node-sync/.sl.
  • 5. Fortsæt og giv signal i en 12-timers cyklus. Det andet trin genetablerer sin egen persistens — en cron-post på Linux, en com.apple.syncd launchd-job på macOS og en planlagt opgave med navnet WindowsNodeSync på Windows — indstillet til at køre igen hver 12. time. Bemærk, at dette er en forskellige Windows-opgavenavn fra den, som dropperen installerer (WinNodeSync); begge er værd at lede efter.

Tidslinje

De otte pakker blev udgivet i et tæt forløb den 13. juli 2026 og 14. juli 2026. Flere har mere end én version; dropperen er identisk på tværs af versioner, hvor kun linjeforskydninger ændres, når størrelsen på den godartede forsyningskode over den ændres.

DatoBegivenhed
2026-07-13De første pakker i klyngen vises under én udgiver (solana-key-utils, eth-wallet-helpers, crypto-validate-lib og tidlige versioner af resten)
2026-07-13 → 07-14Resterende navne og opfølgende versioner offentliggjort; de samme tilføjede dropper-udgaver sendes i hver
2026-07-14Alle otte markeret og analyseret; alle pakker kan stadig installeres fra registreringsdatabasen

I løbet af udbruddet ændrede udgiverens registeromdømme sig fra nogenlunde neutralt i starten af ​​klyngen til stærkt negativt, efterhånden som antallet af detektioner akkumulerede – en observerbar bivirkning af, at pakkerne blev markeret, ikke en designet funktion.

Indikatorer for kompromis

Alle nedenstående indikatorer blev bekræftet til stede på analysetidspunktet — dem i tabellerne for "Anden fase" ved afkodning test/fixtures/keypairs.dat og læser det gendannede syncd.js.

Filer og stier

Indikatorroller
~/.cache-db/.node-sync/syncd.jsAfkodet andet trin, skrevet med tilstand 0o700
~/.cache-db/.node-sync/.slLokal eksfiltreringslog (tidsstempel | nøgletype | IPFS-hash)
test/fixtures/keypairs.datBase64-kodet nyttelast placeret inde i tarballen som en "testfixtur"

Netværksinfrastruktur i andet trin (gendannet fra syncd.js)

Indikatorroller
gist.githubusercontent.com/juang55/b298754cb72942b1cdcf02ccd45cde2f/raw/cfg.txtAktivering uden varsel; script kører kun, hvis konfigurationen læser active=1
Qmcqz3w8j4qFQXDAXAxnrdc2oSX3nzBT4NqtpTqL8mr1gaIPFS-konfigurationsreserve (CID)
QmdTXoqVmTHY1i4ZWLdLkoQ9YChp5TXPh5cWXwnAYZt5iFIPFS-konfigurationsreserve (CID)
QmfJkLU5gdCpqbbqEjWYC2anXW9FmuEeSLLeLiHVJKYUjpIPFS-konfigurationsreserve (CID)
gateway.pinata.cloud, ipfs.io, cloudflare-ipfs.comIPFS-gateways brugt til at hente konfigurationsfallback'en
api.pinata.cloud/pinning/pinJSONToIPFSEksfiltreringsslutpunkt, stjålne data fastgjort til offentlig IPFS
Pinata API-nøgle13c766575b9270a9825d, hardcoded exfiltration legitimationsoplysninger

Anden fase af samlingsmål (hentet fra syncd.js)

Indikatorroller
~/.config/solana, ~/.ethereum/keystore, ~/.foundry, ~/.hardhat, ~/.ssh, ~/.env, shell rc-filerMapper/filer søgt efter nøgler og hemmeligheder
AppData\Roaming\Solana, AppData\Local\ethereum\keystoreSøgte Windows-ækvivalenter
Høstede artefakttyperETH private nøgler, Bitcoin WIF, BIP-39 seed phrases, Solana-nøglepar, Ethereum-nøglelager JSON, SSH-nøgler, hemmelige miljøvariabler

Persistensartefakter

perronIndikator
Linuxlancering af cron-post syncd.js; installeret via crontab filtreret igennem grep -v syncd
Windowsplanlagt opgave WinNodeSync (dråbetæller) og WindowsNodeSync (anden fase)
MacOSlaunchd-etiket com.apple.syncd
AlleAnden fase fortsætter igen i en 12-timers cyklus

Behavioral

  • Selvkaldende funktion tilføjet efter modul.eksport, planlægning a sætTimeout på ~37,000 ms ved import.
  • barn_proces spawn("node", ) med den frakoblede indstillingsgruppe.
  • Tomgangsstyret aktivering i andet trin (xprintidle / ioreg HIDIdleTime / PowerShell inaktiv tid; ~15 minutters tærskel).
  • RSA-4096-kryptering efter søgning, derefter HTTPS POST til en offentlig IPFS-pinning-API.

Pakker og versioner (npm, udgiver solbuilder_io)

Pakkeversioner
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

Forlægger

  • solbuilder_ioangel_lopez89[@]proton[.]mig, e-mail ubekræftet, ingen verificeret kildekontrolkonto, intet tilknyttet arkiv.

Attribuering og observeret adfærd

De otte pakker deler én udgiverkonto og én nyttelast. Hver pakke er en triviel pakke – et par kilobyte rigtig værktøjskode med den samme dropper tilføjet – udgivet uden et linket arkiv og under en ubekræftet engangs-e-mail. Den ensartethed, den delte drop-sti, de delte persistensetiketter og den delte nøglepar.dat Staging-filer er det, der binder klyngen sammen.

Pakkerne er usædvanligt ærlige om deres egen opførsel. solana-key-utils indeholder indlejrede kommentarer, der beskriver den tilføjede blok i klare vendinger - man mærker den PHANTOM: usynlig vedholdenhed, en anden (på spansk) læser Udkast topografisk billede i baggrunden, “kør muldvarpen i baggrunden.” Dette er kodens egne annotationer af, hvad den gør; kampagnenavnet i dette indlæg er hentet fra den første etiket sammen med den falske node “sync daemon” (synkroniseret) som persistensmaskineriet imiterer.

Vi beskriver kun, hvad koden observerbart gør. Navngivningen af ​​pakkerne – alle krypto-, wallet- og blockchain-værktøjstermer – indikerer den udviklergruppe, der mest tilbøjelig er til at hente dem ved navn; det fastslår ikke i sig selv, hvem udgiveren er. Det andet trin kunne fuldt ud gendannes ved at afkode base64-fixturen, og dens adfærd er beskrevet ovenfor: den indsamler wallet-nøgler, seed-fraser og hemmeligheder og eksfiltrerer dem, RSA-krypteret, til offentlig IPFS-lagring via Pinata. Et bemærkelsesværdigt designvalg er fraværet af en privat C2-server – konfigurationen kommer fra en GitHub-gist og IPFS, og stjålne data parkeres i offentlig decentraliseret lagring, der er kodet af indholdshash, som begge er sværere at få fat i end en enkelt angriberkontrolleret vært. Der blev ikke fundet nogen tidligere offentlig rapportering, der matchede denne klynge, på tidspunktet for skrivningen.

Indvirkning, tendenser og vejledning for forsvarere

Hvem er udsat. Enhver, der har tilføjet en af ​​disse pakker til et Node-projekt og derefter kørt kode, der importerer den. Da detonation sker under importtid snarere end installationstid, er det ikke nok blot at have pakken til stede – men enhver normal brug, der indlæser modulet, når nyttelasten. Lokkenavnene er rettet mod udviklere, der bygger på Ethereum, Solana, Arbitrum, Layer-2 og generelle wallet-/kodningsværktøjer – den population, der mest sandsynligt har præcis de aktiver, som det andet trin jagter efter. Enhver, der har kørt et af disse moduler på en maskine, der indeholder wallet-nøgler, seed-fraser, keystores, SSH-nøgler eller ... .env Hemmeligheder bør behandle disse legitimationsoplysninger som kompromitterede og rotere dem.

To mønstre, der er værd at internalisere. First, nyttelast som inventar: forsendelse af det andet trin som base64 inde i en plausibelt navngivet datafil (test/fixtures/keypairs.dat) holder den synlige kilde til pakken ren og skubber det skadelige indhold ind i en fil, som gennemgangsværktøjer og menneskelige skims ofte behandler som inaktive data. For det andet, importtidsforsinket udførelse med tværplatformspersistensAt flytte udløseren væk fra installationskrogen, tilføje en timer og derefter bevare den på tværs af cron, planlagte opgaver og launchd er et bevidst skridt væk fra de mere støjende installationsscriptteknikker, som automatiseret registreringsdatabasescanning overvåger nøje.

Vejledning til forsvarere og vedligeholdere:

  • Behandl tilføjet kode efter modul.eksport som en gennemgangsprioritet — dropper-logik gemmer sig ofte under den "rigtige" moduloverflade.
  • Antag ikke, at datafiler er inaktive. En base64-blob under test/kampprogram/ der læses ved kørsel og afkodes, er eksekverbar-tilstødende; markerer kørselslæsninger af fixture-filer, der føder en Funktion/eval/skriv-så-spawn kæde.
  • Jagt efter faldstien ~/.cache-db/.node-sync/ og for persistensenhederne WinNodeSync (planlagt opgave) og com.apple.syncd (launchd) på udviklermaskiner, der hentede disse navne.
  • Advarsel om nodeprocesser, der opretter cron-poster, planlagte opgaver eller launchd-job — legitime biblioteker gør sjældent dette ved import.
  • Foretræk låsefiler og fastlåste versioner, og gennemgå diff'en for enhver ny lille "hjælpe"-afhængighed, især nul-afhængighedspakker udgivet af ubekræftede konti uden tilknyttet arkiv.

For registerforsvarere er konklusionen, at overvågning af installations-hook er nødvendig, men ikke tilstrækkelig: en importtids-, timerforsinket dropper, der er placeret i en datafil, vil bestå en kontrol kun på installationstidspunktet, og persistens-trinnet er ofte det højeste observerbare signal, der er tilbage at opfange.

sca-tools-software-kompositionsanalyseværktøjer
Prioriter, afhjælp og sørg for dine softwarerisici
Få din gratis konto.
Der kræves ikke noget kreditkort.

Sikr din softwareudvikling og -levering

med Xygeni-produktsuite