Cybersecurity-themed server room showing software deployment, threat monitoring, communication tools, and blockchain networks.
The malicious npm package indexed-btree has been removed from the public registry, but the incident is a warning that npm’s new install-script controls do not stop a dependency from executing hostile code once an application begins using it. Checkmarx reported on September 17 that the package imitated the established sorted-btree library and placed its loader inside BTree.prototype.set()—ordinary-looking application code rather than preinstall, install, or postinstall hooks.

For Windows developers and the administrators who manage their build infrastructure, the immediate implication is straightforward: a clean npm install is no longer meaningful evidence that a package is safe. Any workstation, self-hosted CI runner, build agent, or server that installed and used an affected package should be treated as a potential compromise until its dependency tree, credentials, and outbound activity have been reviewed.

Checkmarx identified nine related packages alongside indexed-btree: ordered-kv-index, btree-leaderboard, priority-slot-queue, btree-range-store, btree-core, btree-time-index, btree-lru-cache, neighbor-key-map, and sliding-score-window. The researcher said those related packages had subsequently been removed. Socket’s package record independently shows that npm replaced indexed-btree with a security holding package, rather than leaving the malicious listing available for fresh installation.

Runtime execution is outside npm v12’s main defense​

GitHub’s npm team announced in June that npm v12 would disable dependency lifecycle scripts by default unless a project explicitly approves them. The change covers the familiar preinstall, install, and postinstall execution paths, and npm also restricted automatic retrieval of Git and remote-URL dependencies. Those are worthwhile controls: they remove a common route by which a package can execute code before developers have even run their project.

But indexed-btree did not need that route.

According to Checkmarx’s analysis, the package contained no lifecycle hook in its package.json. Instead, a modified implementation of BTree.prototype.set() checked for a specific key value—100—and, when its loader file was present, spawned a detached Node.js process with windowsHide enabled. The process then ran sharedLoad.min.js, an obfuscated first-stage loader placed within the package itself.

That choice changes the security boundary. npm’s script-approval system governs code that npm starts during dependency installation. It does not—and cannot, without fundamentally changing how JavaScript packages work—prevent a library from calling Node.js APIs such as child_process, fs, or crypto during normal execution. A package that offers a data-structure method is expected to run code when an application calls that method. The malicious behavior was placed at precisely that expected point.

The practical failure is not that npm v12 “did nothing.” It prevented one category of automatic execution. The campaign instead demonstrates that teams should not translate “no install scripts” into “no executable risk.” On a Windows build host, an application process running under a developer’s account or a CI service identity can still read environment variables, enumerate accessible files, contact external services, and launch child processes—even when its dependency installation generated no script approval prompt.

The loader’s design raises the incident beyond a typo package​

Checkmarx says the first-stage code fingerprints the host, collecting information including system architecture, hostname, CPU details, memory, and uptime. It then sends information to hardcoded Slack and Telegram destinations. That telemetry by itself may not be proof of full credential theft, but it provides operators with an inventory of potentially useful systems and confirms that a target has executed the package.

The more serious component is the reported command-and-control design. Rather than depending only on a conventional domain or IP address, the malware polls an Ethereum smart contract on the Sepolia test network. Checkmarx found that it uses an X25519 key exchange to derive an AES key, then decrypts a second-stage payload from data stored in the contract.

This makes simple domain blocking less effective. Blocking a known hostname can interrupt a conventional command server; a public blockchain endpoint and a smart contract create a different detection problem. Defenders need visibility into unusual outbound traffic from Node.js processes, including RPC requests to blockchain infrastructure, rather than relying entirely on DNS or URL reputation controls.

Checkmarx also found routines intended to delete malware files and remove the malicious trigger from the package source. That capability deserves careful interpretation. It does not prove the actors successfully erased evidence from every victim, and it does not establish what the eventual second stage does on each host. It does mean that teams should preserve forensic evidence before beginning cleanup: lockfiles, package-manager logs, CI logs, endpoint telemetry, and copies of affected node_modules trees can answer questions that a reinstallation cannot.

The claim that the campaign earned 109 ETH needs more restraint than some early reporting gave it. Checkmarx reported a wallet balance of 109 ETH, but did not establish that the funds came from this operation or from cryptocurrency theft. A wallet balance is an indicator worth tracking, not attribution of profit or proof of a particular victim impact.


