Skip to main content
Emerging ThreatsSupply Chain Attacks

Malicious npm Packages Evade Detection by Hiding in Runtime Code

Laptop screen displays code in a coding environment amidst office workspace elements.

"Unlike the common attacks we've seen in the supply chain space, this package does not rely on preinstall / postinstall at all. Instead, it runs entirely from application code at runtime," Checkmarx said.

How indexed-btree disguised its malicious loader

A malicious npm package published as indexed-btree mimicked a legitimate B-tree utility (sorted-btree) but buried its loader inside ordinary-looking library code rather than in install-time hooks. The package first appeared on the npm registry on June 18, 2026, uploaded by a user named "charlessadler25" and — according to available statistics cited by researchers — accumulated millions of downloads in a short span before being removed.

Checkmarx reported that indexed-btree avoided the now-common lifecycle scripts (preinstall/postinstall) and instead placed the loader inside a BTree.prototype.set() method. That method triggers an embedded file, sharedLoad.min.js, which contains an obfuscated first-stage payload. The package and its associated GitHub repository are no longer available for download on npm.

From host fingerprinting to blockchain-delivered stages

The malicious chain analyzed by Checkmarx follows a multi-stage process. The first-stage payload fingerprints the host and sends those details to hard-coded endpoints: a Slack channel and a Telegram bot. It then uses an EtherHiding technique to retrieve encrypted blobs from a smart contract deployed on the Sepolia testnet. Those blobs are merged to produce the second-stage payload. As a final act the malware deletes malicious artifacts and removes the trigger from the package code to hinder forensic recovery.

Checkmarx also tied indexed-btree to a larger operation: they listed ten other npm packages associated with the same campaign that have since been removed from the registry, including ordered-kv-index, btree-leaderboard, priority-slot-queue, and btree-core among others.

Why attackers changed tactics: the role of npm v12 and lifecycle script restrictions

The shift away from lifecycle scripts appears to be a direct response to a defensive change in the npm ecosystem. Npm version 12 introduced a restriction preventing automatic execution of lifecycle scripts such as preinstall and postinstall. Checkmarx noted that while those scripts are legitimately used for compiling code or seeding data, they have long been abused to automatically execute malware without user consent. The indexed-btree campaign demonstrates attackers moving malicious execution into runtime code—functionality that looks legitimate at first glance—after install-time controls were strengthened.

Ensar Seker, CISO at SOCRadar, framed the matter succinctly: "Npm has improved install time security by restricting dependency lifecycle scripts, but this campaign demonstrates that attackers can simply move malicious execution into legitimate-looking runtime functionality instead." He added that blocking lifecycle scripts is an important improvement but does not eliminate the underlying threat; layered detection is needed before installation, during execution, and after deployment.

PolinRider resurfaces on Packagist with hybrid PHP–JavaScript execution

Related intelligence from Socket highlights a different but thematically similar campaign: PolinRider, described in the reporting as North Korea-linked. Socket said it deleted malicious code in the "dev-main" version of visanduma/nova-two-factor, a Packagist package with over 700,000 cumulative downloads, as part of this operation.

Socket's analysis emphasized that PolinRider operators often compromise developer accounts to inject malicious content into repositories and then rely on normal developer workflows (cloning, opening in an IDE) as triggers. For visanduma, the intrusion was introduced through the LaHiRu developer account and, according to Socket researcher Karlo Zanki, repositories showed compromises since mid-June 2026. One adaptation observed in this wave was the direct insertion of heavily obfuscated JavaScript into index.php and its execution via PHP's shell_exec(), creating a PHP-to-JavaScript infection path. Socket also pointed to takedown-resistant payload delivery techniques such as EtherHiding and its successor NullReceiver for staged payloads via the blockchain.

What this means for developers, security teams, and repository operators

  • Developers and maintainers: Do not rely solely on install-time protections or on the absence of lifecycle scripts as evidence of safety. Checkmarx and SOCRadar recommend runtime behavior analysis because this campaign embeds malicious execution inside otherwise plausible library code.
  • Security teams and CISOs: Expect attacker behaviour to change in response to controls. As Ensar Seker noted, blocking lifecycle scripts changes attacker behavior rather than eliminating the threat; defenders need layered controls to detect malicious activity before installation, during execution, and after deployment.
  • Repository operators (npm, Packagist): The incidents underline that package publication can be a distribution path for broader Git-based intrusions. Socket observed that PolinRider frequently begins with repository compromise and developer-account abuse, with package publication occurring later when a release is created.

The indexed-btree campaign and the resurfacing of PolinRider provide a stark reminder: when defenders close one execution path, threat actors look for another. In these incidents the new paths were ordinary-looking runtime methods and hybrid PHP–JavaScript triggers tied back to blockchain-delivered payloads — techniques that complicate simple install-time scanning and highlight the need for layered, execution-aware defenses.

Original reporting: The Hacker News