Back to Blog

jscrambler npm package publishes malicious preinstall binary

On July 11, 2026, version 8.14.0 of jscrambler was published to npm carrying a malicious preinstall hook that drops and executes a platform-specific native binary on Linux, Windows, and macOS. jscrambler is the official CLI client for the Jscrambler Code Integrity API, a commercial JavaScript obfuscation and web-app protection service, with a clean version history dating back to 0.1.0. The compromised release was flagged by StepSecurity's AI Release Analyzer with a suspicion score of 0 (the maximum suspicion rating) on publish.
Rohan Prabhu
View LinkedIn

July 11, 2026

Share on X
Share on X
Share on LinkedIn
Share on Facebook
Follow our RSS feed
Table of Contents

On July 11, 2026, version 8.14.0 of jscrambler was published to npm carrying a malicious preinstall hook that drops and executes a platform-specific native binary on Linux, Windows, and macOS. jscrambler is the official CLI client for the Jscrambler Code Integrity API, a commercial JavaScript obfuscation and web-app protection service, with a clean version history dating back to 0.1.0. The compromised release was detected by StepSecurity's OSS AI Package Analyst, which flagged it with a suspicion score of 0 (the maximum suspicion rating) on publish.

We independently downloaded and statically analyzed the 8.14.0 tarball and compared it against 8.13.0, the last clean release published June 30, 2026. The package grows from 37.8 kB to 7.9 MB. A new preinstall script reads a 7.8 MB file disguised as dist/intro.js, which is not JavaScript. It is a custom binary container holding three gzip-compressed native executables, one per platform. On install, the loader decompresses the entry matching the host OS to a randomly named file in the system temp directory and launches it fully detached from the npm install process.

If you installed jscrambler 8.14.0: Treat the host as compromised. Downgrade to 8.13.0, rotate every credential that was logged into a browser on that machine, and audit any crypto wallet browser extensions for unauthorized activity.

Key finding: The payload is not embedded JavaScript. It's a Rust-compiled native binary (ELF on Linux, PE32+ on Windows, Mach-O arm64 on macOS) smuggled inside a file with a .js extension behind a custom 5-byte magic header, alongside legitimately named build artifacts like mutations.js and queries.js. All attacker logic lives in the compiled binary, which resists static string analysis for its command-and-control infrastructure.

Background: What Is jscrambler?

jscrambler is the official npm CLI for Jscrambler's Code Integrity API, used to obfuscate and protect production JavaScript bundles. It has around ten active maintainers and a version history stretching back to 0.1.0, making it an established package with real download volume and a common presence in CI/CD build pipelines, making it a high-value target for supply chain compromise.

Harden-Runner Analysis

Harden-Runner network monitoring captured the dropped binary making outbound calls to archive.torproject.org and check.torproject.org, along with direct connections to two IP addresses, 37.27.122.124 and 57.128.246.79. All four endpoints have been added to StepSecurity's global block list, and connection attempts to them are now blocked for customers running Harden-Runner

The Attack Vector

Version 8.14.0's package.json adds a script not present in any prior version:

dist/setup.js reads dist/intro.js, a new 7.8 MB file with no legitimate function in a JS-obfuscation CLI, and checks it for a custom 5-byte magic header (1B 43 53 49 01). The file is a container format: a platform-id byte, an 8-byte size field, an 8-byte little-endian compressed size, and gzip-compressed data, repeated once per supported platform. The loader selects the entry matching process.platform, decompresses it to a randomly named dotfile in the OS temp directory (with a .exe suffix on Windows), sets the executable bit, and spawns it detached and unref'd, orphaning the process from the npm install tree so it keeps running after npm install exits.

Hiding a compiled binary inside a file with a JavaScript extension, alongside genuinely JS-named build artifacts, minimizes the amount of suspicious-looking source an automated scanner or manual reviewer has to read. The loader script itself is short and unobfuscated; it does not need to hide anything because the actual payload logic never appears as text.

Payload Capabilities

