Open source registries remain one of the highest-leverage intrusion vectors available to state-sponsored actors, and the latest proof point is a cluster of 13 malicious npm packages delivering a previously undocumented JavaScript stealer tracked as WeaselBiscuit. According to research published by OpenSourceMalware, the malware exhibits functional overlap with BeaverTail — a strain strongly associated with the Democratic People's Republic of Korea's (DPRK) long-running Contagious Interview campaign, which lures software developers with fake job offers, coding assessments, and poisoned dependencies.
What makes WeaselBiscuit particularly dangerous is its targeting logic: it harvests Chrome extension storage, the on-disk LevelDB databases where browser extensions — including cryptocurrency wallets, password managers, and session-token-bearing developer tools — persist their data. A single npm install on a developer workstation can therefore translate directly into drained wallets, hijacked SaaS sessions, and a foothold inside your build pipeline. If your organization builds JavaScript or TypeScript anywhere in its SDLC, treat this as an active threat against your engineering fleet, not an abstract supply-chain talking point.
Technical Analysis
What is affected
- Ecosystem: the npm public registry — 13 packages in this cluster (identified and reported by OpenSourceMalware; check their feed and your registry proxy for the current package list, as takedown status changes hourly)
- Platforms: any workstation or CI runner executing
npm install/npm ciagainst the poisoned packages — Windows, macOS, and Linux are all in scope because the payload is JavaScript running under Node.js - Data at risk: Chrome (and Chromium-family) extension local storage — LevelDB files under the browser profile's
Local Extension SettingsandSync Extension Settingsdirectories, keyed by 32-character extension IDs. High-value targets include MetaMask (nkbihfbeogaeaoehlefnkodbefgpgknn), Phantom (bfnaelmomeimhlpmgjnjophhpkkoljpa), Coinbase Wallet (hnfanknocfeofbddgcijnmhnfnkdnaad), and any extension caching API tokens or session state - CVE / CVSS: none assigned — this is a malicious-package campaign, not a vulnerability in npm itself. There is no CISA KEV entry; exploitation is confirmed active in the wild through package publication and download
How the attack works (defender's view of the chain)
- Delivery: a developer installs one of the 13 typosquatted or dependency-confusion-style packages — typically via a fake job-assessment repo, a tutorial, or a transitive dependency pull.
- Execution: the malicious package abuses npm lifecycle hooks (
preinstall/install/postinstall) so its JavaScript runs immediately at install time under the developer's user context, inheriting their credentials and file access. No exploit, no vulnerability — just Node doing exactly what it is designed to do. - Collection: WeaselBiscuit enumerates browser profile directories and reads the extension-storage LevelDB files (
*.log,*.ldb) directly from disk, bypassing browser sandboxing and extension permission models entirely. - Exfiltration / staging: consistent with BeaverTail lineage, harvested data is staged and sent to attacker-controlled infrastructure, frequently over HTTPS to blend with normal traffic. BeaverTail's later variants have also functioned as loaders for follow-on payloads (e.g., Python-based RATs), so a confirmed WeaselBiscuit hit should be treated as a potential full compromise, not a contained theft.
Why the BeaverTail overlap matters
Functional overlap with DPRK's Contagious Interview tooling tells defenders two things. First, the intended victims are developers — recruiters, fake interview loops, and poisoned repos are the lure surface, so your engineering org's hiring and onboarding workflows are part of the attack surface. Second, DPRK operations of this type are financially motivated at scale (cryptocurrency theft funds the regime), meaning wallet extensions and exchange sessions are priority targets and dwell time before monetization can be minutes, not weeks.
Detection & Response
The detection philosophy here is behavioral: you cannot signature-match 13 package names that will be replaced by 13 more next week. You can reliably detect (a) npm install-time script execution spawning unexpected child processes, and (b) any non-browser process reading Chrome extension storage — the second being a high-fidelity indicator with almost no legitimate use.
---
title: Suspicious Child Process Spawned by npm or Node During Install
tid: 3f8a2c14-7b9d-4e61-a502-8c1d9e4f7a23
status: experimental
description: Detects npm/npx/node spawning shells, downloaders, or script interpreters during package install — consistent with malicious lifecycle-script abuse seen in WeaselBiscuit and BeaverTail-style npm supply-chain attacks.
references:
- https://thehackernews.com/2026/09/weaselbiscuit-stealer-spreads-via-13.html
- https://attack.mitre.org/techniques/T1195/002/
author: Security Arsenal
date: 2026/09/19
tags:
- attack.initial_access
- attack.t1195.002
- attack.execution
- attack.t1059.007
logsource:
category: process_creation
product: windows
detection:
selection_parent:
ParentImage|endswith:
- '\node.exe'
- '\npm.cmd'
- '\npx.cmd'
selection_child:
Image|endswith:
- '\powershell.exe'
- '\pwsh.exe'
- '\cmd.exe'
- '\curl.exe'
- '\wscript.exe'
- '\cscript.exe'
- '\mshta.exe'
- '\certutil.exe'
- '\bitsadmin.exe'
condition: selection_parent and selection_child
falsepositives:
- Legitimate native-module builds (node-gyp) can spawn cmd.exe during install; tune by build-server OU or known-good package allowlist
level: high
---
title: Non-Browser Process Accessing Chrome Extension Storage
tid: 9d41b7e6-2c83-4f15-b890-5a3e7d1c6f92
status: experimental
description: Detects any process other than the browser reading Chrome/Chromium extension LevelDB storage. Extension storage holds wallet keys, session tokens, and cached credentials — the primary collection target of the WeaselBiscuit stealer. Near-zero legitimate non-browser access.
references:
- https://thehackernews.com/2026/09/weaselbiscuit-stealer-spreads-via-13.html
- https://attack.mitre.org/techniques/T1555/003/
author: Security Arsenal
date: 2026/09/19
tags:
- attack.credential_access
- attack.t1555.003
- attack.collection
- attack.t1005
logsource:
category: file_event
product: windows
detection:
selection_path:
TargetFilename|contains:
- '\Local Extension Settings\'
- '\Sync Extension Settings\'
selection_ext:
TargetFilename|endswith:
- '.log'
- '.ldb'
- '.sst'
filter_browser:
Image|endswith:
- '\chrome.exe'
- '\msedge.exe'
- '\brave.exe'
- '\opera.exe'
condition: selection_path and selection_ext and not filter_browser
falsepositives:
- Enterprise DLP or EDR content-inspection drivers; validate and exclude by signer, never by path
level: critical
---
title: Node.js Script Reading Browser Profile Directories
tid: 5c27e0a4-8f36-4d09-a174-2b9c6e83f015
status: experimental
description: Detects node.exe executing scripts that reference Chromium browser profile or extension-storage paths — a hallmark of JavaScript stealers such as WeaselBiscuit harvesting extension data from disk.
references:
- https://thehackernews.com/2026/09/weaselbiscuit-stealer-spreads-via-13.html
- https://attack.mitre.org/techniques/T1059/007/
author: Security Arsenal
date: 2026/09/19
tags:
- attack.credential_access
- attack.t1555.003
- attack.execution
- attack.t1059.007
logsource:
category: process_creation
product: windows
detection:
selection_image:
Image|endswith: '\node.exe'
selection_cli:
CommandLine|contains:
- 'Local Extension Settings'
- 'Sync Extension Settings'
- 'Google\\Chrome\\User Data'
- 'Microsoft\\Edge\\User Data'
- 'leveldb'
condition: selection_image and selection_cli
falsepositives:
- Rare; internal tooling that audits browser extension inventories should be renamed/allowlisted explicitly
level: critical
The KQL below hunts both phases — install-time process lineage and storage access — in one union so a single scheduled analytic rule covers the kill chain on Windows endpoints via Defender for Endpoint. It also flags outbound network connections from Node to rare destinations within the same window, which approximates exfiltration staging:
let Lookback = 7d;
let BrowserProcs = dynamic(["chrome.exe","msedge.exe","brave.exe","opera.exe"]);
let InstallExec =
DeviceProcessEvents
| where Timestamp > ago(Lookback)
| where InitiatingProcessFileName in~ ("node.exe","npm.cmd","npx.cmd")
| where FileName in~ ("powershell.exe","pwsh.exe","cmd.exe","curl.exe","wscript.exe","cscript.exe","mshta.exe","certutil.exe","bitsadmin.exe")
| project InstallTime=Timestamp, DeviceId, DeviceName, InitiatingProcessCommandLine, SpawnedProcess=FileName, SpawnedCommandLine=ProcessCommandLine, AccountName;
let StorageRead =
DeviceFileEvents
| where Timestamp > ago(Lookback)
| where FolderPath has_any ("Local Extension Settings","Sync Extension Settings")
| where FileName has_any (".log",".ldb",".sst")
| where not(InitiatingProcessFileName in~ (BrowserProcs))
| project ReadTime=Timestamp, DeviceId, DeviceName, ReaderProcess=InitiatingProcessFileName, ReaderCommandLine=InitiatingProcessCommandLine, TargetPath=FolderPath, FileName, AccountName;
let NodeNet =
DeviceNetworkEvents
| where Timestamp > ago(Lookback)
| where InitiatingProcessFileName =~ "node.exe"
| where RemotePort in (443, 80)
| summarize FirstSeen=min(Timestamp), Connections=count(), Destinations=make_set(RemoteUrl, 25) by DeviceId, DeviceName, InitiatingProcessCommandLine;
union InstallExec, StorageRead
| join kind=leftouter (NodeNet) on DeviceId
| summarize arg_max(coalesce(InstallTime, ReadTime), *) by DeviceId, coalesce(InitiatingProcessCommandLine, ReaderCommandLine)
| order by coalesce(InstallTime, ReadTime) desc
Tune the NodeNet join to exclude your known CI subnets and internal registry (Artifactory/Nexus/Verdaccio) before turning it into an alert — developer machines generate substantial legitimate Node HTTPS traffic.
For endpoint forensics and fleet-wide sweeping, the Velociraptor artifact below inventories who is running Node with browser-path references, which machines hold wallet extension storage, and which non-browser processes have open handles or recent execution lineage touching those paths:
-- Hunt: WeaselBiscuit-style extension-storage theft
-- Identifies Node processes referencing browser profile paths and enumerates
-- wallet-extension LevelDB stores present on disk (potential collection targets).
LET node_sus = SELECT Pid, Name, CommandLine, Exe, Username, CreateTime
FROM pslist()
WHERE Name =~ '(?i)node|npm|npx'
AND CommandLine =~ '(?i)(Local Extension Settings|Sync Extension Settings|User Data|leveldb|nkbihfbeogaeaoehlefnkodbefgpgknn|bfnaelmomeimhlpmgjnjophhpkkoljpa)'
LET wallet_stores = SELECT FullPath, Size, Mtime
FROM glob(globs='C:/Users/*/AppData/Local/Google/Chrome/User Data/*/Local Extension Settings/*/*.ldb')
WHERE FullPath =~ '(?i)(nkbihfbeogaeaoehlefnkodbefgpgknn|bfnaelmomeimhlpmgjnjophhpkkoljpa|hnfanknocfeofbddgcijnmhnfnkdnaad)'
SELECT 'suspicious_node_process' AS FindingType,
format(format='%v', args=node_sus) AS Detail,
'' AS WalletStorePath
FROM node_sus
UNION ALL
SELECT 'wallet_extension_store_present' AS FindingType,
format(format='mtime=%v size=%v', args=[wallet_stores.Mtime, wallet_stores.Size]) AS Detail,
wallet_stores.FullPath AS WalletStorePath
FROM wallet_stores
The remediation script audits developer workstations and CI runners for the install-time behaviors this campaign depends on: packages carrying lifecycle hooks, git/URL-based dependencies that bypass registry review, and recently introduced transitive deps. Run it against every repo before merge, and against workstations during IR scoping:
#!/usr/bin/env bash
# weaselbiscuit-repo-audit.sh — audit npm projects for supply-chain risk indicators
set -euo pipefail
REPO="${1:-.}"
echo "=== Auditing $REPO for npm supply-chain risk indicators ==="
# 1. Flag packages declaring install-time lifecycle hooks (WeaselBiscuit's execution vector)
echo "--- [1] Dependencies with preinstall/install/postinstall hooks ---"
if [ -f "$REPO/package-lock.json" ]; then
grep -nE '"(preinstall|install|postinstall)"' "$REPO/package-lock.json" || echo "none in lockfile"
fi
find "$REPO/node_modules" -maxdepth 3 -name package.json 2>/dev/null \
| xargs grep -lE '"(preinstall|install|postinstall)"' 2>/dev/null || echo "node_modules not present or no hooks found"
# 2. Flag non-registry dependency sources (git URLs, tarballs, file: refs)
echo "--- [2] Non-registry dependency sources in manifests ---"
grep -nE '"[^"]+":\s*"(git\+|git://|https?://|file:)' "$REPO/package.json" 2>/dev/null || echo "none"
# 3. Surface recently published / recently added transitive deps for review
echo "--- [3] Lockfile packages resolved in last 30 days (review newest first) ---"
find "$REPO" -name package-lock.json -mtime -30 -print
# 4. Registry audit and signature check
echo "--- [4] npm audit + registry signature verification ---"
( cd "$REPO" && npm audit --audit-level=moderate || true )
( cd "$REPO" && npm audit signatures 2>/dev/null || echo "signature audit not supported on this npm version" )
# 5. Check for install-script execution evidence in shell history / CI logs
echo "--- [5] Reminder: run installs with scripts disabled ---"
echo " npm ci --ignore-scripts # default posture for CI"
echo " Verify allowlisted native builds explicitly (node-gyp, esbuild, etc.)"
Remediation
There is no patch for this threat — the fix is posture, scoping, and response. Execute in this order:
- Determine exposure (today). Pull the 13 package names from the OpenSourceMalware report and The Hacker News coverage, then search every lockfile, private registry cache (Artifactory/Nexus/Verdaccio), and CI build log for them. Any hit is a confirmed execution — the install hook ran the moment the package was installed. Escalate hits to IR immediately.
- Contain confirmed hits. Isolate affected workstations. Because BeaverTail-lineage malware functions as a loader, assume full compromise: rotate all credentials accessible from that machine (SSH keys, cloud CLI tokens, npm tokens, GitHub PATs), revoke browser-synced sessions, and treat any wallet extension on the host as drained — move funds from new, clean keys.
- Kill the execution vector in CI. Make
npm ci --ignore-scriptsthe default for all pipeline installs, with an explicit allowlist for the small set of packages that legitimately need native builds. This single change neutralizes the entire class of install-hook stealers. - Gate the registry. Route all installs through a private registry proxy with a quarantine policy: block packages published fewer than N days old, block packages without provenance/signatures (
npm audit signatures), and block install hooks from unvetted packages. Evaluate Socket, StepSecurity, or equivalent for behavioral package analysis before admission. - Pin and review. Enforce lockfile integrity (
npm ci, never floatingnpm installin CI), require human review of any PR that adds a dependency or changespackage-lock.json, and alert ongit+/ URL-based dependencies in manifests. - Harden the human layer. Brief engineering on Contagious Interview tradecraft: no coding-assessment repos or recruiter-supplied projects on corporate hardware or corporate GitHub accounts; run interview exercises in disposable VMs or cloud dev environments with no browser profile, no saved credentials, and no network path to production.
- Deploy the detections above and schedule the KQL as an analytic rule on developer and build-server asset groups. The extension-storage read rule is near-zero-false-positive — page on it.
Reference: OpenSourceMalware's campaign write-up and package list (via The Hacker News: https://thehackernews.com/2026/09/weaselbiscuit-stealer-spreads-via-13.html). No vendor advisory or CISA deadline applies — this is malicious-package takedown plus defender posture, not a patch cycle.
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.