/00 — boot sequence

Hello.

Article

Jscrambler npm Supply Chain Compromise: Rust Infostealer in 5 Malicious Versions

July 19, 2026•7 min read
security supply-chain npm jscrambler infostealer rust

The official jscrambler npm package — a commercial JavaScript protection tool with ~60,000 monthly downloads — was compromised on July 11, 2026. Over a three-hour window, an attacker with access to the npm publishing credentials pushed five malicious versions (8.14.0, 8.16.0, 8.17.0, 8.18.0, 8.20.0), each containing a native Rust-compiled infostealer that targets browser credentials, Bitwarden vaults, and Steam sessions, and installs persistence via Windows Task Scheduler and macOS LaunchAgents.

What Happened

On July 11, 2026, between 15:12 and 18:12 UTC, the attacker published five trojanized versions of the jscrambler npm package. The compromise bypassed the project's normal GitHub release workflow — no commits, tags, or pull requests correspond to the malicious versions. The attacker appears to have gained access to the npm account or CI/CD pipeline of the legitimate maintainer (jscrambler_ account, [email protected]).

The malicious payload evolved across two distinct phases:

Phase 1 — Install-time dropper (8.14.0, 8.16.0, 8.17.0): A preinstall hook in package.json executed node dist/setup.js at install time. The dropper extracted a platform-specific, gzipped binary from a bundled container (dist/intro.js), wrote it to a hidden file in the system temp directory, and executed it detached.

Phase 2 — Runtime dropper (8.18.0, 8.20.0): The attacker moved the dropper out of the install hook and into the package's main entry points (dist/index.js and dist/bin/jscrambler.js) as an Immediately Invoked Function Expression (IIFE). This means the payload executes whenever any code requires the module or the CLI is invoked — completely bypassing npm install --ignore-scripts.

The maintainers responded by publishing clean versions (8.15.0 at 17:07, 8.22.0 at 18:12) but the attacker re-compromised the pipeline 19 minutes after the first remediation (8.16.0 at 17:26). Versions 8.18.0 and 8.20.0 remain published and not deprecated as of writing.

Vulnerability Details

Package: jscrambler (npm) Malicious Versions: 8.14.0, 8.16.0, 8.17.0 (install-time), 8.18.0, 8.20.0 (runtime) Clean Versions: 8.13.0 (last known clean), 8.15.0, 8.22.0 (current latest) Compromise Window: July 11, 2026, 15:12–18:12 UTC Monthly Downloads: ~60,000 Attack Vector: Compromised npm publishing credentials / CI pipeline

Payload Characteristics:

  • Language: Rust (compiled with rustc 1.98.0-nightly, June 4, 2026)
  • Architecture: Multi-platform (Linux ELF x86-64, Windows PE x86-64, macOS Mach-O arm64)
  • Delivery: Single bundled container (dist/intro.js, 7.8 MB, magic 0x1b43534901) with platform-matched binaries
  • Trigger: preinstall hook (Phase 1) or module require/CLI invocation (Phase 2)

Impact Assessment

Credential Theft Targets:

  • Chrome, Chromium, Brave, Edge browser profile data (cookies, login data, history)
  • Bitwarden browser extension (nngceckbapebfimnlniiiahkandclblb) — explicit targeting of password manager vault data
  • Steam session cookies (steamLoginSecure, sessionid)

Persistence Mechanisms:

  • Windows: Hidden Task Scheduler task (<Hidden>true</Hidden>, restart interval 1 minute, count 999)
  • macOS: LaunchAgent plist (RunAtLoad, KeepAlive, StartInterval 30 seconds)
  • Linux: Hidden dotfiles in os.tmpdir() (.{random})

Stealth Features:

  • Payload runs detached (detached: true, stdio: 'ignore', windowsHide: true)
  • No console output, no installer window
  • Binary container identical across all malicious versions (SHA256: a41a523ef9517aab37ed6eea0ec881821bdcb7aefcb5c5f603adc7907f868c86)
  • Phase 2 bypasses --ignore-scripts entirely — the dropper is in the module's own code path

Affected Systems

Any system where a developer or CI pipeline installed [email protected], 8.16.0, 8.17.0, 8.18.0, or 8.20.0 between July 11, 2026 15:12 UTC and the present (since 8.18.0 and 8.20.0 are still published and not deprecated).

High-Risk Scenarios:

  • CI/CD pipelines that install dependencies without version pinning
  • Developers who ran npm install jscrambler or npm update during the compromise window
  • Projects using jscrambler as a transitive dependency (though the payload only triggers on direct require/CLI invocation)

Other Jscrambler Packages: The ecosystem packages (jscrambler-webpack-plugin, gulp-jscrambler, jscrambler-metro-plugin, grunt-jscrambler) were not affected — their latest versions remain on the June 30 clean release.

Mitigation & Patching

Immediate Actions:

  1. Pin to a verified clean version: Use [email protected] (current latest) or 8.15.0 / 8.13.0. Verify by inspecting dist/index.js and dist/bin/jscrambler.js — they must be byte-identical to the clean versions.
  2. Audit all environments: Check package-lock.json, yarn.lock, pnpm-lock.yaml for any of the five malicious versions.
  3. Rotate compromised credentials: Assume browser-stored credentials, Bitwarden master passwords, and Steam sessions are compromised. Rotate all.
  4. Check persistence artifacts:
    • Windows: Task Scheduler → Task Scheduler Library → look for hidden tasks with 1-minute restart intervals
    • macOS: ~/Library/LaunchAgents/ → inspect unfamiliar plists with 30-second StartInterval
    • Linux: Check os.tmpdir() for hidden dotfiles (.{random})

