# jscrambler npm package compromised to drop a Rust infostealer

> On 2026-07-11 15:12 UTC the official `jscrambler` npm package was hijacked via the `jscrambler_` maintainer account. Five malicious releases (`8.14.0`, `8.16.0`, `8.17.0`, `8.18.0`, `8.20.0`) shipped a Rust infostealer that harvests Chromium browser profiles, the Bitwarden vault, Steam sessions, AWS/Azure/GCP credentials, and MetaMask/Phantom/Exodus wallet seeds.

- Published: 2026-07-11
- Severity: critical
- Kind: Compromise - A legitimate, already-used package, maintainer account, CI pipeline, or release tag was hijacked and a malicious version was published under a name people already depend on.
- Ecosystems: npm
- Scope: 1 package, 5 compromised versions
- Tags: maintainer-takeover, account-takeover, infostealer, credential-theft, crypto-wallet-drain, obfuscation
- Also known as: Scavenger infostealer, jscrambler 8.14.0 compromise
- Detected by: Socket, SafeDep, StepSecurity, Snyk
- Incident ID: npm-2026-07-11-jscrambler-rust-infostealer

## Affected packages (1)

Only the exact versions listed were named by the source advisories. Other versions are not implicated.

| Package | Ecosystem | Compromised versions |
|---|---|---|
| [jscrambler](https://dependencywatch.io/package/npm/jscrambler) | npm | 8.14.0, 8.16.0, 8.17.0, 8.18.0, 8.20.0 |

## What happened

On 2026-07-11 at 15:12 UTC the official [`jscrambler`](https://www.npmjs.com/package/jscrambler) npm package - the JavaScript client for the Jscrambler code-integrity platform - was hijacked. Over the next ~3 hours the attacker pushed 5 malicious releases from the legitimate `jscrambler_` maintainer account, interleaved with clean releases the Jscrambler maintainers appear to have pushed as remediation. Socket, StepSecurity, SafeDep, and Snyk all published write-ups within 24 hours.

## Release timeline

| Version | Publish (UTC, 2026-07-11) | Dropper location | Registry state at write-up |
|---|---|---|---|
| **8.14.0** | 15:12 | `preinstall` → `dist/setup.js` → unpacks `dist/intro.js` | deprecated as compromised |
| 8.15.0 | 17:07 | clean (maintainer remediation) | published |
| **8.16.0** | 17:26 | `preinstall` → `dist/setup.js` | deprecated as compromised |
| **8.17.0** | 17:41 | `preinstall` → `dist/setup.js` | deprecated as compromised |
| **8.18.0** | (within 3h window) | **runtime** in `dist/index.js` + `dist/bin/jscrambler.js` - no preinstall | **still published, NOT deprecated** |
| **8.20.0** | (within 3h window) | **runtime** in `dist/index.js` + `dist/bin/jscrambler.js` | **still published, NOT deprecated** |
| 8.22.0 | (post-remediation) | clean | current `latest` |

Socket flagged `8.14.0` six minutes after publish; the take-down window before deprecation was measured in tens of minutes for the earliest versions but stretched significantly for the runtime-dropper builds.

## Payload - cross-platform Rust infostealer

Every malicious release ships the same native dropper. `dist/setup.js` (or the runtime-load equivalent) is a small loader; `dist/intro.js` - despite the extension - is a ~7.8MB container packing three gzip-compressed native binaries, one each for Linux, Windows, and macOS. The loader matches the target platform, extracts the appropriate binary, and detaches it as a background process.

The stealer itself is a Rust-compiled infostealer that has been informally attributed to the **Scavenger** family based on Rust artefact and target-list matching. It targets:

- **Browser profile data** - Chrome, Brave, Edge, and Chromium - including cookies, saved passwords, autofill, and session data
- **Bitwarden browser extension** - vault export via the extension's local storage
- **Steam** - session tokens and login data
- **Cloud credentials** - AWS access keys and STS tokens, Azure token cache, GCP application default credentials, plus the AWS/GCP/Azure metadata endpoints (169.254.169.254) so a compromised CI runner leaks its instance-role identity
- **Crypto wallets** - MetaMask, Phantom, and Exodus browser-extension / desktop wallet profiles (seed phrases and encrypted key stores)
- **Persistence** - Windows Task Scheduler entries on Windows and `~/Library/LaunchAgents/` plists on macOS, so the stealer re-runs after reboot

Exfiltration is TLS to a drop server. The C2 endpoint is not visible in plain strings - the binary decrypts its configuration at runtime using embedded AES / ChaCha20 primitives, which is why publicly-shared IOCs do not yet include a clean host list.

## 8.18.0 - the runtime dropper bypass

The most significant part of this compromise is the **8.18.0 pivot**. npm 12 shipped on 2026-07-08 (three days before the compromise) with dependency install scripts disabled by default - a long-anticipated hardening step that neuters `preinstall`/`postinstall` hooks unless the user explicitly approves them. On npm 12, `8.14.0`/`8.16.0`/`8.17.0` install cleanly without executing the payload.

At `8.18.0` the attacker moved the dropper out of the `preinstall` hook and into the package's own `dist/index.js` and `dist/bin/jscrambler.js`, so it runs when the module is required - i.e. every time a downstream build or CLI loads jscrambler at runtime. This defeats npm 12's new default, `--ignore-scripts`, and any hardening that only inspects install-time hooks. A CI job that pins `jscrambler@8.18.0` and passes `--ignore-scripts` is still fully compromised the moment `require('jscrambler')` executes.

This is the first known npm compromise where the attacker deliberately pivoted mid-campaign to defeat the npm 12 install-script default. Expect the pattern to become standard.

## Attribution

No commit, tag, or release for any of the malicious versions exists in the [jscrambler/jscrambler](https://github.com/jscrambler/jscrambler) GitHub repository - the malicious tarballs were built and uploaded straight to npm under the legitimate `jscrambler_` maintainer account. That points to a compromised npm account or build pipeline, not a source-tree tamper. The Jscrambler organisation has not published an official postmortem at time of write.

## Detection window

Socket detected `8.14.0` six minutes after publish and issued its advisory. StepSecurity, SafeDep, Snyk, and The Hacker News all had write-ups live within 24 hours. `8.14.0`, `8.16.0`, and `8.17.0` were deprecated by npm as compromised; `8.18.0` and `8.20.0` were still live on the public registry at time of write. Because those two runtime-dropper versions remain published, any semver-based resolver against a stale lockfile can still land on live malware today.

## Impact

- Any host that ran `npm install jscrambler@{8.14.0,8.16.0,8.17.0,8.18.0,8.20.0}` should be treated as fully compromised - the payload runs at install time (8.14.0/8.16.0/8.17.0 via `preinstall` hook) or at module-require time (8.18.0/8.20.0)
- **Credentials at risk**: AWS/Azure/GCP cloud credentials including the metadata endpoints CI runners use; Bitwarden browser-extension vault; browser-stored credentials from Chrome, Brave, Edge, and Chromium profiles; Steam session tokens
- **Wallet drain**: MetaMask, Phantom, and Exodus wallet extensions - seed phrases and private keys
- **Persistence**: Windows Task Scheduler entries and macOS LaunchAgents survive reboot; the Rust binary keeps beaconing after the first exfil
- **Attack vector**: the malicious versions were published under the legitimate `jscrambler_` maintainer account with no matching commit/tag/release in the [jscrambler/jscrambler](https://github.com/jscrambler/jscrambler) GitHub repository, pointing to a compromised npm account or CI publish pipeline - the source tree is not implicated
- **8.18.0 dropped the `preinstall` hook** and moved the dropper into `dist/index.js` and `dist/bin/jscrambler.js`, so it runs when your code loads the module (i.e. every time you run your build/CLI, not just `npm install`). This defeated npm 12's new install-script sandbox (npm 12 shipped 2026-07-08 with install scripts off by default) and bypassed any hardening based on `--ignore-scripts` / `preinstall` denylists
- **Registry state as of write-up**: `8.14.0`, `8.16.0`, and `8.17.0` were deprecated by npm as compromised; `8.18.0` and `8.20.0` remain published and NOT deprecated - a lockfile pin against those two still resolves malware today. `latest` is now `8.22.0` (clean)

## What to do

1. Grep every lockfile for `jscrambler` - pin only `>=8.22.0` (the clean release; latest at write-up) or `<=8.13.x` (last confirmed-clean before the compromise window)
2. If you installed any of `8.14.0`, `8.16.0`, `8.17.0`, `8.18.0`, `8.20.0` on a developer workstation: assume every browser-stored credential, Bitwarden vault, MetaMask/Phantom/Exodus seed, and Steam session token is captured. Rotate from a different machine and re-image the affected host
3. If you installed any of the compromised versions on a CI runner: assume AWS/Azure/GCP credentials reachable from the runner (including instance metadata) are captured. Rotate cloud creds, revoke long-lived IAM keys, and audit CloudTrail / activity logs for the exposure window
4. Remove persistence artifacts: check Windows Task Scheduler for unfamiliar tasks; check `~/Library/LaunchAgents/` on macOS for unexpected plists; check `~/.config/systemd/user/` on Linux
5. Do NOT rely on `--ignore-scripts` to have protected you - the `8.18.0` and `8.20.0` versions ship the dropper in `dist/index.js` and `dist/bin/jscrambler.js`, which run at module-load time, not install time. `--ignore-scripts` blocks the preinstall path for 8.14.0/8.16.0/8.17.0 but does nothing for the runtime droppers
6. Flush internal Artifactory / Nexus / Verdaccio caches - these mirrors will keep serving the malicious tarballs long after the public deprecation
7. Because `8.18.0` and `8.20.0` are STILL published as of the write-up, semver ranges like `^8.14.0` or `~8.18` on a stale lockfile will resolve to malware on the next `npm install`. Pin the exact `8.22.0` (or newer clean release) in your lockfile now
8. If you are the maintainer of a downstream package that depends on `jscrambler`: bump your minimum to `>=8.22.0` and publish an advisory bump so consumers stop resolving the compromised range

## Check your own dependencies

- Check a package-lock.json against this incident: https://dependencywatch.io/check/package-lock-json

The scan runs entirely in the browser; lockfile contents are never uploaded.

## References

- [Official jscrambler npm Package Compromised Across Multiple Releases](https://safedep.io/jscrambler-npm-supply-chain-compromise/) - SafeDep
- [jscrambler npm package publishes malicious preinstall binary](https://www.stepsecurity.io/blog/jscrambler-npm-package-publishes-malicious-preinstall-binary) - StepSecurity
- [Compromised jscrambler 8.14.0 npm Release Drops Rust Infostealer During Install](https://thehackernews.com/2026/07/compromised-jscrambler-8140-npm-release.html) - The Hacker News
- [jscrambler - Snyk vulnerability database](https://security.snyk.io/package/npm/jscrambler) - Snyk
- [jscrambler - npm registry](https://www.npmjs.com/package/jscrambler) - npm
- [jscrambler/jscrambler - GitHub repository (no matching malicious commit/tag)](https://github.com/jscrambler/jscrambler) - GitHub

---

Canonical page: https://dependencywatch.io/incident/npm-2026-07-11-jscrambler-rust-infostealer
Markdown version: https://dependencywatch.io/incident/npm-2026-07-11-jscrambler-rust-infostealer.md
Site index for agents: https://dependencywatch.io/llms.txt
DependencyWatch.io is operated by Precursor Security (https://precursorsecurity.com).
