Threat Summary
AlienVault OTX pulse data confirms an active software supply chain compromise targeting the JavaScript ecosystem. On August 4, 2026, a threat actor compromised the npm maintainer account Jaredwray — maintainer of the widely deployed keyv and cacheable packages — and published at least ten malicious package versions. Combined, these packages account for tens of millions of weekly downloads, meaning the blast radius extends across a massive portion of the global Node.js dependency tree, including transitive dependencies inside enterprise CI/CD pipelines.
The attack chain is surgical and modern:
- Initial access: Compromise of a legitimate maintainer account (likely via credential theft or session token hijacking), bypassing the need for typosquatting or social engineering.
- Delivery: Malicious
preinstallhooks embedded inpackage.jsonexecute automatically duringnpm install— before any dependency audit or lockfile review can catch them. - Staging: The hook downloads a Bun runtime (a legitimate JavaScript runtime abused as a living-off-the-land execution vehicle) and runs a heavily obfuscated payload through it.
- C2 resolution via blockchain: Rather than hardcoding C2 infrastructure — which defenders can sinkhole — the malware reads its command-and-control address from an Ethereum smart contract. This makes the C2 mutable, resilient, and effectively impossible to take down through traditional registrar/hosting channels.
- Objective: Mass credential harvesting — cloud provider keys (AWS/GCP/Azure), npm tokens, SSH keys, environment variables, and CI/CD secrets — with self-propagating worm behavior that uses stolen npm tokens to publish malicious versions of additional packages the victim maintains.
This is a credential theft and propagation engine, not a smash-and-grab. The worm component means every infected developer machine becomes a new distribution node.
Threat Actor / Malware Profile
Attribution: Unknown actor. The tradecraft — maintainer account compromise, preinstall hook execution, blockchain-based C2 resolution, and self-propagation via stolen publish tokens — mirrors techniques observed in prior npm ecosystem attacks (e.g., the Shai-Hulud worm lineage), but no formal attribution exists at this time.
Distribution method: Trojanized legitimate packages published to the official npm registry under a trusted maintainer account. Victims pull the malware through normal npm install workflows — no user error required.
Payload behavior:
- Executes during package installation via
preinstalllifecycle scripts - Downloads and stages the Bun runtime to execute payloads outside of the standard Node.js process lineage (evading Node-focused EDR detections)
- Harvests credentials from environment variables,
~/.npmrc,~/.aws/credentials,~/.ssh/, cloud metadata endpoints, and CI secret stores - Uses stolen npm automation tokens to self-propagate by publishing poisoned versions of any packages the victim can publish
C2 communication: The C2 address is retrieved dynamically by reading state from an Ethereum smart contract. Network defenders will see outbound JSON-RPC calls to public Ethereum endpoints (e.g., eth_call to nodes such as cloudflare-eth.com, Infura, Alchemy, or public RPC gateways) followed by HTTPS beaconing to the resolved C2. Blocking static C2 domains is insufficient — monitor for anomalous blockchain RPC traffic from developer workstations and build runners.
Persistence mechanism: Primary persistence is environmental — the malware embeds in node_modules, lockfiles, and downstream published packages rather than traditional host persistence. On developer machines, expect credential theft to enable follow-on access that outlives removal of the package itself.
Anti-analysis techniques:
- Heavy payload obfuscation executed through Bun to break Node.js-specific instrumentation
- Blockchain C2 resolution defeats static IOC blocking and sandbox detonation without live Ethereum RPC access
- Execution during
preinstalloccurs before most npm audit and SCA tooling scans the dependency
IOC Analysis
The pulse contains 9 file-based indicators — 2 SHA1, 3 SHA256, and 4 MD5 hashes — representing the malicious payload samples, staged Bun runtime artifacts, and obfuscated scripts recovered from the compromised package versions.
Operationalization guidance for SOC teams:
- File hashes (SHA256 priority): Push all SHA256 values into your EDR blocklist immediately. SHA256 is collision-resistant and should be your enforcement-grade indicator. MD5/SHA1 values are useful for retro-hunting in forensic images and artifact stores but should not be the sole blocking mechanism.
- Where to hunt: These artifacts will appear in
node_modulesdirectories, npm cache paths (~/.npm/_cacache), temporary download directories, and CI/CD build agent workspaces — not in standard program directories. - Tooling: Use
npm ls keyv cacheableand lockfile diffing to identify affected dependency versions across repositories. YARA scanning of build agent disk images and EDR hash-based retrohunts (Defender, CrowdStrike, SentinelOne) will surface historical infections. Network-side, hunt proxy and DNS logs for Ethereum JSON-RPC endpoints originating from non-browser processes. - Blockchain IOCs: Identify the Ethereum contract address referenced in the Netskope research and monitor for
eth_callrequests to it — this is a high-fidelity, low-noise network indicator.
Detection Engineering
---
title: Malicious npm preinstall Hook Spawning Runtime Download
description: Detects npm/npx install processes spawning shell commands that download or execute payloads, consistent with the keyv/cacheable supply chain compromise using preinstall hooks to stage the Bun runtime
id: 9f3a2c1e-7b4d-4e8f-a2c1-5d6e7f8a9b0c
status: experimental
author: Security Arsenal Threat Intelligence
date: 2026/08/06
logsource:
category: process_creation
product: windows
service: null
detection:
selection_parent:
ParentImage|endswith:
- '\node.exe'
- '\npm.cmd'
- '\npm.exe'
- '\bun.exe'
selection_child:
Image|endswith:
- '\curl.exe'
- '\powershell.exe'
- '\pwsh.exe'
- '\cmd.exe'
- '\certutil.exe'
- '\bitsadmin.exe'
selection_args:
CommandLine|contains:
- 'preinstall'
- 'bun.sh'
- 'bun.exe'
- 'install.sh'
- 'curl '
- 'Invoke-WebRequest'
condition: selection_parent and (selection_child or selection_args)
falsepositives:
- Legitimate build tooling downloading dependencies during CI builds
level: high
tags:
- attack.initial_access
- attack.t1195.002
- attack.t1059
---
title: Outbound Ethereum JSON-RPC C2 Resolution from Non-Browser Process
description: Detects processes other than browsers making outbound connections to public Ethereum RPC endpoints, matching the npm stealer technique of reading C2 configuration from an Ethereum smart contract
id: 2b7c4d5e-1a8f-4c9d-b3e2-6f7a8b9c0d1e
status: experimental
author: Security Arsenal Threat Intelligence
date: 2026/08/06
logsource:
category: network_connection
product: windows
detection:
selection_dest:
DestinationHostname|contains:
- 'cloudflare-eth.com'
- 'infura.io'
- 'alchemy.com'
- 'rpc.ankr.com'
- 'eth.llamarpc.com'
- 'ethereum-rpc.publicnode.com'
- 'mainnet.infura.io'
filter_browsers:
Image|endswith:
- '\chrome.exe'
- '\firefox.exe'
- '\msedge.exe'
- '\brave.exe'
- '\opera.exe'
condition: selection_dest and not filter_browsers
falsepositives:
- Legitimate Web3 development tooling and crypto wallet desktop applications
level: high
tags:
- attack.command_and_control
- attack.t1071.001
- attack.t1102
---
title: Bun Runtime Execution of Obfuscated JavaScript Payload
description: Detects execution of the Bun runtime with script arguments from temporary or npm cache directories, matching the keyv/cacheable payload staging behavior
id: 5e8f1a2b-3c4d-4e5f-9a6b-7c8d9e0f1a2b
status: experimental
author: Security Arsenal Threat Intelligence
date: 2026/08/06
logsource:
category: process_creation
product: linux
detection:
selection_img:
Image|endswith:
- '/bun'
- '/npm'
- '/node'
selection_paths:
CommandLine|contains:
- '/tmp/'
- '/.npm/_cacache'
- 'node_modules'
- 'preinstall'
- 'eval('
- 'atob('
- 'base64'
condition: selection_img and selection_paths
falsepositives:
- Legitimate packages using preinstall scripts for native module compilation
level: medium
tags:
- attack.execution
- attack.t1059.007
- attack.t1027
// Hunt: npm supply chain compromise artifacts — keyv/cacheable Ethereum C2 stealer
// Looks for: (1) hash matches on known malicious payloads, (2) suspicious child processes
// of npm/node/bun, (3) non-browser connections to Ethereum RPC endpoints
let MaliciousHashes = dynamic([
"54dc7ea54a1317cca0e890a2770630cf7fa6c97813e0cb9d2caa93012b350668",
"9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc",
"fd3ca4007b225fdf8de7af4345a19179d5efa8c4bb9205f88cda806e5684b1eb",
"4140f7e17e6f97f83aa3472473e01add",
"7bcf8d9f6834c44450eac145a967d2f2",
"f92ee93a0af971a3966bfa8efa9c2625"
]);
let EthRpcHosts = dynamic([
"cloudflare-eth.com", "infura.io", "alchemy.com",
"rpc.ankr.com", "eth.llamarpc.com", "ethereum-rpc.publicnode.com"
]);
// Part 1: File hash matches in node_modules, npm cache, or temp paths
union withsource=tableName_
(DeviceFileEvents
| where SHA256 in~ (MaliciousHashes) or MD5 in~ (MaliciousHashes)
| project tableName_, TimeGenerated, DeviceName, FileName, FolderPath, SHA256, MD5, InitiatingProcessCommandLine),
(DeviceProcessEvents
| where SHA256 in~ (MaliciousHashes) or MD5 in~ (MaliciousHashes)
| project tableName_, TimeGenerated, DeviceName, FileName, FolderPath, SHA256, MD5, ProcessCommandLine),
// Part 2: Suspicious child processes spawned by package managers
(DeviceProcessEvents
| where InitiatingProcessFileName has_any ("node.exe", "npm.cmd", "npm", "bun", "bun.exe")
| where FileName has_any ("powershell.exe", "pwsh.exe", "cmd.exe", "curl.exe", "curl", "wget", "certutil.exe", "sh", "bash")
| where ProcessCommandLine has_any ("preinstall", "bun", "Invoke-WebRequest", "iwr", "base64", "eval(", "/tmp/")
| project tableName_, TimeGenerated, DeviceName, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine, AccountName),
// Part 3: Non-browser processes resolving C2 via Ethereum RPC
(DeviceNetworkEvents
| where RemoteUrl has_any (EthRpcHosts)
| where not(InitiatingProcessFileName has_any ("chrome.exe", "firefox.exe", "msedge.exe", "brave.exe", "opera.exe"))
| project tableName_, TimeGenerated, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteUrl, RemoteIP, RemotePort)
| sort by TimeGenerated desc
<#
.SYNOPSIS
Hunt script: npm keyv/cacheable supply chain compromise artifacts
.DESCRIPTION
Checks developer workstations and build agents for:
- Known malicious file hashes (SHA256/MD5/SHA1) in npm cache, node_modules, temp dirs
- Installed keyv/cacheable packages with suspicious preinstall hooks
- Recent Bun runtime downloads staged outside normal paths
- Evidence of credential store access following install activity
Run as Administrator. Output written to C:\Temp\npm-sc-hunt.csv
#>
$ErrorActionPreference = 'SilentlyContinue'
$results = @()
$outFile = "C:\Temp\npm-sc-hunt.csv"
New-Item -ItemType Directory -Path "C:\Temp" -Force | Out-Null
$maliciousHashes = @(
"54dc7ea54a1317cca0e890a2770630cf7fa6c97813e0cb9d2caa93012b350668",
"9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc",
"fd3ca4007b225fdf8de7af4345a19179d5efa8c4bb9205f88cda806e5684b1eb",
"4140f7e17e6f97f83aa3472473e01add",
"7bcf8d9f6834c44450eac145a967d2f2",
"f92ee93a0af971a3966bfa8efa9c2625",
"35a672cf34b996b91f3e1c28cbf3a05a37e036e4",
"f525d52ceb966516686b482d3dc0137028cc6a63"
)
Write-Host "[*] Phase 1: Hash sweep of npm cache, temp, and node_modules paths..." -ForegroundColor Cyan
$searchRoots = @(
"$env:LOCALAPPDATA\npm-cache",
"$env:APPDATA\npm-cache",
"$env:TEMP",
"$env:USERPROFILE\.npm\_cacache",
"C:\Windows\Temp"
)
foreach ($root in $searchRoots) {
if (Test-Path $root) {
Get-ChildItem -Path $root -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object {
$sha256 = (Get-FileHash -Path $_.FullName -Algorithm SHA256).Hash
if ($maliciousHashes -contains $sha256.ToLower()) {
$results += [PSCustomObject]@{
Finding = "MALICIOUS_HASH_MATCH"
Path = $_.FullName
Hash = $sha256
Detail = "SHA256 match against OTX pulse IOC"
Time = Get-Date
}
Write-Host "[!!!] MALICIOUS FILE: $($_.FullName)" -ForegroundColor Red
}
}
}
}
Write-Host "[*] Phase 2: Scanning package.json files for suspicious preinstall hooks..." -ForegroundColor Cyan
$userDirs = Get-ChildItem "C:\Users" -Directory
foreach ($user in $userDirs) {
Get-ChildItem -Path $user.FullName -Recurse -Filter "package.json" -File -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -match "node_modules" } | ForEach-Object {
$content = Get-Content $_.FullName -Raw
if ($content -match '"preinstall"' -and $content -match 'curl|wget|bun|powershell|Invoke-WebRequest|base64') {
$results += [PSCustomObject]@{
Finding = "SUSPICIOUS_PREINSTALL_HOOK"
Path = $_.FullName
Hash = "N/A"
Detail = "preinstall hook containing download/execution primitives"
Time = Get-Date
}
Write-Host "[!!] Suspicious preinstall: $($_.FullName)" -ForegroundColor Yellow
}
}
}
Write-Host "[*] Phase 3: Checking for rogue Bun runtime installs..." -ForegroundColor Cyan
$bunPaths = @("$env:TEMP\bun.exe", "$env:LOCALAPPDATA\Temp\bun.exe", "C:\ProgramData\bun\bun.exe")
foreach ($p in $bunPaths) {
if (Test-Path $p) {
$h = (Get-FileHash $p -Algorithm SHA256).Hash
$results += [PSCustomObject]@{
Finding = "BUN_RUNTIME_STAGED"
Path = $p
Hash = $h
Detail = "Bun runtime present in non-standard staging path"
Time = Get-Date
}
Write-Host "[!!] Bun runtime staged: $p" -ForegroundColor Yellow
}
}
Write-Host "[*] Phase 4: Checking active connections to Ethereum RPC endpoints..." -ForegroundColor Cyan
$ethHosts = @("cloudflare-eth.com","infura.io","alchemy.com","rpc.ankr.com","eth.llamarpc.com")
Get-NetTCPConnection -State Established | ForEach-Object {
$proc = Get-Process -Id $_.OwningProcess
if ($proc.ProcessName -notmatch "chrome|firefox|msedge|brave|opera") {
$results += [PSCustomObject]@{
Finding = "NONBROWSER_OUTBOUND_CONN"
Path = $proc.Path
Hash = "N/A"
Detail = "$($proc.ProcessName) -> $($_.RemoteAddress):$($_.RemotePort) — validate against Ethereum RPC hosts"
Time = Get-Date
}
}
}
Write-Host "[*] Phase 5: Flagging credential files modified in last 72h (possible theft staging)..." -ForegroundColor Cyan
$credFiles = @("$env:USERPROFILE\.npmrc", "$env:USERPROFILE\.aws\credentials", "$env:USERPROFILE\.ssh\id_rsa")
foreach ($cf in $credFiles) {
if (Test-Path $cf) {
$item = Get-Item $cf
if ($item.LastWriteTime -gt (Get-Date).AddHours(-72)) {
$results += [PSCustomObject]@{
Finding = "CREDENTIAL_FILE_RECENT_ACCESS"
Path = $cf
Hash = "N/A"
Detail = "Modified $($item.LastWriteTime) — correlate with install activity"
Time = Get-Date
}
Write-Host "[!!] Credential file recently modified: $cf" -ForegroundColor Yellow
}
}
}
if ($results.Count -gt 0) {
$results | Export-Csv -Path $outFile -NoTypeInformation
Write-Host "`n[+] $($results.Count) findings exported to $outFile" -ForegroundColor Green
} else {
Write-Host "`n[+] No indicators found." -ForegroundColor Green
}
Response Priorities
Immediate (0–4 hours):
- Push all 9 IOC hashes into EDR/AV blocklists; enforce at the gateway where possible
- Freeze dependency updates in CI/CD pipelines; pin lockfiles to known-good versions of
keyvandcacheablereleased before 2026-08-04 - Run the hash sweep and preinstall-hook hunt across developer workstations and build agents
- Block or alert on non-browser outbound traffic to public Ethereum RPC endpoints
Within 24 hours:
- Rotate all credentials on any machine that installed a compromised package version — this is credential-stealing malware: npm automation tokens, AWS/GCP/Azure keys, SSH keys, and any secrets present in environment variables or CI secret stores must be treated as compromised
- Audit npm publish activity for any packages maintained by your developers — the self-propagating worm uses stolen tokens to publish poisoned versions under victim identities
- Revoke and reissue CI/CD pipeline tokens; review cloud audit logs (CloudTrail, Azure Activity Log) for anomalous API usage from developer IP ranges
Within 1 week:
- Enforce phishing-resistant MFA (FIDO2/hardware keys) on all npm maintainer and publisher accounts — this attack began with a maintainer account compromise
- Deploy lifecycle-script blocking in build environments (
npm config set ignore-scripts truewhere feasible) and require explicit allowlisting forpreinstall/postinstallhooks - Implement private registry proxying (Artifactory/Nexus/Verdaccio) with quarantine windows for newly published package versions before they reach build agents
- Add egress controls restricting build runners to an approved destination list — blockchain RPC endpoints and arbitrary download hosts should never be reachable from CI infrastructure
- Integrate lockfile integrity verification and SCA tooling with install-time behavioral analysis into the pipeline
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.