Static analysis of the binaries surfaced a clear capability signature: an embedded SQLite engine (FTS3/FTS4 tokenizer strings), the storage format Chrome and Firefox use for credential stores (Login Data, Cookies, Web Data, key4.db); an embedded LevelDB engine, the format Chromium uses for Local Storage and IndexedDB, where browser-extension wallets like MetaMask persist their encrypted vault; and an embedded BIP39 English wordlist, used to parse and validate crypto wallet seed phrases. Together, this points to a cross-platform credential and crypto-wallet stealer, not a simple downloader.

Technical analysis of the full native payload, including disassembly and recovery of the C2 endpoint, is ongoing and this post will be updated as additional details become available.

Advanced Capabilities: Kernel Instrumentation and Anti-Analysis

Import table analysis of each platform's binary surfaced capability well beyond a typical npm supply chain stealer.

The Linux binary dynamically links against libbpf.so.1 and imports bpf_object__open_mem, bpf_object__load, bpf_program__attach, bpf_map__fd, and related libbpf functions. bpf_object__open_mem loads a compiled eBPF program from an in-memory buffer rather than from a file on disk, meaning the eBPF bytecode is embedded inside the binary itself and never touches the filesystem before being loaded into the kernel. This gives the payload kernel-level instrumentation capability, not just userspace file access. The binary also imports the generic syscall function rather than named wrappers for sensitive operations like ptrace or memfd_create, which keeps those calls out of the dynamic symbol table and out of simple import-based detection. Recovering what the embedded eBPF program actually does requires extracting and disassembling the BPF bytecode itself, which is part of the ongoing Stage 2 work.

The Windows binary imports IsDebuggerPresent, a standard anti-debugging check, and GetExtendedTcpTable from iphlpapi.dll, which enumerates active TCP connections along with their owning process IDs. This is commonly used by stealers either to detect security tooling and analysis environments by their network footprint, or to identify and close a running browser process before copying its credential store. The macOS binary imports sysctl and sysctlbyname, the standard mechanism for checking the P_TRACED flag to detect an attached debugger on that platform.

Indicators of Compromise

Type Value Significance
Malicious package jscrambler@8.14.0 Presence in package-lock.json/node_modules confirms the compromised version was installed
Suspicious file node_modules/jscrambler/dist/intro.js (7.8 MB) Custom binary container disguised as JS; magic bytes 1B 43 53 49 01
Dropped file (transient) <tmpdir>/.[a-z0-9]{6,}(.exe) Randomly named dotfile in OS temp dir written by the preinstall loader
Payload SHA256 (Linux) fbbcf4d8f98168f78f5c0c47a9ae56d59ec8ac84a7c9ca6b797fedfb8d62d2bd Extracted ELF x86-64 payload from dist/intro.js
Payload SHA256 (Windows) b7ca95d1b23c8e67416a25cedf741de0917c2096bbc9d24649eea7853d054903 Extracted PE32+ payload from dist/intro.js
Payload SHA256 (macOS) c8fd47d36bdf7c825378593ab82ed8c24d1dc52e26b507812393e24e1d5201fd Extracted Mach-O arm64 payload from dist/intro.js
Outbound call (domain) check.torproject.org Observed via Harden-Runner network monitoring; now blocked on StepSecurity's global block list
Outbound call (domain) archive.torproject.org Observed via Harden-Runner network monitoring; now blocked on StepSecurity's global block list
Outbound call (IP) 37.27.122.124 Observed via Harden-Runner network monitoring; now blocked on StepSecurity's global block list
Outbound call (IP) 57.128.246.79 Observed via Harden-Runner network monitoring; now blocked on StepSecurity's global block list

Remediation

Immediate actions if jscrambler 8.14.0 was installed:

  1. Downgrade: npm install jscrambler@8.13.0
  2. Treat the host as compromised. The dropped binary reads browser credential stores (Chrome/Firefox Login Data, Cookies, Web Data) and Chromium Local Storage/IndexedDB, where extension wallets such as MetaMask persist encrypted vault data.
  3. Rotate all credentials that were logged into a browser on the affected machine: cloud provider consoles, npm/GitHub tokens, SSO sessions, and any crypto wallet browser extensions.
  4. Move crypto assets from any wallet whose extension storage may have been accessible to a new wallet with a freshly generated seed phrase, on a clean device.
  5. Hunt for the dropped binary in OS temp directories (/tmp, %TEMP%, $TMPDIR) matching a random dotfile name, and for any still-running orphaned process spawned around the install time.

