Back to Intelligence

keyv & cacheable npm Supply Chain Compromise: Self-Propagating Cloud Credential Theft — OTX Pulse Analysis & Enterprise Detection Pack

SA
Security Arsenal Team
August 6, 2026
10 min read

Threat Summary

A live OTX pulse published by AlienVault (TLP:WHITE) confirms an active supply chain compromise affecting the keyv and cacheable npm package ecosystems — libraries that collectively account for tens of millions of weekly downloads. The campaign began on August 4, 2026, when attackers compromised the maintainer account jaredwray, granting them publish rights across multiple packages in both namespaces.

This is not a typosquat or dependency-confusion play — this is a trusted-maintainer account takeover, the most dangerous class of npm supply chain attack, because malicious versions are signed by a legitimate, widely trusted publisher. Any CI/CD pipeline, developer workstation, or production build that resolves latest or a floating semver range for keyv or cacheable-namespace packages is a candidate victim.

The attack chain is elegant and dangerous:

  1. Maintainer account compromise — attacker publishes trojanized package versions.
  2. Preinstall hook execution — a malicious preinstall script fires before the package is even fully installed, bypassing many lockfile-review workflows.
  3. Runtime bootstrap — the hook downloads a Bun runtime (a legitimate JavaScript runtime), giving the attacker a clean, portable execution environment independent of the host's Node version and many EDR script-interpreter hooks.
  4. Obfuscated payload execution — the Bun runtime executes heavily obfuscated payloads that harvest credentials.
  5. Self-propagation — the payload is capable of spreading, likely re-publishing to additional npm accounts using stolen maintainer tokens, and harvesting cloud credentials (AWS, GCP, Azure) from developer environments and CI runners.

The collective objective is clear: credential harvesting at scale, with cloud credentials as the crown jewel. Stolen cloud keys from developer machines and build agents enable lateral movement into production infrastructure, data theft, crypto-mining, and downstream supply chain attacks. The self-propagating design mirrors the playbook of the Shai-Hulud npm worm campaigns — steal maintainer tokens, republish, repeat.

Threat Actor / Malware Profile

Attribution: Unknown (no named actor in the pulse). The tradecraft — maintainer account takeover, preinstall-hook delivery, credential harvesting with self-propagation — is consistent with financially motivated supply chain crews and overlaps with TTPs seen in prior npm worm incidents.

Distribution method: Trojanized versions of legitimate packages in the keyv and cacheable npm namespaces, published through the compromised jaredwray maintainer account. Victims ingest the malware through normal npm install / npm ci operations with no social engineering required.

Payload behavior:

  • Executes via a preinstall lifecycle hook defined in package.json — runs before dependency resolution completes.
  • Downloads and stages a Bun runtime binary, then executes obfuscated JavaScript payloads through it. Using Bun instead of the host Node interpreter is an evasion choice: it avoids Node-specific instrumentation and gives the attacker a controlled, statically compiled runtime.
  • Harvests credentials from developer/CI environments: ~/.npmrc (npm tokens), ~/.aws/credentials, ~/.azure/, ~/.config/gcloud/, environment variables (AWS_ACCESS_KEY_ID, AZURE_*, GITHUB_TOKEN), SSH keys, and browser/OS keychain material where accessible.
  • Self-propagating: uses stolen npm publish tokens to push malicious versions to additional packages the victim maintains — turning every compromised developer into a new distribution node.

