Back to Intelligence

Malicious 'indexed-btree' NPM Package Typosquats sorted-btree: Detection and Supply-Chain Remediation Guide

SA
Security Arsenal Team
September 23, 2026
13 min read

SecurityWeek reports that a malicious npm package named indexed-btree has accumulated millions of downloads by masquerading as the legitimate sorted-btree library. The package hides its payload behind a tampered prototype method — meaning the malicious code does not detonate on install or on import, but only when a specific method is invoked at runtime. That design choice matters enormously for defenders: this is a sleeper dependency engineered to survive static code review, casual sandboxing, and install-time scanners, and to blend into legitimate B-tree usage inside production Node.js workloads.

This is the latest in a long pattern of npm ecosystem abuse — typosquatting and name-confusion attacks against high-value open-source libraries — and it reinforces a hard truth: your production attack surface includes every transitive dependency pulled by every developer machine and CI runner in your organization. If a developer fat-fingered an install command, copied a code snippet from a poisoned Stack Overflow answer, or an AI coding assistant hallucinated this package name into a package.json, you may be running attacker-controlled code with the privileges of your Node.js process right now. Treat this as an active incident-response task, not a hygiene item.

Technical Analysis

What happened

  • Malicious package: indexed-btree on the npm registry
  • Impersonated package: sorted-btree, a legitimate and widely used B-tree implementation for JavaScript/TypeScript
  • Delivery mechanism: Name-confusion/typosquatting. The malicious name is close enough to the legitimate one to be installed via typo, misremembered package name, copy-paste from poisoned documentation, or dependency hallucination by AI coding assistants — a vector we are seeing with increasing frequency in 2025–2026.
  • Payload concealment: The malicious trigger is embedded inside a prototype method of the exported class rather than in a postinstall script or top-level module code. This is a deliberate evasion technique:
    • Many dependency scanners flag install scripts (preinstall/postinstall) and obvious top-level execution. A dormant method bypasses those heuristics.
    • The code only executes when the application calls the trojanized method — so the package can sit in node_modules inert during CI builds and security scanning, then activate in production.
    • Because the package otherwise behaves like a functional B-tree, runtime behavior appears normal until the trigger fires.

Why prototype-method triggers are dangerous

In JavaScript, mutating or hiding logic on a class prototype lets an attacker interleave malicious behavior with legitimate API calls. From a defender's perspective, the observable behaviors once the trigger fires typically include:

  1. Process execution — the Node.js process spawning child processes (cmd.exe, powershell.exe, /bin/sh, /bin/bash) via child_process.exec/spawn to run second-stage payloads.
  2. Network egress — the Node.js process making outbound HTTPS connections to non-registry, non-CDN infrastructure for data exfiltration or payload retrieval (often exfiltrating environment variables, .npmrc tokens, .env files, cloud credentials, or SSH keys).
  3. File access — reads of sensitive paths (~/.ssh, ~/.aws/credentials, .env, CI runner token stores) by a process whose legitimate job is sorting data structures.

A B-tree library has zero legitimate reason to spawn a shell, read your SSH keys, or open arbitrary outbound connections. That asymmetry is your strongest detection anchor.

Exploitation status

  • The package accumulated millions of downloads, meaning exposure is widespread and largely unintentional — the victims are downstream consumers who installed it by mistake.
  • Because the trigger is runtime-gated, confirmed active exploitation in the wild is difficult to quantify from telemetry alone. Assume compromise wherever the package is present, and hunt for post-trigger behavior (child processes, egress) rather than just package presence.
  • No CVE has been assigned for this package (malicious packages are typically removed from the registry rather than patched). Do not wait for a CVE to act — registry takedown does not remove copies already vendored, cached in artifact registries, or baked into container images.

Who is affected

  • Any Node.js project with indexed-btree in dependencies, devDependencies, or as a transitive dependency.
  • CI/CD runners and build agents where the package was installed (credential theft from build agents is the highest-impact scenario — these hold npm tokens, cloud deploy keys, and signing certificates).
  • Published artifacts: container images, serverless bundles, and internal npm packages that bundled the library.

