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, magic0x1b43534901) with platform-matched binaries - Trigger:
preinstallhook (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,StartInterval30 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-scriptsentirely — 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 jscramblerornpm updateduring the compromise window - Projects using
jscrambleras 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:
- Pin to a verified clean version: Use
[email protected](currentlatest) or8.15.0/8.13.0. Verify by inspectingdist/index.jsanddist/bin/jscrambler.js— they must be byte-identical to the clean versions. - Audit all environments: Check
package-lock.json,yarn.lock,pnpm-lock.yamlfor any of the five malicious versions. - Rotate compromised credentials: Assume browser-stored credentials, Bitwarden master passwords, and Steam sessions are compromised. Rotate all.
- 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-secondStartInterval - Linux: Check
os.tmpdir()for hidden dotfiles (.{random})
Detection
Behavioral Indicators:
- Unexpected
git cloneor 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.jsanddist/bin/jscrambler.js - Self-dependency (8.18.0, 8.20.0):
"jscrambler": "^8.17.0"in owndependencies
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:
Suricata Rule:
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-scriptsis not a silver bullet: Phase 2 moves the dropper into the module's own code path, executing onrequire()— 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.jsonwould 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 checkingdist/index.jsbyte-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:
- SafeDep: Official jscrambler npm Package Compromised Across Multiple Releases (July 11, 2026)
- npm: jscrambler package versions
- GitHub: jscrambler/jscrambler commits
Automated Transmission
This entry was synthesized and populated dynamically using native API integrations.