Detection

Behavioral Indicators:

  • Unexpected git clone or package install commands initiated by AI coding assistants
  • Terminal executing commands that install persistent services (systemd, launchd, Task Scheduler)
  • Network connections to unknown endpoints following AI-assisted resource retrieval

IOCs:

File Hashes (SHA256):

  • dist/intro.js (binary container, all malicious versions): a41a523ef9517aab37ed6eea0ec881821bdcb7aefcb5c5f603adc7907f868c86
  • Linux ELF x86-64: fbbcf4d8f98168f78f5c0c47a9ae56d59ec8ac84a7c9ca6b797fedfb8d62d2bd
  • Windows PE x86-64: b7ca95d1b23c8e67416a25cedf741de0917c2096bbc9d24649eea7853d054903
  • macOS Mach-O arm64: c8fd47d36bdf7c825378593ab82ed8c24d1dc52e26b507812393e24e1d5201fd

Package Artifacts:

  • Install hook (8.14.0, 8.16.0, 8.17.0): "preinstall": "node dist/setup.js"
  • Runtime dropper (8.18.0, 8.20.0): IIFE at top of dist/index.js and dist/bin/jscrambler.js
  • Self-dependency (8.18.0, 8.20.0): "jscrambler": "^8.17.0" in own dependencies

Bitwarden Extension ID: nngceckbapebfimnlniiiahkandclblb

Build Fingerprints:

  • PDB GUID: 6976bd1e56f6b4214c4c44205044422e (Windows)
  • Linux Build ID: c05bf7dc45672d7a59cbd5e300f586482da14d00
  • Compiler: rustc 1.98.0-nightly (e7815e522 2026-06-04)

YARA Rule:

yara

Suricata Rule:

suricata

Frequently Asked Questions

Q: How did the attacker gain access to publish to npm? A: The malicious versions have no corresponding GitHub commits, tags, or PRs. The GitHub repo's latest tag is v8.13.0. This points to npm account or CI pipeline compromise rather than source repository compromise. The legitimate maintainers have 10+ maintainers with @jscrambler.com emails.

Q: Why did the attacker re-publish after the maintainers pushed a clean version? A: The attacker maintained access to the publishing credential/pipeline. At 17:07 UTC the maintainers published clean 8.15.0. At 17:26 UTC (19 minutes later) the attacker published 8.16.0 with identical payload, reclaiming latest. This tug-of-war continued for three hours.

Q: What's the difference between Phase 1 and Phase 2 payloads? A: Phase 1 (8.14.0, 8.16.0, 8.17.0) uses a preinstall hook — runs at npm install time. Phase 2 (8.18.0, 8.20.0) injects the dropper as an IIFE into dist/index.js and dist/bin/jscrambler.js — runs whenever require('jscrambler') is called or the CLI is invoked. Phase 2 bypasses --ignore-scripts.

Q: Are 8.18.0 and 8.20.0 still dangerous? A: Yes. They are still published on npm, not deprecated, and the runtime dropper means any require('jscrambler') triggers the payload. An unpinned install resolving to either version would be compromised.

Q: What should I do if I installed a malicious version? A: Treat the machine as compromised. Rotate all browser-stored credentials, Bitwarden master password, Steam session. Check Task Scheduler (Windows), LaunchAgents (macOS), tmpdir (Linux) for persistence. Pin to 8.22.0 or 8.15.0/8.13.0 after verifying code integrity.

Q: How can I detect this in my CI/CD? A: Pin dependencies to exact clean versions. Use a package analyzer like SafeDep's Package Manager Guard (pmg) that analyzes package payload rather than just install hooks — it would have caught Phase 2 where the dropper hides in module code.

Q: Was the Rust payload custom or off-the-shelf? A: Custom. The binary contains a bespoke C2 protocol (JSON messages with t:out/t:done), AWS SigV4 and Hashicorp Vault targeting, browser SQLite/LevelDB readers, ASAR parser for Electron apps, and a dropper code template for self-propagation. Compiler: rustc 1.98.0-nightly. Project name: "agent". OPSEC: zeroed PE timestamp/checksum, stripped panic locations, relative PDB path.

Key Takeaways

  • Supply chain attacks are evolving: This compromise featured a tug-of-war between attacker and maintainers over three hours, with the attacker escalating from install-time to runtime injection to bypass script-ignoring defenses.
  • --ignore-scripts is not a silver bullet: Phase 2 moves the dropper into the module's own code path, executing on require() — completely bypassing install-script protections.
  • Binary analysis is essential: The payload was a 7.8 MB Rust binary container with identical SHA256 across all five malicious versions. Source code review of package.json would miss Phase 2 entirely.
  • Credential rotation is non-negotiable: The infostealer targets browser profiles, Bitwarden, and Steam. The blast radius extends far beyond the npm package.
  • Pin dependencies, verify integrity: Pin to exact versions (8.22.0). Verify by checking dist/index.js byte-identity to known-clean releases.

Conclusion

The Jscrambler compromise demonstrates that supply chain attackers are adapting to defenses. The shift from install-time hooks to runtime module injection renders traditional preinstall/postinstall auditing insufficient. Defenders must analyze package payloads — not just metadata — and treat any unpinned dependency as a potential vector. For the ~60,000 monthly users of jscrambler, the window of exposure remains open until 8.18.0 and 8.20.0 are deprecated and all environments are audited.


Sources:

Automated Transmission

This entry was synthesized and populated dynamically using native API integrations.

Resources & Links