Jscrambler’s npm Package Got Backdoored. The Malware Ran Before Your App Ever Started.

The CyberSec Guru

Jscrambler's npm Package Got Backdoored

If you like this post, then please share it:

Buy me A Coffee!

Support The CyberSec Guru’s Mission

🔐 Fuel the cybersecurity crusade by buying me a coffee! Why your support matters: Zero paywalls: Keep the main content 100% free for learners worldwide.

“Your coffee keeps the servers running and the knowledge flowing in our fight against cybercrime.”☕ Support My Work

Buy Me a Coffee Button

On July 11, 2026, someone with a valid npm publishing credential for jscrambler pushed a new version of the package. It looked like a routine release. It was not. Version 8.14.0 carried an undocumented preinstall hook, a 7.8 MB binary container disguised as a JavaScript file, and a Rust-built infostealer compiled separately for Linux, Windows, and macOS. Anyone who ran npm install against a project depending on that version got the malware whether they ever imported the package or not.

Jscrambler sells JavaScript obfuscation and “code integrity” tooling. Its whole pitch is that it makes your code harder to tamper with. For a few hours on a Saturday, its own npm package was the tampering.

What is jscrambler

Jscrambler Logo
Jscrambler Logo

jscrambler is the npm client teams install to fold Jscrambler’s commercial obfuscation service into a JavaScript or TypeScript build. Developers add it as a dev dependency or call it from CI so production bundles get run through Jscrambler’s protection before shipping. That means the package sits at build time, in exactly the environment that holds the things worth stealing: source code, environment variables, cloud credentials, deployment tokens.

It pulls around 15,800 downloads a week. That is a small number next to the npm mega-compromises of the last two years, several of which sit in the billions of weekly downloads. But raw reach was never the constraint here. A build-time tool doesn’t need scale. It needs to land on the right kind of machine, and every machine that runs it is, by definition, a developer’s or a CI runner’s.

Timeline: three hours, five malicious releases

Socket’s research team flagged 8.14.0 six minutes after it hit the registry. That is fast by any measure, and it still wasn’t fast enough to stop every install that landed in the window.

Over the following three hours, the same threat actor kept publishing. Socket eventually tied five malicious versions to the campaign: 8.14.0, 8.16.0, 8.17.0, 8.18.0, and 8.20.0. Version 8.15.0, published in between two of the malicious drops, turned out to be clean, which on its own tells you the attacker was iterating on delivery rather than working from a single prepared payload.

That iteration is the part is to be noted. The first three malicious versions ran the dropper from a preinstall script, the classic npm attack vector. Starting with 8.18.0, the install hook disappeared entirely. The identical dropper code got moved into a self-executing function at the top of dist/index.js and dist/bin/jscrambler.js instead, so it fires the moment the package is imported or the CLI runs rather than at install time. That single change defeats any scanner looking only at lifecycle scripts, and it survives npm install --ignore-scripts, which is the standard advice for exactly this class of attack. Versions 8.18.0 and 8.20.0 also declared a self-dependency on an earlier compromised release, which would have let the malware ride in transitively even for projects that pinned a supposedly safer version.

The timing has a bitter irony to it. npm 12 shipped on July 8, three days before this attack, and one of its headline changes was turning off automatic install scripts by default. On a project running npm 12, the preinstall-hook versions of this attack simply don’t fire without explicit approval. Older clients, and the huge installed base that hasn’t upgraded yet, ran them automatically. The attacker’s pivot to a runtime dropper in the later versions reads like a direct answer to that exact protection, whether or not it was intentional.

Jscrambler confirmed the cause in its own advisory: the attacker published using a compromised npm publishing credential, not a source-code breach. Researchers at StepSecurity and SafeDep both checked the GitHub repository and found no commit, tag, or pull request matching 8.14.0. The last real tag sits at 8.13.0. The company said the intrusion was contained to the jscrambler CLI package for its Code Integrity product, and that companion packages, the webpack, gulp, Metro, and grunt plugins, stayed clean on their June releases with no install hooks added. Jscrambler revoked and rotated its publishing credentials, hardened its release pipeline, and pointed to 8.22.0 as the confirmed clean version to move to.

