Back to Intelligence

indexed-btree npm Malware Hid Loader in Runtime Code: Detection and Remediation Guide

SA
Security Arsenal Team
September 22, 2026
9 min read

Checkmarx reported that the npm package indexed-btree was malicious and impersonated the legitimate sorted-btree package, a routine B-tree and indexing utility. The important shift is not only the typosquatting pattern. The package reportedly concealed its loader inside runtime application code instead of relying primarily on npm lifecycle scripts such as preinstall, install, or postinstall.

That matters because many teams still treat --ignore-scripts, disabled lifecycle hooks, and install-time scanning as if they neutralize package-borne execution. This incident is a direct warning: if a dependency is imported by your application, test harness, build tool, or server process, malicious code can execute when the module is required, not only when npm runs an install hook. Defenders should assume developer workstations, CI runners, artifact builders, and production Node services are all exposed if the package entered a lockfile or image.

No CVE identifier was provided in the reporting, and no CVSS score should be inferred. Treat this as an active supply-chain malware technique with practical urgency: identify exposure, remove the package, rotate secrets reachable from affected contexts, and hunt for runtime execution that install-script controls would miss.

Technical Analysis

Affected component: the npm ecosystem dependency path, specifically projects that resolved or installed indexed-btree in place of, alongside, or downstream from code expecting sorted-btree. The risk spans developer endpoints, CI/CD agents, Docker builds, server-side Node.js services, and any monorepo where dependency resolution can be influenced by name confusion, stale lockfiles, or permissive registry configuration.

Observed behavior from the reporting: indexed-btree masqueraded as an ordinary indexing library and hid malicious logic in runtime code rather than making lifecycle scripts the primary execution mechanism. From a defender perspective, the likely attack chain is:

  1. A developer, transitive dependency, automation bot, or poisoned lockfile introduces indexed-btree.
  2. Install-time controls that focus only on lifecycle scripts do not observe obvious execution.
  3. Application code imports the package during tests, build, server startup, or job execution.
  4. The module initializer or an exported function runs loader logic in the Node.js process.
  5. The loader may stage secondary payloads, read environment variables, access npm/Git/cloud credentials, spawn child processes, or open outbound connections.

Exploitation status: the package was observed and later removed, per the news summary. There is no public CVE in the item, no CISA KEV entry cited, and no confirmed widespread exploitation metrics provided. The defensive lesson is current and active in 2026: threat actors are adapting to controls that over-index on lifecycle scripts.

Key implication: --ignore-scripts is still useful, but it is not a boundary. A malicious package that is imported becomes code running with the identity, network reachability, filesystem scope, and secret material of the importing process. In CI, that can mean npm tokens, cloud role credentials, repository write tokens, signing keys, and access to internal package registries. In production, it can mean runtime access to customer data and service-to-service credentials.

Detection & Response

Prioritize hunts that connect three facts: presence of indexed-btree, execution by Node.js or package-manager processes, and follow-on behavior such as child process creation or unexpected egress. Do not rely only on install-hook telemetry.

YAML
---
title: Node.js Runtime Spawning Shell After Dependency Load
id: 5d21f6c1-8f44-4f0b-9a2d-9b7f31a6d001
status: experimental
description: Detects Node.js processes launching command interpreters or script hosts, consistent with runtime-loaded npm malware executing after import rather than during install.
references:
  - https://thehackernews.com/2026/09/malicious-npm-package-indexed-btree-hid.html
  - https://attack.mitre.org/techniques/T1059/
  - https://attack.mitre.org/techniques/T1195/002/
author: Security Arsenal
date: 2026/09/18
tags:
  - attack.execution
  - attack.t1059
  - attack.supply_chain_compromise
logsource:
  category: process_creation
  product: windows
detection:
  selection_parent:
    ParentImage|endswith:
      - '\node.exe'
      - '\nodejs.exe'
  selection_child:
    Image|endswith:
      - '\cmd.exe'
      - '\powershell.exe'
      - '\pwsh.exe'
      - '\wscript.exe'
      - '\cscript.exe'
      - '\rundll32.exe'
      - '\mshta.exe'
  condition: selection_parent and selection_child
falsepositives:
  - Legitimate build tools and test frameworks that shell out from Node.js
level: high
---
title: Suspicious npm Package indexed-btree Present in Command Line
id: 9d3af0ec-65f7-4b52-a3d4-8b77bb20d002
status: experimental
description: Detects package manager or Node command lines referencing the malicious indexed-btree package name during install, CI resolution, or execution.
references:
  - https://thehackernews.com/2026/09/malicious-npm-package-indexed-btree-hid.html
  - https://attack.mitre.org/techniques/T1195/002/
