TL; DR
Tussen 1 april en 3 mei 2026 zal één enkele npm-publisher, user0001, geregistreerd met het niet-geverifieerde Gmail-adres tanvisoul9@gmail.com, bracht stilletjes zes pakketten op de markt met opzettelijk saaie, op infrastructuur lijkende namen: centralogger, dom-utils-lite, node-fetch-lite, connector-agent, node-gyp-runtimeen node-env-resolve.
De twee meest recente versies van node-env-resolveDe versies 1.0.7 en 1.0.8 werden op 2 mei door Xygeni's Malware Early Warning (MEW)-systeem als schadelijk aangemerkt en op 3 mei bevestigd.
Het implantaat is ongebruikelijk vanwege wat het níét is: er zijn geen Telegram-bots, geen OAST-callbacks, geen verduisteringen er werd tijdens de installatie geen poging gedaan om AWS-referenties te bemachtigen. In plaats daarvan, postinstall.js plant een Windows-opstartpersistentie-item in, met behulp van een HKCU Run-toets die wordt gestart. wscript.exe tegen een VBS-stub. Vervolgens start het een losgekoppelde Node.js-agent die modules bundelt voor microfoonopname, het stelen van browsergeschiedenis, het maken van screenshots en muis-/toetsenbordsimulatie.
We noemen dit cluster DevTapNa de installatie blijft het programma draaien op de computer van de ontwikkelaar.
Alle zes pakketten waren op het moment van schrijven nog beschikbaar op npm. We hebben ze gemeld bij het misbruikkanaal van het register. Beheerders dienen alle installaties van de uitgever te verwijderen in afwachting van verwijdering.
Het cluster: zes pakketten, één uitgever
Het uitgeversaccount user0001 is niet geverifieerd. Het heeft geen SCM verificatie, geen domeingebonden e-mailadres en geen eerdere geschiedenis. Het Gmail-gebruikersnaam tanvisoul9 komt niet voor in andere npm-publisherrecords die we hebben kunnen vinden.
Alle zes pakketten hebben dezelfde beheerder, zijn gepubliceerd vanaf hetzelfde account en delen dezelfde functionaliteit. package.json standaardformulering. Er is geen repositoryGeen homepage, en nee description langer dan één regel.
De naamgevingsstrategie is het interessante aspect. In tegenstelling tot een klassieke campagne die verwarring zaait over afhankelijkheden of een typosquat-campagne, is geen van deze namen gericht op een specifieke upstream-bibliotheek. In plaats daarvan zijn ze zo ontworpen dat ze opgaan in de werkelijke code. package.json or npm ls uitgang.
| Pakket | Eerste druk | Laatste versie | versies | Rol binnen het cluster |
|---|---|---|---|---|
| centralogger | 2026-04-01 12:46 UTC | 1.0.9 | 5, van 1.0.5 tot 1.0.9 | Omslag van het "logboekregistratieprogramma" van Cluster; vroegste publicatie |
| dom-utils-lite | 2026-04-14 07:36 UTC | 1.0.3 | 3 | Algemene DOM-helper cover |
| node-fetch-lite | 2026-04-19 10:22 UTC | 1.0.2 | 3 | Bootst de node-fetch-familie na. |
| connector-agent | 2026-04-25 05:12 UTC | 1.0.0 | 1 | Generieke naam “agent” |
| node-gyp-runtime | 2026-04-25 05:17 UTC | 1.0.0 | 1 | Bootst de buildtools van native modules na. |
| node-env-resolve | 2026-04-25 05:21 UTC | 1.0.9 | 10, van 1.0.0 tot 1.0.9 | Actieve druppelaar; volledig implantaat |
Namen zoals centralogger, node-fetch-liteen node-gyp-runtime Ze zijn zo ontworpen dat ze er tijdens een codebeoordeling onomstreden uitzien. Ze klinken als elementen die al in de afhankelijkheidsstructuur van een project aanwezig zouden zijn.
In combinatie met een nieuwe, niet-geverifieerde uitgever en ontbrekende links naar de repository, vormen ze een herkenbaar patroon: een partij die namen met weinig problemen in het register plaatst en vervolgens snel de namen selecteert die er echt toe doen. In dit geval, node-env-resolve Ik ontving tien versies in acht dagen.
Wat komt er op een Windows-ontwikkelcomputer terecht?
De kwaadaardige keten op node-env-resolve:1.0.8 Het is kort, direct en ongebruikelijk rijk aan functies voor een npm RAT.
Na de installatie: persistentie en losgekoppelde agent
package.json declareert één enkele installatiehook:
{ "scripts": { "postinstall": "node postinstall.js" } }
postinstall.js Doet drie dingen in de aangegeven volgorde.
Eerst wordt er een installatiemap buiten de npm-directory aangemaakt. Het pad wordt tijdens de uitvoering van het script berekend als INSTALL_DIRVervolgens voert het een interne procedure uit. execSync('npm install --production --silent ...') Binnenin wordt de runtime-afhankelijkheden van het implantaat opgehaald. Hierdoor wordt de agent ergens handmatig op de schijf geplaatst. node_modules De audit zal niets vinden.
Ten tweede schrijft het een VBS-launcher en registreert deze voor persistentie tijdens het opstarten:
reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Run \
/v ${AGENT_NAME} \
/t REG_SZ \
/d "wscript.exe \"${vbsPath}\"" /f
wscript.exe is de Windows Script Host, een ondertekend Microsoft-programma dat wordt uitgevoerd .vbs bestanden zonder consolevenster. Dat is voorafcisDaarom wordt het vaak gebruikt voor autorun-stubs.
Er is ook een bijpassende optie. reg delete pad binnen src/index.jsDit suggereert dat het implantaat zo is ontworpen dat het op commando van de gebruiker gemakkelijk en zonder resten kan worden verwijderd.
Ten derde start het een losgekoppeld kindproces:
spawn(node, [path.join(INSTALL_DIR, 'src/index.js')], {
detached: true,
env: { ...process.env, SERVER_URL }
})
Twee details zijn hier van belang.
SERVER_URL wordt uit de omgeving gelezen. Daardoor is het C2-eindpunt per implementatie configureerbaar en niet standaard in het pakket opgenomen. Deze kleine maar bewuste keuze maakt de meeste statische IOC-scans onmogelijk.
Bovendien erft het aangemaakte kind de volledige omgeving van de ouder. Daarom kan elk kind de omgeving van het kind overnemen. NPM_TOKEN, AWS_*, GITHUB_TOKEN, of de SSH-agent-socket die tijdens de installatie in de ontwikkelaarsomgeving aanwezig is, wordt opgeslagen in het geheugen van de agent met een lange levensduur.
Wat de agent verzendt
De gebundelde src/ De boomstructuur bevat drie modules waarvan de namen al veelzeggend zijn: audioCapture.js, browserHistory.jsen systemInfo.js.
De runtime-afhankelijkhedenlijst, opgelost tijdens de stagingfase. npm install, bevestigt ze.
Deze aanpak transformeert een chaotisch incident in een gecontroleerde reactie.
In plaats van blindelings te reageren, werken teams samen met Duidelijke prioritering en snelle oplossing..
| Afhankelijkheid | Waarvoor dient het in deze context? |
|---|---|
| schermafbeelding-bureaublad | Periodieke schermopname |
| @nut-tree-fork/nut-js | Automatisering van muis en toetsenbord / invoersimulatie |
| beter-sqlite3 | Directe toegang tot de SQLite-databases met de geschiedenis van Chrome, Edge en Firefox. |
| adm-zip | Het bundelen van verzamelde artefacten vóór de exfiltratie. |
| scherp | Schermafbeeldingen en beeldartefacten verkleinen/comprimeren |
| socket.io-client | Aanhoudend bidirectioneel C2-kanaal |
| node-machine-id | Stabiele vingerafdruk per host voor het traceren van slachtoffers. |
systemInfo.js gesprekken os.networkInterfaces() voor aanvullende vingerafdrukregistratie vóór de eerste baken.
Waarom de audio-opname het meest opvallende signaal is
De meeste npm RAT's die we in MEW opsporen, stoppen bij het stelen van geheime gegevens tijdens de installatie. Ze lezen... ~/.npmrc, Lees ~/.aws/credentialsschrapen process.env, een POST-verzoek naar een webhook sturen en vervolgens afsluiten. Dat past in een economisch model van snelle, meedogenloze aanvallen. Inloggegevens zijn maar kort geldig en de aanvaller moet snel geld verdienen.
node-env-resolve is anders van vorm.
De persistentie is zo ingesteld dat deze een herstart overleeft. De agent draait losgekoppeld en langdurig onder wscript.exeDe meegeleverde kit is bedoeld om permanent op de computer van een ontwikkelaar te staan, niet om even snel mee te nemen: een microfoonrecorder, een toetsenbord-/muisstuurprogramma, volledige browsergeschiedenisregistratie, schermopname en een interactief Socket.IO-kanaal naar een configureerbare C2-server.
Het opnemen van microfoons is met name de grens die deze campagne overschrijdt, iets wat de meeste npm-malware niet doet.
Een ontwikkelaarswerkplek is steeds vaker ook de plek waar ontwikkelaars stand-ups houden, klantgesprekken voeren, ontwerpbesprekingen houden en bereikbaarheidsdiensten draaien. Een implant die de microfoon opneemt, is niet zozeer geïnteresseerd in het npm-token van de ontwikkelaar, maar in wat de ontwikkelaar zegt achter de laptop.
Die reeks mogelijkheden, in combinatie met het ontbreken van verhulling en de afwezigheid van opvallende datalekken tijdens de installatie, duidt eerder op een voorbereiding op gerichte toegang dan op opportunistische diefstal van inloggegevens.
We baseren onze toeschrijving niet uitsluitend hierop. We beschrijven het operationele profiel.
De iteratiecyclus van acht dagen op node-env-resolve is het meest operationeel veelzeggende detail in de tijdlijn.
Dit is geen eenmalige installatie. De uitgever onderhoudt de dropper actief, wat meestal een van de volgende twee dingen betekent: ofwel stemmen ze de dropper af op testomgevingen voordat ze deze op grotere schaal uitrollen, ofwel ontvangen ze al installatiegegevens en reageren ze daarop.
De andere vijf pakketten hebben na hun eerste publicatie vrijwel geen wijzigingen ondergaan. Dat past bij een "één keer klaarzetten, naam behouden"-patroon voor het ondersteunende cluster, terwijl de technische inspanningen zich richten op het pakket dat het werk uitvoert.
Bronvermelding: Kleine aanwijzingen, geen definitief oordeel
Publieke OSINT De signalen zijn zwak en we zullen ze niet uitrekken. Wat waarneembaar is:
De Gmail-gebruikersnaam tanvisoul9 Het lijkt gedeeltelijk op een Zuid-Aziatische voornaam, "Tanvi". Dit is een zwak signaal. Gmail-gebruikersnamen zijn geen identiteit, en de achtervoegsels soul9 is generiek. Het is niet veilig om de geografische locatie af te leiden uit alleen het lokale gedeelte van een e-mailadres.
De code-stijl is onopvallend in Node.js: standard bibliotheekaanroepen zoals os, child_process, spawnen reg addEr is geen sprake van verhulling, geen tekenreeksrotatie en geen anti-debuglogica. De auteur is vertrouwd met Windows-registerprimitieven en de orkestratie van losgekoppelde subprocessen, maar lijkt niet te streven naar meer heimelijke methoden die hij mogelijk zou kunnen gebruiken.
De afhankelijkheden zijn volledig standaard en algemeen bekend: screenshot-desktop, nut-js, better-sqlite3en socket.io-clientEr is geen aangepast protocol, geen volledig vanaf nul opgebouwde C2-stack en geen nieuwe persistentietruc die verder gaat dan wat in leerboeken staat. HKCU-sleutel uitvoerenDit is een bekwame integrator, geen ontwikkelaar van tools.
We hebben geen niet-Engelstalige tekenreeksen, ingebedde commentaren, lokalisatiegegevens of tijdzone-vingerafdrukken van de compileruitvoer in de gepubliceerde artefacten gevonden.
We hebben gecontroleerd op code-overlappingen met eerdere campagnes die we al volgen, waaronder Shai Hulud, UilivionBuildkite en de recente heibai / claude-code-best We hebben geen overeenkomsten gevonden met de Anthropic-CLI-kloonfamilie: geen gedeelde C2-patronen, geen gedeelde bestandsindelingen en geen gedeelde idiomen.
Wat we níét zouden zeggen: dat het hier om een staatsactor, een bekende groepering of een geografische binding met een specifiek land gaat.
Het operationele profiel komt overeen met dat van een klein team met gemiddeld geschoolde medewerkers dat bewaking uitvoert op werkstations van ontwikkelaars. De financiële motivatie ligt waarschijnlijk in het gebruik van de verkregen toegang door derden, maar mogelijk ook in het aanbieden van verkenningsdiensten aan een aparte afnemer.
Twee indirecte argumenten ondersteunen dat: het vermijden van opvallende methoden om gegevens uit het cluster te halen die het snel onbruikbaar zouden maken, zoals Telegram-bots, oastify.comOfwel canarytokens, en de opzettelijk saaie pakketnamen, die de voorkeur geven aan stille installaties met een lange levensduur boven een korte piek met een hoog volume.
Indicatoren van inbreuk en detectie
| Pakketten en uitgever | |
|---|---|
| Veld | Waarde |
| npm-uitgever | user0001 |
| E-mailadres van de uitgever | tanvisoul9@gmail.com, niet geverifieerd |
| Formules | centralogger, dom-utils-lite, node-fetch-lite, connector-agent, node-gyp-runtime, node-env-resolve |
| Bevestigd kwaadaardig | node-env-resolve@1.0.7, node-env-resolve@1.0.8 |
| Gastheer-artefacten | |
|---|---|
| Type | Waarde |
| Persistentiesleutel | HKCU\Software\Microsoft\Windows\HuidigeVersie\Uitvoeren |
| Persistentiewaarde | Variabelenaam; gegevens in de vorm wscript.exe " \\ .vbs" |
| Haak installeren | postinstall: node postinstall.js in het pakketmanifest |
| Implantatiemodules | src/audioCapture.js, src/browserHistory.js, src/systemInfo.js |
| Staging-directory | INSTALL_DIR wordt buiten de node_modules-map van het project gevonden; de locatie wordt tijdens de installatie door postinstall.js bepaald. |
| Netwerk | |
|---|---|
| Type | Waarde |
| C2-transport | socket.io-client, persistent bidirectioneel kanaal |
| C2-eindpunt | Wordt aan de agent verstrekt via de omgevingsvariabele SERVER_URL; niet hardgecodeerd in gepubliceerde artefacten. |
Detectie-aantekeningen
Twee regels vangen dit gezin op zonder dat het C2-eindpunt nodig is.
Markeer eerst de postinstallatiescripts die een interne controle uitvoeren. npm install naar een pad buiten de eigen map van het pakket. Elke legitieme prebuild-downloader, zoals node-gyp herbouwt of downloadt vooraf gecompileerde binaire bestanden, schrijft binnen het pakket of naar het platform.standard Cachepaden met geverifieerde hashes. Er wordt niet geschreven naar een nieuw aangemaakt bestand. INSTALL_DIR elders op de schijf.
Ten tweede, markeer postinstallatiescripts die dit probleem veroorzaken. reg add HKCU\…\Run with wscript.exe als de launcher. Dit is in principe nooit legitiem voor een npm-pakket. Markeer en plaats het in quarantaine.
Een derde heuristiek is handig om het volgende verwante pakket te herkennen voordat het bevestigd is: een nieuwe npm-publisher zonder SCM verificatie, een Gmail-e-mailadres, nee repository Het feit dat er meerdere korte, generieke, op infrastructuur lijkende pakketnamen binnen enkele dagen na elkaar zijn gepubliceerd, is op zich al voldoende reden voor handmatige controle.
Gemeld bij het npm-register. We zullen dit bericht bijwerken zodra het uitgeversaccount is verwijderd.




