PhantomSync: npm Crypto Packages Hide Wallet Stealer

PhantomSync: acht crypto-ontwikkelaars npm-pakketten verbergen een vertraagde, zelfpersistente dropper.

TL; DR

Eén enkele npm-uitgever bracht acht kleine pakketten uit waarvan de namen klonken als alledaagse bouwstenen voor blockchain- en wallet-ontwikkeling. base58-utils, abi-encode, eth-dev, arb-kit, layer2-sdk, solana-key-utils, eth-wallet-helpersen crypto-validate-libElk van deze modules bevat een werkend hulpprogramma. Daarachter bevindt zich echter een zelfuitvoerend codeblok dat wordt uitgevoerd zodra de module wordt geïmporteerd.

Ongeveer 37 seconden na het importerenDat blok decodeert een payload die vermomd als testfixture in het pakket zit, schrijft deze naar een verborgen bestand in de thuismap van de gebruiker en registreert zichzelf om bij elke volgende poging opnieuw op te starten. login Het werkt op Windows, macOS en Linux en start het gedecodeerde script als een losstaand proces. Er gebeurt niets tijdens `npm install`; het wacht tot de code is geïmporteerd en uitgevoerd, wat een rustiger moment is dan de installatie zelf.

De payload is niet ondoorzichtig. Deze wordt verzonden als gewone base64, dus het decoderen van de "testfixture" herstelt de tweede fase volledig: een crypto-wallet en geheimendief Het systeem wacht tot de machine inactief is, verzamelt privésleutels en seed phrases, versleutelt elke vondst met een hardgecodeerde RSA-4096-sleutel en exfiltreert deze door ze vast te zetten op openbare IPFS-opslag, waarna het elke 12 uur een signaal terugstuurt.

We volgen het cluster als PhantomSyncAlle acht pakketten waren op het moment van analyse beschikbaar in het npm-register en gepubliceerd onder één account.

EcosysteemNPM
Arrangementenbase58-utils, abi-encode, eth-dev, arb-kit, layer2-sdk, solana-key-utils, eth-wallet-helpers, crypto-validate-lib
Doelgerichte platformsWindows, MacOS, Linux
KerngedragVertraagde, tijdens het importeren te gebruiken dropper, verborgen als testfixture; installeert platformonafhankelijke persistentie.
payloadCryptowallet- en geheimendief, RSA-4096-encryptie, data-exfiltratie via openbare IPFS-pinning

Aanvalsanatomie

Elke verpakking lijkt op het eerste gezicht onopvallend. base58-utils, bijvoorbeeld, is een paar kilobytes aan Base58/Bitcoin-WIF-hulpcode zonder afhankelijkheden — precies wat de naam belooft. De relevante code bevindt zich na van de module module.exports, op een plek waar een lezer die alleen de bovenkant van het bestand doorleest waarschijnlijk niet zal kijken: een zelfuitvoerende functie die zichzelf met een timer plant.

De reeks bewerkingen, gereconstrueerd vanuit de broncode van het pakket, verloopt als volgt:

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

Twee ontwerpkeuzes springen eruit.

De lading wordt vervoerd als testopstelling. De tweede fase is niet geschreven als voor de hand liggende code. Het bevindt zich in test/fixtures/keypairs.dat, een base64-bestand waarvan de naam opgaat in een pakket dat beweert sleutelparen te verwerken. Voor iemand die de tarball vluchtig bekijkt, lijkt het op voorbeeldgegevens; voor de toegevoegde code is het een script om te decoderen en uit te voeren. De dropper zelf bevat geen netwerkadres — die bevinden zich in de tweede fase — maar er is geen verdere versleuteling: de fixture is een enkele laag base64, dus het decoderen ervan (base64 -d) herstelt het volledige syncd.js en het netwerkgedrag ervan. In het volgende gedeelte wordt uitgelegd wat die herstelde fase doet.

De ontploffing is vertraagd en gekoppeld aan de import, niet aan de installatie. Omdat de trigger vereisen () plus een timer van ongeveer 37 seconden in plaats van een installatiehook, omzeilt het gedrag controles die alleen de npm installeren stap, en de vertraging duurt langer dan veel kortstondige sandbox- en CI-runs. Tegen de tijd dat er iets wordt uitgevoerd, is de installatie die het pakket heeft binnengehaald allang voltooid.