Download counts are exposure signals, not victim counts​

Checkmarx put indexed-btree at nearly 2 million weekly downloads and listed substantial download totals for the other suspected packages, including nearly 2 million for btree-core. Those figures demonstrate that the operators got their names in front of dependency resolvers at scale. They do not mean two million separate organizations ran the malware.

npm download data can include repeated CI installs, package-manager retries, development environments, mirrors, and automated systems. A package can also be downloaded without being imported, while this malware’s trigger required the application to call the altered B-tree method with its expected condition. Conversely, a smaller number of downloads can carry high risk if they occur in build systems with cloud credentials, source-control tokens, npm tokens, code-signing material, or production deployment permissions.

Socket currently reports roughly 1.5 million weekly downloads for indexed-btree and identifies versions 2.1.1 through 2.1.3 as affected, with a 0.0.1-security holding package in place. The difference from Checkmarx’s approximately 2 million figure is not necessarily a contradiction: the measurements were captured at different points in a fast-moving incident, after the package’s removal process had begun. It does underline why administrators should check their own lockfiles and artifact repositories instead of trying to infer their exposure from a public download number.

The project’s outward presentation was part of the attack. Checkmarx described a legitimate-looking GitHub repository, fabricated commit history, and a curated developer profile. Its researchers also noted that the public repository did not include the malicious code found in the npm package. That is a meaningful discrepancy: source-repository review is valuable, but it is insufficient if the published tarball cannot be matched to a reproducible build from the reviewed source.

What Windows and CI teams should do now​

Organizations should first search source repositories, dependency inventories, build caches, and lockfiles for the ten package names—not merely run npm audit. npm’s own documentation describes npm audit as a check against the registry’s advisory data. It is useful once an advisory has been published, but it cannot be assumed to detect a newly identified malicious package immediately or to tell an investigator whether hostile code already ran.

For a confirmed hit, remove the dependency and rebuild from a known-good lockfile after validating that the package is absent from both direct and transitive dependency paths. Do not reuse a node_modules directory or an internal package cache that may preserve the affected archive. In Azure DevOps, GitHub Actions, Jenkins, TeamCity, or self-hosted runners, review cache keys and artifact retention policies before treating a fresh checkout as a clean build.

Credential rotation should be prioritized according to what the affected process could access:

  • Rotate npm, GitHub, GitLab, Azure DevOps, cloud-provider, package-registry, and deployment credentials available to the user or runner that executed the affected code.
  • Revoke and recreate tokens stored in environment variables, .npmrc files, Windows Credential Manager entries, CI secret stores, and deployment tooling.
  • Review source-control audit logs, npm publish history, cloud IAM events, and new CI workflow definitions for activity from the compromise window.
  • Hunt endpoint and network telemetry for node.exe spawning detached child processes, unexpected outbound Slack or Telegram traffic, and Node.js connections to Sepolia or other Ethereum RPC services.

Teams using Windows endpoints should also inspect scheduled tasks, startup locations, user-profile directories, and workspace configuration files during incident response. Checkmarx’s report specifically describes cleanup features in the malware, so the absence of the package directory after the fact should not be treated as proof that no execution occurred.

Package provenance now needs to include the published artifact​

npm has added stronger publisher-side controls this year, including staged publishing, publish-time malware scanning, and trusted publishing options. Those mechanisms can reduce the odds of a malicious upload becoming broadly available, but they cannot perfectly determine whether a JavaScript library’s ordinary runtime behavior is malicious—especially when that behavior is delayed, obfuscated, or gated behind application-specific inputs.

The more durable control for enterprise users is to narrow what unreviewed dependency code can do after installation. Run builds with short-lived credentials, avoid placing production secrets on developer workstations and general-purpose CI agents, segregate release signing from application builds, and capture egress telemetry for build infrastructure. A dependency that reaches runtime should not automatically inherit the keys to publish packages, modify repositories, or deploy production services.

indexed-btree is now a security holding package, but that only prevents the same registry name from being installed again. It does not rewrite existing lockfiles, purge package caches, or revoke a token that a previously infected runner may have exposed. The lasting lesson from this campaign is that npm’s install-time defenses are a useful gate, not a runtime sandbox.