Detection & Response

Step 1 — Find the package everywhere

Before hunting for runtime behavior, establish exposure. Search every lockfile, node_modules tree, private registry cache, and container image in your estate. The presence of indexed-btree anywhere is a finding requiring investigation.

Sigma Rules

The following rules target the highest-signal post-trigger behavior: Node.js spawning shells and making suspicious outbound connections. They are intentionally narrow to avoid drowning your queue in legitimate Node.js build tooling noise — tune the exclusions to your build fleet, not to your production workloads.

YAML
---
title: Node.js Process Spawning Shell or Script Interpreter
tid: 3f8a2b1c-7d4e-4f5a-9b6c-2e1d0a9f8b7c
status: experimental
description: Detects node.exe spawning cmd.exe, powershell.exe, or other script interpreters, consistent with a malicious npm package trigger executing via child_process (e.g. indexed-btree typosquat payload).
references:
  - https://www.securityweek.com/malicious-b-tree-npm-package-accumulates-millions-of-downloads/
  - https://attack.mitre.org/techniques/T1195/002/
  - https://attack.mitre.org/techniques/T1059/
author: Security Arsenal
date: 2026/04/06
tags:
  - attack.supply_chain_compromise
  - attack.t1195.002
  - attack.t1059
logsource:
  category: process_creation
  product: windows
detection:
  selection_parent:
    ParentImage|endswith: '\node.exe'
  selection_child:
    Image|endswith:
      - '\cmd.exe'
      - '\powershell.exe'
      - '\pwsh.exe'
      - '\wscript.exe'
      - '\cscript.exe'
      - '\mshta.exe'
      - '\curl.exe'
      - '\certutil.exe'
  filter_build_tools:
    CommandLine|contains:
      - 'node-gyp'
      - 'prebuild-install'
  condition: selection_parent and selection_child and not filter_build_tools
falsepositives:
  - Native module compilation during npm install (node-gyp)
  - Legitimate build orchestration scripts — restrict alerting to production servers and CI runners with pinned baselines
level: high
---
title: Node.js Process Making Outbound Connection to Non-Registry Destination
tid: 8c4d1e2f-3a5b-4c6d-8e7f-1a2b3c4d5e6f
status: experimental
description: Detects node.exe establishing outbound connections to destinations outside expected npm/CDN infrastructure, consistent with data exfiltration or second-stage payload retrieval from a trojanized dependency such as indexed-btree.
references:
  - https://www.securityweek.com/malicious-b-tree-npm-package-accumulates-millions-of-downloads/
  - https://attack.mitre.org/techniques/T1195/002/
  - https://attack.mitre.org/techniques/T1041/
author: Security Arsenal
date: 2026/04/06
tags:
  - attack.supply_chain_compromise
  - attack.t1195.002
  - attack.exfiltration
  - attack.t1041
logsource:
  category: network_connection
  product: windows
detection:
  selection:
    Image|endswith: '\node.exe'
    Initiated: 'true'
  filter_rfc1918:
    DestinationIp|startswith:
      - '10.'
      - '192.168.'
      - '172.16.'
      - '172.17.'
      - '172.18.'
      - '172.19.'
      - '172.2'
      - '172.30.'
      - '172.31.'
      - '127.'
  filter_known_registries:
    DestinationHostname|endswith:
      - '.npmjs.org'
      - '.npmjs.com'
      - '.yarnpkg.com'
      - '.github.com'
      - '.githubusercontent.com'
  condition: selection and not filter_rfc1918 and not filter_known_registries
falsepositives:
  - Legitimate application API calls from Node.js services — apply this rule to build agents and CI runners first, where Node.js egress should be almost exclusively registry traffic
level: medium
---
title: Linux Node Process Executing Shell Utility
tid: 5e2f7a8b-9c1d-4e3f-a6b5-8d7c6e5f4a3b
status: experimental
description: Detects a Node.js process on Linux executing shell utilities or script interpreters, a hallmark of malicious npm package runtime triggers (e.g. the indexed-btree prototype-method payload invoking child_process).
references:
  - https://www.securityweek.com/malicious-b-tree-npm-package-accumulates-millions-of-downloads/
  - https://attack.mitre.org/techniques/T1195/002/
  - https://attack.mitre.org/techniques/T1059/004/