C2 communication: Exfiltration of harvested credentials to attacker infrastructure (specific C2 endpoints were not enumerated in this pulse's IOC set — network egress from bun/node processes to non-registry hosts should be treated as the primary detection surface).

Persistence: Primary persistence is ecosystem-level — the worm persists by re-publishing across the npm registry rather than on a single host. Host-level persistence may include modification of package.json files in local projects and poisoning of local npm caches (~/.npm/_cacache).

Anti-analysis techniques: Heavy JavaScript obfuscation, execution under an alternate runtime (Bun) to evade Node-instrumented EDRs, preinstall timing to execute before security scanners that audit post-install trees, and abuse of legitimate package signing/publish workflows that defeats signature-based trust.

IOC Analysis

The pulse contains 9 file hash indicators — no IPs, domains, or URLs were published in this set:

  • FileHash-SHA1 (2): 35a672cf34b996b91f3e1c28cbf3a05a37e036e4, f525d52ceb966516686b482d3dc0137028cc6a63
  • FileHash-SHA256 (3): 54dc7ea54a1317cca0e890a2770630cf7fa6c97813e0cb9d2caa93012b350668, 9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc, fd3ca4007b225fdf8de7af4345a19179d5efa8c4bb9205f88cda806e5684b1eb
  • FileHash-MD5 (3): 4140f7e17e6f97f83aa3472473e01add, 7bcf8d9f6834c44450eac145a967d2f2, f92ee93a0af971a3966bfa8efa9c2625

These hashes correspond to the malicious package payloads and staged artifacts (likely the obfuscated payload files and/or the downloaded Bun-staged scripts). Multi-algorithm publication (MD5/SHA1/SHA256) ensures compatibility across tooling generations.

How to operationalize:

  1. EDR/XDR block lists: Push all SHA256 hashes to your endpoint platform's block policy immediately (MD5/SHA1 retained for legacy SIEM correlation).
  2. Artifact scanning in CI/CD: Hash every file in node_modules and npm cache directories during pipeline security stages; flag matches before deployment.
  3. SIEM ingestion: Load hashes into a threat intel watchlist (Sentinel ThreatIntelligenceIndicator, Splunk lookup, etc.) and join against file-creation telemetry.
  4. Behavioral coverage: Because hashes rotate with each republished package version, hash-only detection is insufficient — pair with the behavioral Sigma/KQL detections below targeting preinstall execution and Bun runtime staging.
  5. Tooling for decoding/verification: Use shasum/certutil -hashfile for local verification, VirusTotal/MalwareBazaar for hash enrichment, and npm audit plus Socket.dev CLI for package-level detection of the trojanized versions.

Detection Engineering

YAML
---
title: npm Preinstall Hook Spawning Script or Runtime Download
description: Detects npm lifecycle hooks (preinstall/install) spawning shells or downloading executables — the initial execution vector in the keyv/cacheable supply chain compromise.
logsource:
    category: process_creation
    product: windows
 detection:
    selection_parent:
        ParentImage|endswith:
            - '\npm.cmd'
            - '\npm.exe'
            - '\node.exe'
    selection_script:
        CommandLine|contains:
            - 'preinstall'
            - 'curl '
            - 'wget '
            - 'Invoke-WebRequest'
            - 'iwr '
            - 'bun.sh'
    condition: selection_parent and selection_script
falsepositives:
    - Legitimate packages with native build steps downloading toolchains
level: high
tags:
    - attack.initial_access
    - attack.t1195.002
    - attack.execution
    - attack.t1059
status: experimental
date: 2026/08/06
---
title: Bun Runtime Execution from Temp or Cache Directory
description: Detects execution of a Bun runtime binary from user-writable temp/cache paths — staging behavior used by the compromised keyv/cacheable payload to execute obfuscated scripts under an alternate runtime.
logsource:
    category: process_creation
    product: windows
 detection:
    selection_img:
        Image|contains:
            - '\AppData\Local\Temp\'
            - '\.npm\_cacache\'
            - '\node_modules\'
        Image|endswith:
            - '\bun.exe'
            - '\bun'
    condition: selection_img
falsepositives:
    - Developers legitimately using Bun installed via official installer (path will typically be under a dedicated .bun directory)
level: high
tags:
    - attack.execution
    - attack.t1059.007
    - attack.defense_evasion
    - attack.t1218
status: experimental
date: 2026/08/06
---
title: Node or Bun Process Reading Cloud Credential Stores
description: Detects node.exe/bun.exe accessing AWS, Azure, GCP, or npm credential files — the credential harvesting objective of the keyv/cacheable worm payload.
logsource:
    category: file_event
    product: windows
detection:
    selection_proc:
        Image|endswith:
            - '\node.exe'
            - '\bun.exe'
            - '\npm.cmd'
    selection_target:
        TargetFilename|contains:
            - '\.aws\credentials'
            - '\.azure\'
            - '\gcloud\credentials'
            - '\.npmrc'
            - '\.ssh\id_'
    condition: selection_proc and selection_target
falsepositives:
    - Legitimate CLI tooling (aws-cli, az, gcloud wrappers) invoked via Node scripts
level: critical
tags:
    - attack.credential_access
    - attack.t1552
    - attack.t1552.001
status: experimental
date: 2026/08/06
KQL — Microsoft Sentinel / Defender
// Hunt: npm/bun processes making network connections AND touching credential stores
// Target: keyv/cacheable supply chain payload behavior
let credPaths = dynamic(["\\.aws\\credentials", "\\.azure\\", "\\gcloud\\", ".npmrc", "\\.ssh\\"]);
let suspectProcs = dynamic(["node.exe", "bun.exe", "npm.cmd"]);
let credAccess = DeviceFileEvents
| where TimeGenerated > ago(7d)
| where InitiatingProcessFileName has_any (suspectProcs)
| where FolderPath has_any (credPaths)
| project CredAccessTime=TimeGenerated, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, FolderPath, ActionType;
let netEgress = DeviceNetworkEvents
| where TimeGenerated > ago(7d)
| where InitiatingProcessFileName has_any (suspectProcs)
| where RemoteUrl !has_any ("registry.npmjs.org", "github.com", "nodejs.org")
| where isnotempty(RemoteIP)
| project NetTime=TimeGenerated, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteIP, RemoteUrl, RemotePort;
credAccess
| join kind=inner netEgress on DeviceName, InitiatingProcessFileName
| where abs(datetime_diff('minute', NetTime, CredAccessTime)) <= 30
| project DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, FolderPath, CredAccessTime, RemoteIP, RemoteUrl, RemotePort, NetTime
| order by CredAccessTime desc
PowerShell
# IOC & Artifact Hunt: keyv/cacheable npm supply chain compromise
# Run elevated on developer workstations and build agents

$ErrorActionPreference = 'SilentlyContinue'
$report = @()

# --- 1. Hash sweep of known-bad payloads in npm caches and temp dirs ---
$badHashes = @(
    '54dc7ea54a1317cca0e890a2770630cf7fa6c97813e0cb9d2caa93012b350668',
    '9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc',
    'fd3ca4007b225fdf8de7af4345a19179d5efa8c4bb9205f88cda806e5684b1eb'
)
$sweepPaths = @("$env:LOCALAPPDATA\npm-cache", "$env:USERPROFILE\.npm", "$env:TEMP", "$env:USERPROFILE\.bun")
foreach ($p in $sweepPaths) {
    if (Test-Path $p) {
        Get-ChildItem -Path $p -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object {
            $h = (Get-FileHash -Path $_.FullName -Algorithm SHA256).Hash.ToLower()
            if ($badHashes -contains $h) {
                $report += [pscustomobject]@{Finding='MALICIOUS HASH MATCH'; Path=$_.FullName; SHA256=$h}
            }
        }
    }
}

# --- 2. Detect suspicious preinstall hooks referencing keyv/cacheable or remote downloads ---
$projectRoots = @("$env:USERPROFILE\source", "$env:USERPROFILE\repos", "$env:USERPROFILE\dev", "C:\builds", "C:\agent\_work")
foreach ($root in $projectRoots) {
    if (Test-Path $root) {
        Get-ChildItem -Path $root -Recurse -Filter 'package.json' -Depth 6 -ErrorAction SilentlyContinue | ForEach-Object {
            $raw = Get-Content $_.FullName -Raw
            if ($raw -match '"preinstall"\s*:\s*"[^"]*(curl|wget|bun|node -e|Invoke-WebRequest)') {
                $report += [pscustomobject]@{Finding='SUSPICIOUS PREINSTALL HOOK'; Path=$_.FullName; SHA256='-'}
            }
            if ($raw -match '"(keyv|cacheable[^"]*)"\s*:\s*"\^') {
                $report += [pscustomobject]@{Finding='FLOATING RANGE ON AFFECTED PACKAGE'; Path=$_.FullName; SHA256='-'}
            }
        }
    }
}

# --- 3. Bun runtime staged in non-standard locations ---
$bunHits = Get-ChildItem -Path "$env:TEMP","$env:USERPROFILE\.npm" -Recurse -Filter 'bun*.exe' -ErrorAction SilentlyContinue
foreach ($b in $bunHits) {
    $report += [pscustomobject]@{Finding='BUN RUNTIME IN TEMP/CACHE'; Path=$b.FullName; SHA256=(Get-FileHash $b.FullName -Algorithm SHA256).Hash}
}

# --- 4. Network connections from node/bun to non-registry endpoints ---
Get-NetTCPConnection -State Established | Where-Object {
    $_.OwningProcess -and ((Get-Process -Id $_.OwningProcess).ProcessName -match '^(node|bun)')
} | ForEach-Object {
    $report += [pscustomobject]@{Finding='ACTIVE NODE/BUN EGRESS'; Path=(Get-Process -Id $_.OwningProcess).Path; SHA256="$($_.RemoteAddress):$($_.RemotePort)"}
}

# --- 5. Exposure check: cloud credential files present (at-risk if above hits) ---
$credFiles = @("$env:USERPROFILE\.aws\credentials", "$env:USERPROFILE\.npmrc", "$env:USERPROFILE\.azure")
foreach ($c in $credFiles) { if (Test-Path $c) { $report += [pscustomobject]@{Finding='CREDENTIAL STORE PRESENT (AT RISK)'; Path=$c; SHA256='-'} } }

$report | Format-Table -AutoSize
if ($report) { $report | Export-Csv -Path "$env:TEMP\keyv_hunt_$(Get-Date -Format yyyyMMdd_HHmm).csv" -NoTypeInformation; Write-Host "Findings exported." -ForegroundColor Red } else { Write-Host 'No indicators found.' -ForegroundColor Green }

Response Priorities

Immediate (0–4 hours):

  • Push all 9 IOC hashes to EDR/XDR block lists and SIEM watchlists.
  • Pin or roll back keyv and cacheable-namespace dependencies to known-good versions across all active projects; freeze CI builds using floating semver ranges (^, latest).
  • Run the hunt script on developer workstations, build agents, and any host that executed npm install since August 4, 2026.
  • Audit npm audit logs / lockfile diffs for version changes to affected packages in the campaign window.

24 hours:

  • This is credential-stealing malware — treat all credentials on any potentially exposed host as compromised. Rotate: npm publish tokens, cloud access keys (AWS/Azure/GCP), GitHub/GitLab PATs, SSH keys, and CI/CD secrets.
  • Revoke and reissue maintainer-level npm tokens org-wide; enforce 2FA with hardware keys or granular access tokens on all publisher accounts.
  • Review cloud audit logs (CloudTrail, Azure Activity Log, GCP Audit Logs) for anomalous use of developer/CI keys since Aug 4.
  • Identify any packages your organization publishes — confirm none were republished by a compromised internal maintainer (self-propagation check).

1 week:

  • Architectural hardening: enforce lockfile integrity with npm ci --ignore-scripts as the default in CI; require explicit allowlisting for lifecycle scripts.
  • Deploy a package-vetting proxy (Socket.dev, JFrog Curation, Artifact Firewall) that blocks newly published versions for a quarantine window before pipeline ingestion.
  • Move CI builds to ephemeral, credential-less runners using OIDC-based short-lived cloud auth (no static keys on disk).
  • Add the Sigma rules above to production and integrate the KQL hunt as a scheduled Sentinel analytic rule.
  • Update vendor/third-party risk assessments: any vendor software bundling keyv/cacheable must disclose patched versions.

Related Resources

Security Arsenal Incident Response Managed SOC & MDR Services AlertMonitor Threat Detection From The Dark Side Intel Hub

Is your security operations ready?

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