Skip to content

Back to the catalog

jscrambler npm package publishes malicious preinstall binary
stepsecurity.iowriteup

jscrambler npm package publishes malicious preinstall binary

StepSecurity documents a supply-chain compromise of jscrambler, the official npm CLI for the Jscrambler Code Integrity API, starting with version 8.14.0. A malicious preinstall hook (later inlined into the main module to evade install-script scanners) drops a Rust-compiled, platform-specific native binary (ELF/PE32+/Mach-O) smuggled inside a fake dist/intro.js behind a custom magic header, containing an embedded SQLite/LevelDB parser and BIP39 wordlist consistent with a credential and crypto-wallet stealer. The Linux payload dynamically links libbpf and loads an in-memory eBPF program for kernel-level instrumentation, and all three platform binaries include anti-debugging checks; affected versions (8.14.0, 8.16.0, 8.17.0, 8.18.0, 8.20.0) were interleaved with clean releases, and C2 traffic was observed reaching Tor entry-node infrastructure and two raw IPs.

TL;DR A compromised jscrambler npm package (8.14.0+) drops a Rust-compiled, eBPF-capable native stealer targeting browser credentials and crypto wallets, hidden inside a fake .js file.
Document

What it is

jscrambler, the official npm CLI for Jscrambler's Code Integrity API (JS obfuscation/web-app protection service, ~10 maintainers, history back to 0.1.0), was compromised starting with version 8.14.0 (published July 11, 2026). Affected releases: 8.14.0, 8.16.0, 8.17.0, 8.18.0, 8.20.0. The last known clean release before the compromise was 8.13.0 (June 30, 2026); 8.22.0 is confirmed clean. The payload is a Rust-compiled native binary — a cross-platform browser-credential and crypto-wallet stealer, not a downloader.

How it works

A 7.8 MB file disguised as dist/intro.js is not JavaScript — it is a custom container behind a 5-byte magic header (1B 43 53 49 01) holding three gzip-compressed native executables (ELF/PE32+/Mach-O arm64), one per platform. A short, unobfuscated loader selects the entry matching process.platform, decompresses it to a randomly named dotfile in the temp dir, sets the exec bit, and spawns it detached and unref'd so it survives after the parent exits. All attacker logic lives in the compiled binary, which resists static string analysis.

The critical detail: the trigger changed across two generations, but the container is byte-for-byte identical throughout.

flowchart TD
  A[dist/intro.js container<br/>identical all releases] --> G1
  A --> G2
  G1[Gen 1: 8.14.0/8.16.0/8.17.0<br/>preinstall hook] -->|npm install| X[loader spawns binary]
  G2[Gen 2: 8.18.0/8.20.0<br/>inlined IIFE in dist/index.js] -->|any require jscrambler| X
  X --> Y[credential + wallet stealer]

Gen 2 removes the preinstall script from package.json entirely and inlines the same loader as an IIFE at the top of dist/index.js, so it fires on any require("jscrambler") — a build tool, CI helper, or app using the API programmatically — with no npm install needed. Scanners that only check for install hooks flag these two releases clean.

Static analysis of the binary shows embedded SQLite (FTS3/4 strings, targeting Chrome/Firefox Login Data, Cookies, Web Data, key4.db), LevelDB (Chromium Local Storage / IndexedDB where MetaMask-style wallets store encrypted vaults), and a BIP39 English wordlist (seed-phrase parsing).

Preconditions / Affected

Compromised and clean releases are interleaved, not a contiguous bad range — verify each version individually. A fast tell: unpacked tarball jumps from ~37.8 kB to ~7.9 MB. Gen 2 does not require an install step to fire, only an import.

What to do

If you installed 8.14.0, 8.16.0, 8.17.0, 8.18.0, or 8.20.0: treat the host as compromised. Pin to 8.22.0 or another verified clean version, rotate every credential logged into a browser on that machine, and audit crypto wallet browser extensions for unauthorized activity.

Block/hunt these IOCs (added to StepSecurity's Harden-Runner block list):

archive.torproject.org
check.torproject.org
37.27.122.124
57.128.246.79

Caveats

Full disassembly and recovery of the binary's C2 endpoint were still ongoing at time of writing; the observed network endpoints above come from Harden-Runner runtime monitoring, not from recovered strings.

Organisations named here

  • OSS AI Package Analyst

Technologies named here

  • StepSecurity OSS AI Package Analyst
  • jscrambler
  • Linux
  • Harden-Runner
  • Windows
  • StepSecurity Secure Registry
  • StepSecurity Dev Machine Guard
  • macOS
  • npm Package Cooldown Check
  • @vapi-ai/server-sdk
  • NPM Package Compromised Updates Check
  • ai-sdk-ollama
  • jagreehal package family
  • npm Package Search
  • StepSecurity Threat Center
  • npm
  • Rust
  • eBPF
  • SQLite
  • StepSecurity
  • Tor
  • MetaMask

Techniques named here

  • Malicious preinstall hook
  • Binary hidden inside fake .js file
  • Detached/unref'd process spawning
  • Inlining payload into main module
  • In-memory eBPF loading
  • Generic syscall use to evade import-table detection
  • Anti-debugging checks
  • TCP connection/process enumeration
  • Browser credential store and crypto wallet theft
  • Phantom Gyp

Threat actors named here

  • Miasma Campaign
  • Phantom Gyp technique
  • Miasma