author: Security Arsenal
date: 2026/04/06
tags:
  - attack.supply_chain_compromise
  - attack.t1195.002
  - attack.t1059.004
logsource:
  category: process_creation
  product: linux
detection:
  selection_parent:
    ParentImage|endswith: '/node'
  selection_child:
    Image|endswith:
      - '/sh'
      - '/bash'
      - '/dash'
      - '/curl'
      - '/wget'
      - '/python'
      - '/python3'
      - '/base64'
      - '/env'
  filter_node_gyp:
    CommandLine|contains:
      - 'node-gyp'
      - 'make '
  condition: selection_parent and selection_child and not filter_node_gyp
falsepositives:
  - Native addon builds (node-gyp invokes make/compilers)
  - Process managers and deployment wrappers — baseline per host role
level: high

KQL Hunt (Microsoft Sentinel / Defender)

This query hunts for the runtime trigger across both workstation/server telemetry (Defender) and syslog-ingested Linux hosts. Prioritize hits on build agents and production servers.

KQL — Microsoft Sentinel / Defender
// Hunt for Node.js spawning shells or sensitive-file access — indexed-btree style payload trigger
let ShellBinaries = dynamic(["cmd.exe","powershell.exe","pwsh.exe","mshta.exe","wscript.exe","cscript.exe","certutil.exe","/bin/sh","/bin/bash","sh","bash","curl","wget","base64","env","python3","python"]);
union isfuzzy=true
(DeviceProcessEvents
 | where InitiatingProcessFileName =~ "node.exe" or InitiatingProcessFileName =~ "node"
 | where FileName has_any (ShellBinaries)
 | extend HostName = DeviceName, Parent = InitiatingProcessFileName, Child = FileName, Cmd = ProcessCommandLine, Account = InitiatingProcessAccountName
 | project TimeGenerated, HostName, Parent, Child, Cmd, Account, Source = "Defender"),
(Syslog
 | where Facility == "user" and SyslogMessage has "node" and SyslogMessage has_any ("/bin/sh","/bin/bash","child_process")
 | extend HostName = HostName, Cmd = SyslogMessage
 | project TimeGenerated, HostName, Cmd, Source = "Syslog")
| order by TimeGenerated desc
KQL — Microsoft Sentinel / Defender
// Sentinel: Linux auditd/syslog hunt for Node.js reading credential files (post-trigger exfiltration behavior)
Syslog
| where TimeGenerated > ago(7d)
| where SyslogMessage has_all ("node", "open") or SyslogMessage has "node"
| where SyslogMessage has_any (".aws/credentials", ".ssh/id_", ".npmrc", ".env", ".netrc", "/etc/shadow", "kubeconfig")
| project TimeGenerated, HostName, ProcessName, SyslogMessage, SeverityLevel
| order by TimeGenerated desc

Velociraptor VQL

Use this artifact to sweep your fleet for the malicious package on disk — in node_modules trees, lockfiles, and build-agent caches. Presence of indexed-btree is itself a reportable finding.

VQL — Velociraptor
-- Hunt for the malicious indexed-btree npm package across node_modules trees and lockfiles
LET hits_packages = SELECT FullPath, Size, Mtime
FROM glob(globs=['/**/node_modules/indexed-btree/package.json'], root='/')

LET hits_lockfiles = SELECT FullPath, Size, Mtime
FROM glob(globs=['/**/package-lock.json', '/**/yarn.lock', '/**/pnpm-lock.yaml'], root='/')
WHERE read_file(filename=FullPath, length=50000000) =~ 'indexed-btree'