author: Security Arsenal
date: 2026/09/18
tags:
  - attack.supply_chain_compromise
  - attack.t1195.002
logsource:
  category: process_creation
  product: windows
detection:
  selection:
    CommandLine|contains:
      - 'indexed-btree'
      - 'node_modules\indexed-btree'
      - 'node_modules/indexed-btree'
falsepositives:
  - Threat hunting, detonation, or documentation mentioning the package name
level: critical
---
title: Node.js Loading From Temp or Cache Paths With Package Execution Context
id: 2b6c74f5-a22a-4f5f-9f7f-6b8f2a90d003
status: experimental
description: Detects Node.js executing scripts or child processes from temporary, cache, or user-profile paths while npm or node activity is present, a possible runtime loader staging pattern.
references:
  - https://thehackernews.com/2026/09/malicious-npm-package-indexed-btree-hid.html
  - https://attack.mitre.org/techniques/T1059/007/
author: Security Arsenal
date: 2026/09/18
tags:
  - attack.execution
  - attack.t1059.007
logsource:
  category: process_creation
  product: windows
detection:
  selection_img:
    Image|endswith:
      - '\node.exe'
      - '\npm.exe'
      - '\npx.exe'
  selection_cli:
    CommandLine|contains:
      - '\AppData\Local\Temp\'
      - '\.npm\'
      - '\node_modules\'
  filter_common:
    CommandLine|contains:
      - '\node_modules\npm\bin\npm-cli.js'
      - '\node_modules\corepack\'
  condition: selection_img and selection_cli and not filter_common
falsepositives:
  - Legitimate npx cache execution and local development tooling
level: medium
KQL — Microsoft Sentinel / Defender
// Hunt for exposure to indexed-btree and suspicious Node.js follow-on execution in Microsoft Defender/Sentinel
let lookback = 14d;
let suspiciousPackage = dynamic(['indexed-btree','node_modules/indexed-btree','node_modules\indexed-btree']);
let proc = DeviceProcessEvents
| where TimeGenerated >= ago(lookback)
| where ProcessCommandLine has_any (suspiciousPackage)
   or (FileName in~ ('node.exe','nodejs.exe','npm.exe','npx.exe','pnpm.exe','yarn.exe') and ProcessCommandLine has_any ('node_modules','.npm','AppData\\Local\\Temp','/tmp/','.cache'))
| project TimeGenerated, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, SHA256, DeviceId;
let nodeChildren = DeviceProcessEvents
| where TimeGenerated >= ago(lookback)
| where InitiatingProcessFileName in~ ('node.exe','nodejs.exe')
| where FileName in~ ('cmd.exe','powershell.exe','pwsh.exe','rundll32.exe','mshta.exe','wscript.exe','cscript.exe','bash','sh','curl.exe','wget.exe')
| project ChildTime=TimeGenerated, DeviceName, ChildProcess=FileName, ChildCommandLine=ProcessCommandLine, NodeParent=InitiatingProcessFileName, ParentCommandLine=InitiatingProcessCommandLine, DeviceId;
proc
| join kind=leftouter nodeChildren on DeviceId
| extend Exposure = iif(ProcessCommandLine has 'indexed-btree', 'package-reference', 'node-runtime-context')
| summarize arg_max(TimeGenerated, *) by DeviceId, ProcessCommandLine
| order by TimeGenerated desc;

// Network egress from Node.js around package execution windows
DeviceNetworkEvents
| where TimeGenerated >= ago(lookback)
| where InitiatingProcessFileName in~ ('node.exe','nodejs.exe')
| where InitiatingProcessCommandLine has_any ('indexed-btree','node_modules','.npm','AppData\\Local\\Temp','/tmp/')
| project TimeGenerated, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteIP, RemoteUrl, RemotePort, ActionType
| order by TimeGenerated desc;
VQL — Velociraptor
-- Hunt endpoints for indexed-btree references and suspicious Node.js child execution
SELECT Pid, Name, CommandLine, Exe, Username, CreateTime
FROM pslist()
WHERE CommandLine =~ 'indexed-btree|node_modules(.|\\\\)indexed-btree|npm(.exe)? install|pnpm|yarn'
   OR (Name =~ 'node|nodejs|npm|npx' AND CommandLine =~ 'Temp|/tmp/|.npm|node_modules')