One detail matters for anyone doing cleanup: deprecating a version on npm does not remove it. 8.14.0 is still installable by exact version number today. Any lockfile or CI script pinned to it will keep pulling the infostealer until someone actively removes the pin.

📬 Stay Ahead of Cyber Threats

Get the latest cybersecurity news, critical vulnerabilities, threat intelligence, tutorials, and exclusive giveaways delivered straight to your inbox. No spam. Unsubscribe anytime.

Subscribe to the Newsletter →

Inside the payload

Version 8.14.0 added exactly two new files under dist/: setup.js and intro.js. setup.js is a small loader wired up through the new preinstall entry in package.json. intro.js, despite the name and the .js extension, isn’t JavaScript at all. It’s a binary container starting with a custom five-byte header, and it packs three gzip-compressed native executables: a Linux x86-64 ELF, a Windows x86-64 PE32+, and an Apple Silicon macOS Mach-O.

On install, the loader reads the container, picks the blob matching the host’s process.platform, decompresses it into a randomly named hidden file in the system temp directory (with a .exe suffix added on Windows), marks it executable, and launches it detached with its output suppressed. Then it calls unref() so the spawned process doesn’t keep the Node install process alive. None of this shows up as a visible window or a logged command. It just runs.

The executables themselves are Rust binaries, and the string obfuscation inside them is unusually disciplined for malware this broad. Every sensitive configuration string, roughly 2,400 of them by Socket’s count, is individually encrypted with ChaCha20-Poly1305 and decrypted on demand into lazily initialized variables. Because Poly1305 is an authenticated cipher, a wrong key or a wrong ciphertext boundary fails the tag check outright, so the decryption process is self-validating. That’s a level of anti-analysis effort you don’t usually see paired with a credential stealer. It slowed researchers down without stopping them. Socket says it cross-validated the recovered strings against two independent AEAD implementations and got byte-identical plaintext with no tag mismatches.

What those strings point to is a target list built specifically for people who write software, not general consumers.

Cryptocurrency wallets. The malware targets MetaMask, Trust Wallet, Coinbase Wallet, and Phantom by their exact browser-extension IDs, plus the standalone Exodus wallet. It goes after the vault- and seed-extraction keys directly, things like mnemonic, seedPhrase, and recoveryPhrase, along with the scrypt key-derivation parameters needed to decrypt a wallet vault rather than just copy an encrypted blob.

AI coding tools. This is the part that should get any developer’s attention regardless of which stack they work in. The payload enumerates configuration files for Claude Desktop, Cursor, Windsurf, Zed, VS Code, Factory, and opencode, hunting specifically for API keys and Model Context Protocol server credentials. As more of the developer toolchain routes through AI agents with their own stored credentials, that config directory has become as valuable a target as a cloud credentials file, and the people building this malware clearly know it.

Cloud credentials. All three major providers are covered. On AWS it goes after the ECS task-metadata endpoint, Secrets Manager, and SSM Parameter Store. On GCP it targets the compute metadata service, GOOGLE_APPLICATION_CREDENTIALS, and Secret Manager. On Azure it hits the instance metadata service and the management API. These are exactly the paths a CI runner would have open by design, which is the whole reason to compromise a build-time tool instead of a runtime one.

Everything else developers have open. Discord (stable, PTB, and Canary builds), Slack, Telegram Desktop, Steam session tokens, Chromium-family and Firefox browser profiles read through their LevelDB and SQLite stores, and KDE’s KWallet on Linux. The malware also attempts local privilege escalation through sudo -S -p and systemd-run, and wires in persistence: a hidden scheduled task on Windows set to relaunch every minute, and a LaunchAgent on macOS that reloads at login.

The Linux build carries one more capability that separates it from a typical stealer. It links against the kernel’s BPF library and can load an eBPF program directly into the kernel from memory, a foothold below userspace rather than the file and process access the rest of the stealer relies on. Both StepSecurity and SafeDep flagged the capability. What the eBPF program does hadn’t been fully reverse-engineered as of the last public update, which is its own kind of unsettling.

Exfiltration runs over TLS using the Rust rustls library, with a literal multipart POST to a /upload endpoint on the drop server. StepSecurity’s runtime monitoring caught the dropped binary reaching two hard-coded IP addresses plus Tor bootstrap infrastructure, the first network indicators published for the campaign.