SELECT FullPath AS ArtifactPath, Size, Mtime, 'package.json (installed copy)' AS FindingType FROM hits_packages
UNION ALL
SELECT FullPath AS ArtifactPath, Size, Mtime, 'lockfile reference' AS FindingType FROM hits_lockfiles
VQL — Velociraptor
-- Windows variant: hunt for indexed-btree and node.exe spawning shells
LET pkg_hits = SELECT FullPath, Size, Mtime
FROM glob(globs=['C:/Users/*/node_modules/indexed-btree/**',
                 'C:/*/node_modules/indexed-btree/package.json',
                 'D:/*/node_modules/indexed-btree/package.json'])

SELECT FullPath, Size, Mtime FROM pkg_hits

-- Correlate with suspicious node child processes
SELECT Pid, Ppid, Name, Exe, CommandLine, Username, CreateTime
FROM pslist()
WHERE Name =~ '(?i)cmd|powershell|pwsh|mshta|cscript|wscript'
  AND Ppid IN (SELECT Pid FROM pslist() WHERE Name =~ '(?i)node')

Fleet Remediation / Exposure Scan Script

Run this on developer workstations, CI runners, and build hosts to enumerate exposure and capture evidence before removal.

Bash / Shell
#!/bin/bash
# indexed-btree exposure scanner - run via your RCM/EDR or Ansible across the fleet
# Finds installed copies, lockfile references, and evidence of execution