Zodra het gedecodeerde script op de schijf staat, ~/.cache-db/.node-sync/syncd.js — een pad dat is gekozen om te lezen als een standaard cachemap — zorgt de dropper ervoor dat het bestand blijft bestaan ​​na herstarts op alle drie de belangrijkste platforms:

  • Linux: Een cron-item dat het script opnieuw start. Het item wordt geïnstalleerd door de bestaande crontab te filteren. grep -v syncd, wat als neveneffect heeft dat de nieuwe vermelding niet wordt weergegeven in een eenvoudige lijst die zoekt naar dezelfde naam.
  • Windows: een geplande taak genaamd WinNodeSync, ingesteld om elke 12 minuten opnieuw te worden afgespeeld.
  • MacOS: een launchd-taak gelabeld com.apple.syncd, aanwezig in diverse verpakkingen — een label dat een legitieme Apple-systeemservice nabootst.

Het script wordt vervolgens direct als een losstaand proces gestart, zodat het blijft draaien nadat het importprogramma is afgesloten.

Wat de tweede fase inhoudt

Omdat de fixture uit één enkele base64-laag bestaat, wordt de tweede fase probleemloos gedecodeerd en volledig uitgelezen. Alle acht pakketten bevatten een van de drie varianten van hetzelfde script, dat zichzelf in een headercommentaar identificeert als phantom syncd v3 — topo dumiente (“slapende mol”). Zijn taak is het stelen van gegevens van cryptovaluta-wallets en geheimen van ontwikkelaars, en deze van de machine te verwijderen. Hij werkt in deze stappen:

  • 1. Wacht tot de machine inactief is. Voordat je iets doet, syncd.js Het systeem controleert hoe lang de gebruiker inactief is geweest en gaat pas verder als een bepaalde drempelwaarde (ongeveer 15 minuten) is bereikt. xprintinactief op Linux, ioreg HIDIdleTime op macOS en een PowerShell-query naar inactiviteitstijd op Windows. Het verzamelen van gegevens vindt dus meestal plaats wanneer niemand achter het toetsenbord zit.
  • 2. Haal een op afstand bedienbare activeringsschakelaar.Het script haalt een kleine configuratie uit een deaddrop voordat het verdergaat. De primaire bron is een onbewerkte GitHub gist-URL (gist.githubusercontent.com/juang55/…/cfg.txt); het script wordt alleen geactiveerd als die configuratie het volgende leest actief=1Als de essentie niet beschikbaar is, wordt teruggevallen op drie vastgelegde IPFS-inhoudsidentificaties, die via de openbare gateways worden opgehaald. gateway.pinata.cloud, ipfs.ioen cloudflare-ipfs.comDit geeft de gebruiker achteraf controle over het in- en uitschakelen van het wapen en een terugvalbeveiliging die bestand is tegen uitschakeling. (De kleinste variant, geleverd in crypto-validate-lib, eth-wallet-helpersen solana-key-utils(De gistlaag is verwijderd en het systeem vertrouwt uitsluitend op de IPFS-identificaties.)
  • 3. Verzamel materiaal en geheimen uit je portemonnee. Het script doorloopt de thuismap van de gebruiker. ~/.config/solana, ~/.ethereum/keystore, ~/.foundry, ~/.hardhat, ~ / .sshen Desktop/Documentatie/Downloads (inclusief Spaanstalige mapnamen), plus ~/.env en shell rc-bestanden, en het equivalent daarvan App data locaties op Windows. Het richt zich op Ethereum-privésleutels, Bitcoin WIF-sleutels, BIP-39-seedphrases, Solana-sleutelparen, Ethereum-keystore-JSON, SSH-sleutels en omgevingsvariabelen die geheimen bevatten. De grotere variant (in abi-encode, base58-utils, eth-dev) bevat het volledige BIP-39-woordenboek van 2048 woorden en gebruikt reguliere expressies om extract individuele sleutels en gevalideerde zaadzinnen uit elke tekst die het leest; de twee kleinere varianten matchen bestanden daarentegen op basis van trefwoord (zaad, ezelsbruggetje, portemonnee, metamask, spook, kasboek, Trezor, …) en upload complete bestanden.
  • 4. Versleutel en exfiltreer via een openbare pinning-service. Elke bevinding is gekoppeld aan een gastheer-vingerafdruk (gebruikersnaam@hostnaam, platform, tijdstempel) en versleuteld met een hardgecodeerde RSA-4096 openbare sleutel die in het script is ingebed. Het versleutelde record wordt vervolgens geüpload door het vast te zetten op IPFS via api.pinata.cloud/pinning/pinJSONToIPFS, geauthenticeerd met hardgecodeerde Pinata API-gegevens. Er is geen speciale C2-server die in beslag genomen kan worden: de gestolen gegevens worden opgeslagen in openbare, gedecentraliseerde opslag, die door de operator kan worden opgevraagd met behulp van de resulterende inhoudshashes. Uploads worden met een paar seconden willekeurige tussenpozen uitgevoerd en er wordt een lokaal logboek bijgehouden van tijdstempel | sleuteltype | geretourneerde hash wordt bewaard op ~/.cache-db/.node-sync/.sl.
  • 5. Blijf volhouden en zend een baken uit met een cyclus van 12 uur. De tweede fase herstelt zijn eigen persistentie — een cron-taak op Linux, een com.apple.syncd launchd-taak op macOS, en een geplande taak genaamd WindowsNodeSync Op Windows — ingesteld om elke 12 uur opnieuw te worden uitgevoerd. Let op: dit is een anders De Windows-taaknaam is afkomstig van de taak die de dropper installeert (WinNodeSyncBeide zijn de moeite waard om naar op zoek te gaan.

Timeline

De acht pakketten werden kort na elkaar gepubliceerd op 13 en 14 juli 2026. Verschillende pakketten bevatten meer dan één versie; de ​​dropper is in alle versies identiek, alleen de regeloffsets verschuiven naarmate de grootte van de bovenliggende utility-code verandert.

Datum Evenementen
2026-07-13 De eerste pakketten in het cluster verschijnen onder één uitgever (solana-key-utils, eth-wallet-helpers, crypto-validate-lib en vroege versies van de rest)
2026-07-13 → 07-14 De overige namen en vervolgversies zijn gepubliceerd; dezelfde toegevoegde dropper-schepen in elk geval.
2026-07-14 Alle acht zijn gemarkeerd en geanalyseerd; elk pakket kan nog steeds vanuit het register worden geïnstalleerd.

Tijdens de uitbarsting veranderde de reputatie van de uitgever in het register van ongeveer neutraal aan het begin van de cluster naar sterk negatief naarmate er meer detecties werden gedaan — een waarneembaar neveneffect van het markeren van de pakketten, geen opzettelijke eigenschap.

Indicatoren van compromis

Alle onderstaande indicatoren bleken aanwezig te zijn op het moment van analyse — die in de tabellen van de "tweede fase" door middel van decodering. test/fixtures/keypairs.dat en het lezen van de herstelde documenten syncd.js.

Bestanden en paden

Indicator Rol
~/.cache-db/.node-sync/syncd.js Gedecodeerde tweede fase, geschreven met modus 0o700
~/.cache-db/.node-sync/.sl Lokaal exfiltratielogboek (tijdstempel | sleuteltype | IPFS-hash)
test/fixtures/keypairs.dat Base64-gecodeerde payload in het tarball-bestand geplaatst als "testomgeving".

Netwerkinfrastructuur van de tweede fase (hersteld vanuit syncd.js)

Indicator Rol
gist.githubusercontent.com/juang55/b298754cb72942b1cdcf02ccd45cde2f/raw/cfg.txt Activering via een doodlopende weg; script wordt alleen uitgevoerd als de configuratie correct is. active=1
Qmcqz3w8j4qFQXDAXAxnrdc2oSX3nzBT4NqtpTqL8mr1ga IPFS-configuratie terugvaloptie (CID)
QmdTXoqVmTHY1i4ZWLdLkoQ9YChp5TXPh5cWXwnAYZt5iF IPFS-configuratie terugvaloptie (CID)
QmfJkLU5gdCpqbbqEjWYC2anXW9FmuEeSLLeLiHVJKYUjp IPFS-configuratie terugvaloptie (CID)
gateway.pinata.cloud, ipfs.io, cloudflare-ipfs.com IPFS-gateways worden gebruikt om de fallback-configuratie op te halen.
api.pinata.cloud/pinning/pinJSONToIPFS Exfiltratie-eindpunt, gestolen gegevens gekoppeld aan openbaar IPFS
Pinata API-sleutel 13c766575b9270a9825dhardgecodeerde exfiltratiegegevens

Doelen van de tweede fase van de verzameling (opgehaald uit syncd.js)

Indicator Rol
~/.config/solana, ~/.ethereum/keystore, ~/.foundry, ~/.hardhat, ~/.ssh, ~/.env, shell rc-bestanden Mappen/bestanden doorzocht op sleutels en geheimen
AppData\Roaming\Solana, AppData\Local\ethereum\keystore Windows-equivalenten gezocht
Geoogste artefacttypen ETH privésleutels, Bitcoin WIF, BIP-39 seed-frases, Solana sleutelparen, Ethereum keystore JSON, SSH-sleutels, geheime omgevingsvariabelen

Artefacten van persistentie

Platform Indicator
Linux cron-taak starten syncd.js; geïnstalleerd via crontab gefilterd via grep -v syncd
Windows geplande taak WinNodeSync (druppelaar) en WindowsNodeSync (tweede fase)
macOS launchd label com.apple.syncd
Alles De tweede fase herhaalt zich in een cyclus van 12 uur.

Behavioral

  • Zelfaanroepende functie toegevoegd na module.exports, het plannen van een setTime-out van ~37,000 ms bij het importeren.
  • kind_proces spawn(“node”, ) met de optie 'losgekoppeld' ingesteld.
  • Activering via een inactieve poort in de tweede fase (xprintinactief / ioreg HIDIdleTime / PowerShell-inactiviteitstijd; drempelwaarde van ongeveer 15 minuten).
  • Vooraf gevonden RSA-4096-versleuteling, daarna HTTPS POST naar een openbare IPFS-pinning-API.

Pakketten en versies (npm, uitgever solbuilder_io)

Pakket versies
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

Uitgever

  • solbuilder_io - angel_lopez89[@]proton[.]me, e-mailadres niet geverifieerd, geen geverifieerd versiebeheeraccount, geen gekoppelde repository.

Toeschrijving en geobserveerd gedrag

De acht pakketten delen één uitgeversaccount en één payload. Elk pakket is triviaal – een paar kilobyte aan nuttige code met dezelfde dropper eraan toegevoegd – gepubliceerd zonder gekoppelde repository en onder een niet-geverifieerd, tijdelijk e-mailadres. Die uniformiteit, het gedeelde drop-pad, de gedeelde persistentielabels en de gedeelde sleutelparen.dat De staging-bestanden zorgen ervoor dat het cluster als het ware met elkaar verbonden is.

De pakketten zijn opvallend openhartig over hun eigen gedrag. solana-key-utils bevat inline-commentaren die het toegevoegde blok in eenvoudige bewoordingen beschrijven — één daarvan geeft het een label. PHANTOM: onzichtbare persistentie, een andere (in het Spaans) luidt Ejecutar topo en background"De mol op de achtergrond laten draaien." Dit zijn de eigen annotaties van de code over wat hij doet; de campagnenaam in dit bericht is afgeleid van dat eerste label, samen met het nepknooppunt "sync daemon" (gesynchroniseerd) die het persistentiemechanisme nabootst.

We beschrijven alleen wat de code aantoonbaar doet. De naamgeving van de pakketten – allemaal termen uit de crypto-, wallet- en blockchain-wereld – geeft aan welke ontwikkelaarsgroep ze waarschijnlijk zal downloaden; het zegt op zichzelf niets over de uitgever. De tweede fase was volledig te herstellen door de base64-fixture te decoderen, en het gedrag ervan is hierboven beschreven: het verzamelt wallet-sleutels, seed-phrases en geheimen en exfiltreert deze, RSA-gecodeerd, naar openbare IPFS-opslag via Pinata. Een opvallende ontwerpkeuze is de afwezigheid van een private C2-server – de configuratie komt van een GitHub gist en IPFS, en gestolen data wordt opgeslagen in openbare gedecentraliseerde opslag met een inhoudshash als sleutel. Beide zijn moeilijker te bemachtigen dan een enkele door een aanvaller gecontroleerde host. Ten tijde van het schrijven van dit artikel zijn er geen eerdere openbare rapporten gevonden die overeenkomen met dit cluster.

Wie wordt blootgesteld? Iedereen die een van deze pakketten aan een Node-project heeft toegevoegd en vervolgens code heeft uitgevoerd die het importeert. Omdat de detonatie plaatsvindt tijdens het importeren en niet tijdens de installatie, is de aanwezigheid van het pakket op zich niet voldoende — maar elk normaal gebruik dat de module laadt, bereikt de payload. De loknamen zijn gericht op ontwikkelaars die bouwen op Ethereum, Solana, Arbitrum, Layer-2 en algemene wallet-/coderingstools — de groep die het meest waarschijnlijk beschikt over precies de activa waar de tweede fase naar op zoek is. Iedereen die een van deze modules heeft uitgevoerd op een machine met wallet-sleutels, seed-phrases, keystores, SSH-sleutels, of .env Beveiligers moeten deze inloggegevens als gecompromitteerd beschouwen en ze regelmatig vervangen.

Twee patronen die het waard zijn om te internaliseren. Eerste, lading als bevestigingsmiddel: het verzenden van de tweede fase als base64 in een gegevensbestand met een plausibele naam (test/fixtures/keypairs.dat) zorgt ervoor dat de zichtbare bron van het pakket er schoon uitziet en duwt de schadelijke inhoud naar een bestand dat door controletools en mensen vaak als inerte data wordt beschouwd. Ten tweede, Uitgestelde uitvoering tijdens import met platformoverschrijdende persistentieDoor de trigger niet langer in de installatiehook te plaatsen, maar een timer toe te voegen en deze vervolgens te laten doorlopen via cron, geplande taken en launchd, is een bewuste stap weg van de meer opvallende installatiescripttechnieken die door geautomatiseerde registerscans het meest nauwlettend in de gaten worden gehouden.

Richtlijnen voor verdedigers en beheerders:

  • Verwerk toegevoegde code daarna module.exports Als beoordelingsprioriteit: de logica achter de dropper zit vaak verborgen onder het oppervlak van de "echte" module.
  • Ga er niet van uit dat gegevensbestanden inert zijn. Een base64-blob onder test/armaturen/ dat tijdens de uitvoering wordt gelezen en gedecodeerd is uitvoerbaar-aangrenzend; markeert tijdens de uitvoering het lezen van fixture-bestanden die een invoer leveren aan een Functie/eval/schrijf-dan-paaien keten.
  • Zoek naar het afdaalpad. ~/.cache-db/.node-sync/ en voor de persistentie-eenheden WinNodeSync (geplande taak) en com.apple.syncd (gelanceerd) op ontwikkelaarsmachines die deze namen hebben opgehaald.
  • Waarschuwing voor Node-processen die cron-taken, geplande taken of launchd-jobs aanmaken — legitieme bibliotheken doen dit zelden bij het importeren.
  • Geef de voorkeur aan lockfiles en vastgezette versies, en controleer de diff van elke nieuwe kleine "hulpprogramma"-afhankelijkheid, met name pakketten zonder afhankelijkheden gepubliceerd door niet-geverifieerde accounts zonder gekoppelde repository.

Voor beheerders van registers is de conclusie dat monitoring van installatie-hooks noodzakelijk, maar niet voldoende is: een dropper die tijdens het importeren met een timervertraging in een gegevensbestand wordt geplaatst, zal een controle tijdens de installatie alleen doorstaan, en de persistentiestap is vaak het meest opvallende signaal dat nog moet worden opgevangen.

sca-tools-software-compositie-analyse-tools
Prioriteer, herstel en beveilig uw softwarerisico's
Maak nu een gratis account aan.
Geen kredietkaart nodig.

Beveilig uw softwareontwikkeling en -levering

met Xygeni-productsuite