What to do about it

If your project touches jscrambler anywhere in its dependency tree, treat this as a “did it run” question, not a “did I mean to install it” question.

Check lockfiles, package-lock.json or yarn.lock, and CI logs for any resolution of jscrambler@8.14.0, 8.16.0, 8.17.0, 8.18.0, or 8.20.0, going back to July 11. Because the dropped binary lands under a randomly generated filename in the temp directory, there’s no fixed name to grep for. Line up install timestamps against Node child-process creation and temp-directory writes instead. On Windows, check Task Scheduler for a hidden task set to relaunch every minute. On macOS, check ~/Library/LaunchAgents for a plist you don’t recognize.

If any of those versions ran on a machine, treat everything that machine could reach as stolen, not just exposed. That means rotating cloud IAM keys across AWS, GCP, and Azure, npm and GitHub tokens, and API keys for any AI coding tool with a stored MCP credential. Revoke active Discord, Slack, and browser sessions from that host, and move any crypto held in a wallet that touched the machine. Block the published command-and-control IPs at the network layer. Then upgrade to 8.22.0, which both Jscrambler and Socket confirm is clean, and make sure nothing in your lockfile is still pinned to an earlier version out of habit.

This is not an isolated incident. It’s a monthly one now

Zoom out from Jscrambler specifically and a pattern shows up that’s hard to ignore if you track this space closely, and I do, for a living.

IronWorm hit npm in early June 2026. JFrog traced it to a compromised account called asteroiddao and found the malware, also a Rust binary, hiding behind its own eBPF rootkit and communicating over Tor. What made IronWorm distinct was its self-propagation: it used stolen npm credentials to push new trojanized versions of packages the victim already maintained, and it used stolen GitHub access to plant 57 backdated commits across nine organizations, timestamped to look years old. Ox Security put the initial infected-package count at 36, with a combined 32,000 or so monthly downloads, caught before it reached anything more widely used.

Shai-Hulud is the campaign that gave this whole category its name. The original worm, tracked by ReversingLabs and Unit 42, surfaced on npm in September 2025 and spread by injecting itself into every package a compromised maintainer controlled, then using that developer’s own publishing rights to keep spreading. A second wave, Shai-Hulud 2.0, hit npm again that November, reaching nearly 800 packages. PyPI’s own security team said at the time that the Python registry hadn’t been directly exploited, only that some exposed PyPI credentials turned up in compromised GitHub repos as a side effect. The registry-crossing part came later. In late April and through mid-May 2026, a group tracked as TeamPCP ran a campaign researchers dubbed Mini Shai-Hulud that hit both npm and PyPI in the same coordinated operation, over 170 packages including chunks of the TanStack and OpenSearch ecosystems. TeamPCP then published the worm’s full source code to GitHub as what it called a gift to the wider criminal community, with a cash bounty attached for whoever built the biggest attack from it. That’s arguably the more dangerous event than the initial compromise: turning a bespoke weapon into a public toolkit lowers the bar for the next imitator considerably.

onering, a Rust crate on crates.io with about 18,000 downloads, got compromised in its 1.4.1 release on June 10, 2026. Instead of a credential stealer, it shipped a build.rs script that quietly captured the diff of your most recent commit and shipped it to what looked like an ordinary Sentry crash-reporting endpoint, source-code exfiltration disguised as telemetry. Aikido’s writeup noted the attacker had left a commented-out line writing the payload to a local file, evidence the exfiltration logic was tested before the network call got wired in.

Mastra, an npm package ecosystem, had 144 packages compromised in mid-June 2026 through a malicious dependency that silently pulled and ran a payload at install time, per Aikido’s tracking.

Four separate registries. Four separate threat actors, as far as public attribution goes. All within about six weeks of each other, all targeting the same underlying weakness: package managers execute code from strangers by default, and almost nobody reviews what a routine version bump contains before it lands on a build machine.

Why “it’s from the security vendor” isn’t a review

There’s a specific failure mode buried in this incident that’s worth naming directly, because it’s not really about Jscrambler as a company. It’s about how trust gets allocated in a dependency tree.