REPORT="/tmp/indexed_btree_scan_$(hostname)_$(date +%Y%m%d).txt"
{
  echo "=== indexed-btree exposure scan: $(hostname) $(date -Is) ==="

  echo "[1] Installed package copies (node_modules):"
  find / -type d -name 'indexed-btree' -path '*node_modules*' 2>/dev/null

  echo "[2] Lockfile references:"
  grep -rls 'indexed-btree' \
    --include='package-lock.json' \
    --include='yarn.lock' \
    --include='pnpm-lock.yaml' \
    /home /root /opt /srv /var/lib 2>/dev/null

  echo "[3] package.json direct references:"
  grep -rls '"indexed-btree"' --include='package.json' \
    /home /root /opt /srv 2>/dev/null

  echo "[4] npm cache copies:"
  find /root/.npm /home/*/.npm -iname '*indexed-btree*' 2>/dev/null

  echo "[5] Running node processes with suspicious children:"
  for pid in $(pgrep -x node 2>/dev/null); do
    children=$(pgrep -P "$pid" 2>/dev/null)
    for c in $children; do
      cname=$(cat /proc/$c/comm 2>/dev/null)
      case "$cname" in
        sh|bash|dash|curl|wget|python*|base64)
          echo "  node[$pid] -> $cname[$c]: $(cat /proc/$c/cmdline 2>/dev/null | tr '\0' ' ')";;
      esac
    done
  done

  echo "[6] Recent node shell history artifacts (auditd, if present):"
  ausearch -k node_exec 2>/dev/null | tail -20 || echo "  (auditd key not configured)"
} | tee "$REPORT"

echo "Report written to $REPORT — forward to the IR team before remediation."
PowerShell
# Windows variant: scan for indexed-btree exposure and suspicious node child processes
$Report = "$env:TEMP\indexed_btree_scan_$env:COMPUTERNAME.txt"
$results = @()

# 1. Find installed copies and lockfile references
$searchRoots = @("C:\Users", "C:\src", "C:\projects", "D:\") | Where-Object { Test-Path $_ }
foreach ($root in $searchRoots) {
    Get-ChildItem -Path $root -Recurse -Directory -Filter "indexed-btree" -ErrorAction SilentlyContinue |
        Where-Object { $_.FullName -match 'node_modules' } |
        ForEach-Object { $results += "INSTALLED: $($_.FullName)" }

    Get-ChildItem -Path $root -Recurse -Include package-lock.json, yarn.lock, pnpm-lock.yaml -ErrorAction SilentlyContinue |
        Where-Object { (Get-Content $_.FullName -Raw -ErrorAction SilentlyContinue) -match 'indexed-btree' } |
        ForEach-Object { $results += "LOCKFILE: $($_.FullName)" }
}

# 2. Check npm cache
Get-ChildItem "$env:LOCALAPPDATA\npm-cache" -Recurse -Filter "*indexed-btree*" -ErrorAction SilentlyContinue |
    ForEach-Object { $results += "CACHE: $($_.FullName)" }

# 3. Detect node.exe with suspicious child processes right now
$nodeProcs = Get-CimInstance Win32_Process -Filter "Name='node.exe'" -ErrorAction SilentlyContinue
foreach ($np in $nodeProcs) {
    Get-CimInstance Win32_Process -Filter "ParentProcessId=$($np.ProcessId)" -ErrorAction SilentlyContinue |
        Where-Object { $_.Name -match 'cmd|powershell|pwsh|mshta|cscript|wscript|certutil' } |
        ForEach-Object { $results += "LIVE-EXEC: node[$($np.ProcessId)] -> $($_.Name) [$($_.ProcessId)]: $($_.CommandLine)" }
}

$results | Out-File $Report
if ($results.Count -gt 0) {
    Write-Warning "indexed-btree exposure or suspicious node execution found. See $Report — escalate to IR before deleting anything."
} else {
    Write-Output "No indexed-btree indicators found on $env:COMPUTERNAME."
}

Remediation

  1. Remove the package — but preserve evidence first. If indexed-btree is found anywhere, capture the installed directory, the lockfile, the installing user's identity, and the install timestamp before deletion. A malicious install on a build agent is an incident, not a cleanup task.
  2. Pin and reinstall from a clean lockfile. Delete node_modules, remove the malicious entry from the lockfile, regenerate it against the legitimate sorted-btree package, and reinstall. Verify integrity hashes (npm ci enforces lockfile integrity — prefer it over npm install in CI).
  3. Rotate credentials on any host where the package was installed. This is non-negotiable. Runtime-triggered npm malware classically harvests environment variables and credential files. Rotate npm tokens, CI/CD secrets, cloud provider keys, SSH keys, and any .env secrets present on affected hosts. Assume exfiltration occurred if the host ever executed the application with the dependency loaded.
  4. Purge caches and artifacts. Clear npm caches on affected machines (npm cache clean --force), purge the package from internal artifact registries (Artifactory/Nexus/Verdaccio) and remote caches, and rebuild any container images or serverless bundles that included it. Scan historical image layers in your registry — takedown from npm does nothing for images already built.
  5. Block the package at the perimeter of your supply chain. Add indexed-btree to your artifact repository's block list and your dependency firewall (e.g., artifact proxy quarantine rules). Also block it at the DNS/egress layer for registry-mirror traffic if applicable.
  6. Institutionalize typosquat defenses:
    • Enforce npm ci with committed, reviewed lockfiles in CI — no ad-hoc npm install <name> in pipelines.
    • Deploy dependency-firewall tooling or registry proxies that score new/unusual packages before they're consumable by developers.
    • Enable npm's --ignore-scripts for CI installs where native builds aren't required, and audit packages that require install scripts.
    • Add a pull-request gate that diffs package.json and lockfiles and flags any new dependency with low download counts, recent publish dates, or names within edit distance of popular packages.
    • Address AI-assistant package hallucination in your secure development guidance — code suggestions referencing plausible-but-wrong package names are a growing initial-access vector, and indexed-btree-style squatters profit from exactly that behavior.
  7. Hunt retroactively. Run the VQL and scripts above across your full estate, including ephemeral build-agent images and developer laptops. Then run the Sigma/KQL detections over the last 30–90 days of telemetry to determine whether the trigger ever fired in your environment — package presence tells you about exposure; child-process and egress telemetry tells you about impact.

The Bottom Line

indexed-btree is a textbook evolution of the npm typosquat playbook: functional decoy code, a runtime-gated trigger hidden on a prototype method, and a distribution model that monetizes simple human error at scale. The defensive lesson is not "audit harder" — it's that dependency presence checks are only the first half of the job. Because the payload sleeps until invoked, your detection strategy must pair software-composition inventory (what's in the tree?) with runtime behavioral detection (is Node.js doing things a sorting library never should?). Close both gaps, rotate credentials wherever exposure is confirmed, and treat every affected build agent as a potential secrets-compromise incident.

Related Resources

Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub

Is your security operations ready?

Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.