CI/CD pipelines: If jscrambler was installed in any build or release workflow, assume every secret exposed to that job, including npm tokens, cloud credentials, and signing keys, was reachable by the dropped process. Rotate all injected secrets for affected pipelines.

Attack Flow Summary

2026-06-30: jscrambler 8.13.0 published. Last known clean release: 37.8 kB tarball, no preinstall payload.

2026-07-11: jscrambler 8.14.0 published. Tarball grows to 7.9 MB; a preinstall hook pointing at dist/setup.js is added.

T+0: npm install jscrambler@8.14.0. npm runs preinstall automatically; setup.js parses dist/intro.js, extracts and decompresses the platform-matched binary to the OS temp directory.

T+seconds: the binary is spawned detached and unref'd, surviving after npm install completes, positioned to read local browser credential and wallet storage.

Same day: 8.14.0 is scored 0 (maximum suspicion) by StepSecurity's OSS AI Package Analyst, triggering this investigation.

This post will be updated as investigation continues, including full disassembly of the native payload, recovery of any additional C2 infrastructure, and further indicators of compromise.

For StepSecurity Customers

Threat Center Alert

StepSecurity has published a threat intel alert in the Threat Center with all relevant links to check if your organization is affected. The alert includes the full attack summary, technical analysis of the Phantom Gyp technique, IOCs, all affected packages and malicious versions, and remediation steps, so teams have everything needed to triage and respond immediately. Threat Center alerts are delivered directly into existing SIEM workflows for real-time visibility.

Harden Runner

Harden-Runner monitors every outbound network connection and process execution during a GitHub Actions workflow. A preinstall hook spawning a detached native process with no expected parent-child relationship to the npm install step is an anomalous pattern Harden-Runner surfaces, and an outbound connection from that process to an unlisted destination would be blocked under an allowed-endpoints policy, even before the destination is known to be malicious.

Secure Registry

StepSecurity Secure Registry provides each enterprise customer with a dedicated, policy-enforced npm registry that sits between your existing package manager (such as JFrog Artifactory) and the public npm registry. Instead of fetching packages directly from registry.npmjs.org, your infrastructure routes requests through your StepSecurity registry, which applies configurable security policies before serving any package.

The primary defense here is the cooldown period. Newly published package versions are held for a configurable window before being served to any developer machine or CI/CD pipeline. When the compromised Miasma packages were published to npm, including @vapi-ai/server-sdk, ai-sdk-ollama, and dozens of packages in the jagreehal ecosystem, Secure Registry customers were never exposed.

Detect Compromised Developer Machines

StepSecurity Dev Machine Guard gives security teams real-time visibility into npm packages installed across every enrolled developer device. When a malicious package is identified, teams can immediately search by package name and version to discover all impacted machines.

npm Package Cooldown Check

Newly published npm packages are temporarily blocked during a configurable cooldown window. When a PR introduces or updates to a recently published version, the check automatically fails. Since most malicious packages are identified within hours, this creates a crucial safety buffer. In this case, 57 packages across 286+ malicious versions were published in a rolling campaign lasting under two hours on June 3, so any PR updating to an affected version during the cooldown period would have been blocked automatically.

npm Package Compromised Updates Check

StepSecurity maintains a real-time database of known malicious and high-risk npm packages, updated continuously, often before official CVEs are filed. If a PR attempts to introduce a compromised package, the check fails and the merge is blocked. All compromised versions from this Miasma campaign, including @vapi-ai/server-sdk, ai-sdk-ollama, and the full jagreehal package family, were added to this database within minutes of detection.

npm Package Search

Search across all PRs in all repositories across your organization to find where a specific package was introduced. When a compromised package is discovered, instantly understand the blast radius: which repos, which PRs, and which teams are affected. This works across pull requests, default branches, and dev machines.

Explore Related Posts