Nobody manually reviews a diff on a jscrambler version bump. Why would they? It’s the obfuscation tool. Its entire commercial purpose is protecting code integrity, which creates a strange kind of blind spot: the more a vendor’s whole brand is “we secure your build,” the less scrutiny its own artifacts tend to get. That’s not a knock-on due diligence practice in general. It’s a structural fact about how supply chains distribute trust, and attackers who understand it will keep going after security vendors, CI tooling, and other high-trust, low-scrutiny links specifically, because the payoff per compromise is disproportionate to the popularity of the package.

The practical fix isn’t “review every dependency more carefully,” which nobody has the hours for. It’s treating install-time execution as a privileged action that needs the same controls as any other privileged action: default-off install scripts (which npm 12 now does), automated scanning that flags binary payloads and unexpected native code regardless of which lifecycle hook delivers them, and credential rotation practiced often enough that a compromised token has a short useful life even before anyone notices the breach.

Indicators of compromise

Malicious package versions: jscrambler@8.14.0, 8.16.0, 8.17.0, 8.18.0, 8.20.0

File hashes (SHA-256):

  • dist/setup.js: a742de963f14a92d24ebcbc7b44ac867e23a20d31d1b0094a13a4f83287f4e60
  • dist/intro.js: a41a523ef9517aab37ed6eea0ec881821bdcb7aefcb5c5f603adc7907f868c86
  • Linux payload (ELF): fbbcf4d8f98168f78f5c0c47a9ae56d59ec8ac84a7c9ca6b797fedfb8d62d2bd
  • Windows payload (PE32+): b7ca95d1b23c8e67416a25cedf741de0917c2096bbc9d24649eea7853d054903
  • macOS payload (Mach-O): c8fd47d36bdf7c825378593ab82ed8c24d1dc52e26b507812393e24e1d5201fd

Network indicators:

  • C2 IP: 37.27.122[.]124
  • C2 IP: 57.128.246[.]79
  • Tor bootstrap contact: check.torproject[.]org, archive.torproject[.]org

On-host artifacts: a hidden, randomly named file in the system temp directory (pattern .{random} or .{random}.exe on Windows); a hidden Windows scheduled task set to relaunch every minute; an unfamiliar macOS LaunchAgent that reloads at login.

Clean versions: 8.13.0 (pre-compromise), 8.15.0 (clean interim release), 8.22.0 (current, confirmed clean).

FAQs

Is jscrambler safe to use now?

Version 8.22.0 is confirmed clean by both Jscrambler and Socket’s research team. Versions 8.14.0, 8.16.0, 8.17.0, 8.18.0, and 8.20.0 all carry the infostealer and should be removed from any lockfile or cache.

Does npm install –ignore-scripts protect against this?

Only against the first three malicious versions, which relied on a preinstall hook. Versions 8.18.0 and 8.20.0 moved the dropper into the package’s main JavaScript files, so it runs on import or CLI execution regardless of the –ignore-scripts flag.

How was Jscrambler compromised?

The company’s security advisory attributes the breach to a compromised npm publishing credential, not a source-code or build-pipeline intrusion. No trace of the malicious versions exists in the project’s GitHub history.

Is this related to the IronWorm or Shai-Hulud npm attacks?

No confirmed link. IronWorm and Shai-Hulud/Mini Shai-Hulud are separate campaigns with different attribution. All three share a delivery pattern of install-time or import-time code execution through a compromised publishing account, but there is no public evidence tying them to the same threat actor.

Buy me A Coffee!

Support The CyberSec Guru’s Mission

🔐 Fuel the cybersecurity crusade by buying me a coffee! Your contribution powers free tutorials, hands-on labs, and security resources.

Why your support matters:
  • Writeup Access: Get complete writeup access within 12 hours
  • Zero paywalls: Keep the main content 100% free for learners worldwide

Perks for one-time supporters:
☕️ $5: Shoutout in Buy Me a Coffee
🛡️ $8: Fast-track Access to Live Webinars
💻 $10: Vote on future tutorial topics + exclusive AMA access

“Your coffee keeps the servers running and the knowledge flowing in our fight against cybercrime.”☕ Support My Work

Buy Me a Coffee Button

If you like this post, then please share it:

News

Discover more from The CyberSec Guru

Subscribe to get the latest posts sent to your email!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Discover more from The CyberSec Guru

Subscribe now to keep reading and get access to the full archive.

Continue reading