TL; DR
Tra il 15/06/2026 e il 16/06/2026 un singolo editore npm ha spinto sei pacchetti — 26 versioni in tutto — che eseguono uno script di installazione su ogni npm installOgni pacchetto include un file README che lo dichiara un artefatto di ricerca "innocuo" del programma Bug Bounty di HackerOne/GitHub, che "non" modifica mai i valori delle credenziali e "non effettua alcuna persistenza". Il codice non corrisponde all'informativa. Sui runner CI Linux, l'hook di installazione rileva l'impronta digitale dell'ambiente e sonda un endpoint dei metadati dell'istanza cloud; su Windows richiede privilegi elevati, viene eseguito come SYSTEMe apre un canale di comando verso un host esterno. L'unico indicatore da cercare ora è l'host C2 173.255.233[.]239. Gravità: altoEcosistema interessato: npm. I nomi si fondono con il rumore delle dipendenze dell'integrazione continua (metrics-pipeline-d8k2, event-metrics-q3x7, pkg-telemetry-r4f9(e tre fratelli).
L'attacco: come funziona
Ogni pacchetto nel cluster è costruito allo stesso modo. Il pacchetto.json collega lo stesso comando in due cicli di vita hooks:
{ "scripts": { "preinstall": "node run.js", "postinstall": "node run.js" } } Funziona su entrambi preinstallazione e postinstallazione significa che il payload viene eseguito indipendentemente dal fatto che le fasi di installazione successive falliscano o meno. run.js Si tratta di un sistema di gestione delle piattaforme a dieci linee:
// Platform dispatcher — runs beaconNN.js on Windows, beacon_linux.js on Linux. // GitHub Bug Bounty authorized research. Contact: nicholas@curran.tech 'use strict'; const { execFileSync } = require('child_process'); const path = require('path'); const node = process.execPath; const script = process.platform === 'win32' ? path.join(__dirname, 'beacon34.js') : path.join(__dirname, 'beacon_linux.js'); try { execFileSync(node, [script], { stdio: 'inherit', timeout: 90000 }); } catch (e) {} Il comportamento si biforca quindi a seconda del sistema operativo. Tutti e sei i pacchetti contengono gli stessi due elementi: run.js e i beacon della piattaforma — quindi sono dropper intercambiabili piuttosto che sei strumenti distinti. L'unica differenza significativa tra loro è la cadenza di pubblicazione: quattro nomi sono stati rilasciati una sola volta, mentre due sono stati ripubblicati ogni 30-60 minuti per ore, il che mantiene una versione appena pubblicata di fronte agli installer e ai resolver anche quando le versioni precedenti vengono rimosse.
Su Linux (beacon_linux.js) lo script profila dove è in esecuzione. Enumera i nomi delle variabili d'ambiente, legge i file sotto / proc, controlla per docker.socke corre hostname and whoamiSuccessivamente, invia una richiesta HTTP all'endpoint dei metadati dell'istanza del container AWS. 169.254.170[.]2 — l'indirizzo link-local che un'attività ECS interroga per ottenere la propria configurazione e le credenziali di ruolo di breve durata (IMDS, il servizio di metadati dell'istanza). I risultati vengono inviati tramite POST al collettore della campagna all'indirizzo 173.255.233[.]239.
Su Windows (beacon34.js / beacon18.js) il pacchetto fa considerevolmente di più che profilare il suo host. Il codice segnalato aggiunge una chiave di registro sotto il ms-impostazioni gestore con un DelegateExecute valore — una nota tecnica di bypass del controllo dell'account utente che consente a un processo avviato di essere eseguito con privilegi elevati senza richiesta di autorizzazione. Richiama PowerShell con -ExecutionPolicy Bypass and Invoca-Espressione, esegue nel NT AUTHORITY \ SYSTEM contesto e interroga un host esterno per i comandi. Il canale di comando di Windows viene eseguito su un Tunnel di Cloudflare, Diameter-coins-limitations-parents.trycloudflare[.]com:443, ricadendo a 173.255.233[.]239:443, utilizzando tre endpoint: /c2/sondaggio per recuperare un comando, /c2/out per restituire l'output, /c2/eof chiudere.
La novità qui: la clausola di esclusione di responsabilità come camuffamento.
L'elemento innovativo non è il payload in sé: la ricognizione dell'hook di installazione e l'elevazione dei privilegi di Windows sono entrambe ben documentate. È la storia di copertura. Ogni pacchetto contiene un file README che si presenta come un documento di divulgazione responsabile:
# @ncurran/sandbox-recon-880538 — authorized npm-sandbox security research Questo è benigno pacchetto pubblicato sotto l'ambito `@ncurran` dell'autore stesso come parte di un Coinvolgimento autorizzato nel programma Bug Bounty di HackerOne/GitHub (npm è incluso).
Durante l'installazione viene effettuato un controllo. il proprio ambiente di esecuzione … variabile d'ambiente
solo i nomi delle chiavi … verifica se è ben noto endpoint dei metadati del cloud sono raggiungibile... Riporta solo i codici di stato, le lunghezze di risposta e hash — mai nessuna credenziale, valore del token o dati di terze parti. Non esegue alcuna operazione Persistenza, assenza di movimenti laterali e assenza di azioni distruttive.
Tre affermazioni in quel testo sono contraddette dal codice distribuito. Il README dice "nessuna persistenza", ma il beacon di Windows scrive una chiave di elevazione basata sul registro. Dice "mai alcun valore di credenziale o token", ma il percorso di Windows esegue comandi recuperati arbitrari come SISTEMA, che non impone alcun limite a ciò che viene letto o restituito. Addirittura nomina un pacchetto — @ncurran/sandbox-recon-880538 — non è quello in cui viene spedito, il che indica che il README è un modello riutilizzato piuttosto che una descrizione dell'artefatto in questione. Un disclaimer sul consenso in un pacchetto che la vittima non ha mai accettato di installare non concede alcun consenso. La classificazione in questo caso è dannosa a prescindere dalla formulazione. Il disclaimer ha anche un obiettivo più subdolo: una fase di triage automatizzata che legge il testo del pacchetto alla ricerca di segnali rassicuranti — "innocuo", "autorizzato", un programma specifico, un indirizzo email di contatto — può essere spinta verso un verdetto di sicurezza da una prosa che il payload contraddice. La lezione vale sia per i revisori umani che per quelli automatici: valutare il comportamento in fase di installazione, non l'autodescrizione del README.
timeline
Tutti i pacchetti sono apparsi entro un intervallo di 24 ore. I quattro nomi a versione singola sono stati pubblicati per primi, seguiti da due pacchetti che sono stati ripubblicati ogni 30-60 minuti.
| Data (UTC) | Event |
|---|---|
| 2026-06-15 18:32 | Primo pacchetto pubblicato — npm:pkg-telemetry-r4f9@1.0.0 |
| 2026-06-15 19:50 | npm:runtime-metrics-w7k2@1.0.0 pubblicato |
| 2026-06-15 20:14 | npm:build-tracker-n5p1@1.0.0 pubblicato |
| 2026-06-15 20:35 | npm:npm-sandbox-ping-r9t2@1.0.0 pubblicato |
| 2026-06-15 21:09–22:42 | npm:event-metrics-q3x7 pubblicato 1.0.0 → 1.0.8 (9 versioni) |
| 2026-06-15 23:40 → 2026-06-16 04:40 | npm:metrics-pipeline-d8k2 pubblicato 1.0.0 → 1.0.10 (11 versioni) |
| 2026-06-16 | Cluster identificato e classificato come dannoso; diversi archivi tar stanno già restituendo un errore 404 al registro (attività in corso). |
La campagna è recente e la fase di rimozione è ancora in corso: al momento dell'analisi, alcune versioni risultavano correttamente registrate, altre no. Considerate tutte le 26 versioni come compromesse.
Indicatori di compromesso
personalizzati
| Ecosistema | CONFEZIONE | versioni | Stato |
|---|---|---|---|
| npm | metrics-pipeline-d8k2 | 1.0.0 – 1.0.10 | maligno |
| npm | event-metrics-q3x7 | 1.0.0 – 1.0.8 | maligno |
| npm | runtime-metrics-w7k2 | 1.0.0 | maligno |
| npm | build-tracker-n5p1 | 1.0.0 | maligno |
| npm | npm-sandbox-ping-r9t2 | 1.0.0 | maligno |
| npm | pkg-telemetry-r4f9 | 1.0.0 | maligno |
Reti
| Tipo | Note | |
|---|---|---|
| IP | 173.255.233[.]239 | Collettore C2; POST di ricognizione Linux e fallback Windows :443 |
| Domini | diameter-coins-limitations-parents.trycloudflare[.]com | Canale di comando Windows su un tunnel Cloudflare, :443 |
| percorso URI | /c2/poll, /c2/out, /c2/eof | Comando Windows poll / output / close |
| IP | 169.254.170[.]2 | Endpoint dei metadati dell'istanza AWS ECS rilevato dal hook di installazione |
Behavioral
| Signal | Descrizione |
|---|---|
| Installazione hooks | preinstall and postinstall entrambi corrono node run.js |
| Piattaforma di elaborazione | run.js corre beacon_linux.js su Linux, beacon34.js / beacon18.js Su Windows |
| Ricognizione Linux | Enumera i nomi delle chiavi delle variabili d'ambiente, legge /proc, controlli docker.sock, corre hostname / whoami, sonda IMDS |
| Prospetto delle finestre | ms-settings DelegateExecute Bypass del registro UAC; PowerShell -ExecutionPolicy Bypass + Invoke-Expression; funziona come SYSTEM |
| Travestimento | Il file README afferma che si tratta di una ricerca "innocua", "autorizzata" e "senza persistenza"; fa riferimento a un nome di pacchetto che non viene spedito. |
Attribuzione e comportamento osservato
Descriviamo l'editore solo attraverso i suoi segnali e non affermiamo l'identità di alcuna persona dietro l'account.
- Catalogo dei segnali. Tutti e sei i pacchetti sono stati pubblicati dallo stesso account npm, npmresearch8847, con l'indirizzo email indicato npmresearch8847@web-library.net (e-mail e SCM verifica entrambe assenti; non abbiamo verificato la proprietà). I README sono pubblicati sotto un @ncurran ambito e fornire un contatto di nicholas@curran.teche run.js punti presso il deposito github.com/ncurran/npm-sandbox-research`. I sei pacchetti condividono un identico run.js addetto alla gestione delle spedizioni, un comune beacon_linux.js fase di ricognizione e singolo host del collettore 173.255.233[.]239`. Lo schema di denominazione è coerente: una radice generica dal suono CI (metrica, telemetria, tracker di build, sandbox-ping) più un breve suffisso casuale.
- Fiducia. I sei pacchetti sembrano far parte di un'unica campagna, data la presenza di un dispatcher, una fase di ricognizione e un host C2 condivisi. La dicitura "ricerca autorizzata" appare coerente in tutti e sei i file README. Non siamo stati in grado di confermare che un programma di bug bounty abbia autorizzato questa attività e le affermazioni contenute nel disclaimer non corrispondono al codice distribuito.
- Capacità osservata. Il carico utile esercitacises quattro classi di capacità: esecuzione del codice install-hook su entrambi preinstallare and post-installazione; ricognizione dell'ambiente host e CI con uscita verso un collettore esterno; sondaggio della raggiungibilità dei metadati dell'istanza cloud dal contesto di installazione; e, su Windows, escalation dei privilegi locali, esecuzione come SISTEMAe un ciclo di comandi di prelievo ed esecuzione su un canale esterno. Il ciclo di comandi di Windows è una funzionalità di controllo remoto: interroga il sistema per ottenere istruzioni e restituisce il loro output.
Impatto, tendenze e cosa dovrebbero fare i difensori
Impact
L'esposizione è massima per gli ambienti di build automatizzati. Un npm install in CI esegue il ciclo di vita hooks con qualsiasi identità detenga il runner; su un runner cloud tale identità spesso include un endpoint di metadati raggiungibile e una credenziale di ruolo. L'indirizzo che il beacon Linux sonda, 169.254.170[.]2, è l'endpoint dei metadati delle attività di AWS ECS: un'attività che può raggiungerlo può in genere anche recuperare le credenziali temporanee del proprio ruolo di attività dallo stesso servizio link-local. Un controllo di raggiungibilità all'interno di uno script di installazione è il passaggio che precede il recupero di tali credenziali, quindi qualsiasi runner che abbia eseguito l'hook e sia riuscito a raggiungere tale endpoint dovrebbe essere considerato come se il proprio ruolo di attività fosse stato esposto. Su un host di sviluppo o di compilazione Windows, il payload richiede privilegi elevati e apre un canale di comando, che estende l'esposizione da un singolo profilo e uscita al controllo remoto interattivo.
Non siamo stati in grado di ottenere dati affidabili sul numero di download delle versioni dannose prima della loro rimozione, pertanto non stimiamo il numero delle vittime. La rapida ripubblicazione di due dei pacchetti — undici e nove versioni in poche ore — ha mantenuto aggiornati i file a disposizione degli installer e dei resolver mentre le versioni precedenti venivano rimosse.
Modelli emergenti
Questo cluster è un esempio di come un'etichetta venga usata come copertura. "Ricerca autorizzata", "bug bounty" e "test in sandbox" vengono sempre più spesso associati a pacchetti i cui script di installazione fanno molto più che profilare un host. Questa strategia sfrutta l'esitazione del revisore ad agire contro qualcosa che si presenta come autorizzato. Si combina con una seconda tattica ben nota: nomi di pacchetti progettati per sembrare strumenti interni di integrazione continua (CI), in modo da sfuggire a una rapida occhiata all'albero delle dipendenze. È la combinazione di questi elementi a rendere questo schema degno di attenzione: un nome che suona come un'infrastruttura di base, avvolto in una prosa che suona come un test autorizzato, che precede un hook di installazione che si spinge oltre i limiti su un sistema operativo e sonda le credenziali cloud sull'altro. Né la clausola di esclusione di responsabilità né il nome anonimo cambiano ciò che il codice fa durante l'installazione, e un processo di triage che consideri uno dei due come prova attenuante giungerà a un verdetto errato.
Cosa dovrebbero fare i difensori
- Cerca nei file di blocco e nei log di installazione i sei nomi dei pacchetti; considera ogni corrispondenza come un incidente, non come un avviso.
- Blocca e avvisa sul traffico in uscita 173.255.233[.]239 e *.trycloudflare[.]com host dalla creazione dell'infrastruttura.
- Ricerca di richieste HTTP in fase di installazione verso indirizzi di metadati dell'istanza (169.254.170.2, 169.254.169.254) provenienti dai gestori di pacchetti in CI.
- Ruotare le credenziali e i token dei ruoli cloud che erano accessibili da un runner che ha installato una versione interessata.
- Audit preinstallare/post-installazione script nel tuo set di dipendenzaUn hook del ciclo di vita che esegue un secondo file di script merita di essere letto.
- Sugli endpoint Windows, cerca nuovi DelegateExecute valori scritti sotto il ms-impostazioni Chiave shell-handler: la primitiva di elevazione utilizzata da questo payload e un segnale affidabile indipendente dal nome del pacchetto.
- Blocca e rivedi i file di blocco e disabilita gli script di installazione (npm install –ignore-scripts) in CI dove la tua build non li richiede.
- Considera le dichiarazioni di non responsabilità "ricerca autorizzata" o "innocue" all'interno di uno script di installazione come testo non attendibile, non come motivo per consentire l'esecuzione dello script.
Un pacchetto che annuncia buone intenzioni in prosa e richieste SISTEMA nel codice dovrebbe essere giudicato sul codice.