-- Search common dependency manifests and lockfiles for the malicious package
SELECT FullPath, Size, Mtime, Data
FROM glob(globs=['C:/Users/*/package-lock.json','C:/Users/*/yarn.lock','C:/Users/*/pnpm-lock.yaml','/home/*/package-lock.json','/home/*/yarn.lock','/home/*/pnpm-lock.yaml','/opt/**/package-lock.json','/srv/**/package-lock.json'])
WHERE Data =~ 'indexed-btree'

-- Review active Node.js network connections during triage
SELECT Pid, Name, CommandLine, LocalAddress, LocalPort, RemoteAddress, RemotePort, State
FROM netstat()
WHERE Name =~ 'node|nodejs'
  AND State =~ 'ESTABLISHED|SYN'
Bash / Shell
#!/usr/bin/env bash
# Verify and contain indexed-btree exposure across repos, lockfiles, caches, and images.
# Run on developer workstations, CI runners, and build hosts. Exit non-zero if exposure is found.
set -euo pipefail
BAD='indexed-btree'
ROOTS=("$HOME" "/workspace" "/workspaces" "/srv" "/opt" "/tmp")
FOUND=0

log() { printf '[%s] %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$*"; }

for root in "${ROOTS[@]}"; do
  [ -d "$root" ] || continue
  while IFS= read -r -d '' f; do
    if grep -Iq "$BAD" "$f"; then
      log "EXPOSED manifest/lockfile: $f"
      FOUND=1
    fi
  done < <(find "$root" -type f \( -name package.json -o -name package-lock.json -o -name npm-shrinkwrap.json -o -name yarn.lock -o -name pnpm-lock.yaml \) -print0 2>/dev/null)

  while IFS= read -r -d '' d; do
    log "EXPOSED node_modules directory: $d"
    FOUND=1
  done < <(find "$root" -type d -path "*/node_modules/$BAD" -prune -print0 2>/dev/null)
done

# Inspect npm cache metadata without executing package code
if command -v npm >/dev/null 2>&1; then
  npm cache ls "$BAD" 2>/dev/null | grep -q "$BAD" && { log 'EXPOSED npm cache reference'; FOUND=1; } || true
fi

# List running Node processes for manual containment; do not kill blindly in shared CI
ps -eo pid,ppid,user,comm,args | grep -E 'node|nodejs|npm|npx' | grep -v grep || true

if [ "$FOUND" -eq 1 ]; then
  log 'Action: stop affected Node processes, revoke reachable credentials, remove package, restore clean lockfile, rebuild artifacts from a clean agent.'
  exit 2
fi

log 'No indexed-btree reference found in scanned roots.'
exit 0

Remediation

  1. Remove and replace: delete indexed-btree from package.json, lockfiles, caches, and node_modules. If B-tree functionality is required, explicitly depend on the legitimate sorted-btree package and verify provenance, publisher history, and release integrity before use.
  2. Treat every importer as compromised: any host, CI job, container build, or production service that installed or imported the package should be considered exposed until proven otherwise. Rebuild artifacts from a clean agent with a clean lockfile rather than patching in place.
  3. Rotate secrets aggressively: npm automation tokens, GitHub/GitLab/Bitbucket tokens, cloud access keys and workload identity credentials, SSH keys, signing keys, registry credentials, database connection strings, and any environment variables present in CI or production runtime. Prioritize credentials available during build because runtime loaders can read process env after import.
  4. Reconcile dependency provenance: generate or refresh SBOMs, diff resolved dependencies against expected packages, alert on name similarity to top packages, and block newly published lookalikes unless reviewed.
  5. Reduce import-time blast radius: keep --ignore-scripts, but add controls that assume code executes at require time. Run builds with least privilege, short-lived credentials, egress allowlists, isolated runners, read-only root filesystems where possible, and no persistent cloud roles on shared agents.
  6. Enforce registry policy: use a private proxy registry with allowlists, namespace ownership checks, provenance/attestation requirements, lockfile-only CI installs such as npm ci, integrity verification, and protections against dependency confusion between public npm and internal scopes.
  7. Monitor after cleanup: hunt for Node.js child processes, outbound connections from build/runtime identities, unexpected writes to temp and cache paths, and cloud or repository API use from CI identities after package removal. Review audit logs for token use from unusual ASN, geography, or user agent.
  8. Validate before closure: require evidence that the malicious package is absent from lockfiles and images, exposed credentials are revoked, affected artifacts are rebuilt, and no suspicious egress or token reuse remains. Reference the original report for context: https://thehackernews.com/2026/09/malicious-npm-package-indexed-btree-hid.html

Related Resources

Security Arsenal Alert Triage Automation AlertMonitor Platform Book a SOC Assessment platform Intel Hub

Is your security operations ready?

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