OpenSourceMalware researcher Paul has disclosed a cluster of nearly 800 malicious packages published to the npm registry as part of a single coordinated campaign. The packages use what he describes as "AI slop squatted" names — randomly generated, typo-adjacent package names at scale — but they all converge on the same payload: a cross-platform Remote Access Trojan (RAT) and infostealer that runs on Windows, macOS, and Linux.
This is not a precision typosquat of one popular library. This is volume-based pollution of the registry: flood npm with hundreds of plausible-looking, machine-generated names, and let developer typos, dependency confusion, and careless copy-paste from AI coding assistants do the rest. Every developer workstation, CI/CD runner, and build server in your environment is a potential victim — and developer endpoints are disproportionately valuable targets. They hold cloud credentials, SSH keys, signing certificates, source code, and tokens for your artifact registries and production infrastructure.
If your organization builds JavaScript or TypeScript anything, assume exposure until proven otherwise. This post gives you the hunting logic, detection rules, and remediation workflow to do exactly that.
Technical Analysis
What the campaign looks like
Based on the reporting from The Hacker News and OpenSourceMalware:
- Scale: ~800 packages published to the public npm registry as one campaign cluster.
- Naming: AI-generated "slop squatting" — randomized and typo-squatting names designed to resemble legitimate packages or to be installed via autocomplete/AI-assistant hallucination. This is a meaningful evolution: traditional typosquats target one or two high-value package names; slop squatting manufactures hundreds of name variants because the attacker only needs a small hit rate across a large surface.
- Payload: A cross-platform RAT with infostealer capability, executing on Windows, macOS, and Linux from the same package family — typically achieved with a JavaScript loader plus OS-conditional logic (
process.platformchecks) that pulls platform-specific second stages. - Delivery mechanism: As with the overwhelming majority of malicious npm packages, execution almost certainly occurs at install time via lifecycle scripts (
preinstall,install,postinstall) inpackage.json, or at firstrequire()/importof the package. Install-time execution is the more dangerous case because it fires in CI pipelines without any developer ever importing the code.
Why the AI-slop angle matters to defenders
Two trends converge here. First, developers increasingly accept package name suggestions from AI coding assistants, which are known to hallucinate plausible-but-nonexistent package names — and attackers now register those hallucinated names preemptively ("slop squatting"). Second, the per-package cost of publishing malware has dropped to near zero with AI-generated naming and boilerplate, so registry defenders are dealing with volume campaigns rather than surgical ones.
Defensive implication: your controls cannot rely on a blocklist of "known bad package names" alone. By the time a name is published on a threat intel feed, the campaign has moved on. You need behavioral detection (what install scripts and node processes do) and provenance controls (what is allowed to be installed at all).
Typical attack chain (defender's view)
- Developer or CI pipeline runs
npm install <slop-squatted-package>— via typo, dependency confusion, or an AI-assistant-suggested import. npm/nodeexecutes the package'spostinstallhook, spawningnode -e,curl/powershell, or a child shell.- Loader fingerprints the OS (
process.platform), then downloads a platform-appropriate second stage to a temp or user-profile directory. - Infostealer harvests browser credential stores, cookies/session tokens, SSH keys (
~/.ssh), cloud CLI credentials (~/.aws,~/.azure,~/.config/gcloud),.npmrctokens,.envfiles, and crypto wallets. - RAT establishes outbound C2 (HTTPS to attacker infrastructure or abused legitimate services) and may persist via Run keys, LaunchAgents, or systemd user units/cron depending on platform.
Exploitation status
This is an active, in-the-wild campaign — the packages were live on the npm registry and are being removed as identified. There is no CVE associated with this activity (it is malware distribution, not a product vulnerability), and it is not a CISA KEV item. The correct frame is: confirmed malicious packages at scale, requiring retroactive exposure assessment across every endpoint and pipeline that installs from npm.
Detection & Response
The detections below focus on high-fidelity behavioral signals: npm lifecycle scripts spawning shells and downloaders, node processes reaching out to pull second stages, and access to credential stores from build tooling. Tune package-name allowlists to your environment; the behaviors are the durable signal.
---
title: NPM Install Lifecycle Script Spawning Shell or Downloader
id: 3f9a1c72-8b4d-4e6f-a2c1-7d5e9b0a3f21
status: experimental
description: Detects npm/npx/node spawning shells, script interpreters, or download utilities consistent with malicious package install-time execution (postinstall hooks delivering RAT/infostealer payloads).
references:
- https://thehackernews.com/2026/08/nearly-800-malicious-npm-packages.html
- https://attack.mitre.org/techniques/T1195/002/
author: Security Arsenal
date: 2026/08/15
tags:
- attack.initial_access
- attack.t1195.002
- attack.execution
- attack.t1059
logsource:
category: process_creation
product: windows
detection:
selection_parent:
ParentImage|endswith:
- '\npm.cmd'
- '\npm.exe'
- '\npx.cmd'
- '\node.exe'
- '\yarn.cmd'
- '\pnpm.cmd'
selection_child:
Image|endswith:
- '\powershell.exe'
- '\pwsh.exe'
- '\cmd.exe'
- '\curl.exe'
- '\wget.exe'
- '\certutil.exe'
- '\bitsadmin.exe'
- '\mshta.exe'
- '\wscript.exe'
- '\cscript.exe'
filter_ci:
CommandLine|contains:
- 'npm run build'
- 'npm ci'
condition: selection_parent and selection_child and not filter_ci
falsepositives:
- Legitimate packages with native builds (node-gyp) may spawn cmd.exe; baseline per build host and alert on new parent/child pairs.
level: high
---
title: Node.js Executing Inline Script with Network or Credential Access
id: 8c2e5b14-6a3f-4d98-b7e2-1f4a9c6d8e30
status: experimental
description: Detects node.exe launched with inline eval flags or from temporary/user-profile directories, a common loader pattern for malicious npm packages executing payload code directly.
references:
- https://thehackernews.com/2026/08/nearly-800-malicious-npm-packages.html
- https://attack.mitre.org/techniques/T1059/007/
author: Security Arsenal
date: 2026/08/15
tags:
- attack.execution
- attack.t1059.007
- attack.command_and_control
- attack.t1071.001
logsource:
category: process_creation
product: windows
detection:
selection_eval:
Image|endswith: '\node.exe'
CommandLine|contains:
- ' -e '
- '--eval'
- '-p "'
selection_path:
Image|endswith: '\node.exe'
CommandLine|contains:
- '\AppData\Local\Temp\'
- '\AppData\Roaming\npm-cache\'
- '%TEMP%'
filter_known:
CommandLine|contains:
- 'npm\\node_modules'
- '\npx '
condition: (selection_eval or selection_path) and not filter_known
falsepositives:
- Developer ad-hoc node -e usage; rare in CI and on standard workstations. Baseline developer machines separately.
level: medium
---
title: Credential Store Access by Node or Package Manager Process
id: 5d7b2f41-9c1e-4a86-c3f5-2b8d6e0a4f19
status: experimental
description: Detects node/npm processes accessing browser credential stores, SSH keys, or cloud CLI credential files, consistent with infostealer staging from a malicious npm package.
references:
- https://thehackernews.com/2026/08/nearly-800-malicious-npm-packages.html
- https://attack.mitre.org/techniques/T1555/003/
- https://attack.mitre.org/techniques/T1552/001/
author: Security Arsenal
date: 2026/08/15
tags:
- attack.credential_access
- attack.t1555.003
- attack.t1552.001
logsource:
category: file_event
product: windows
detection:
selection_image:
Image|endswith:
- '\node.exe'
- '\npm.exe'
- '\npm.cmd'
selection_target:
TargetFilename|contains:
- '\Login Data'
- '\Cookies'
- '\Local State'
- '\.ssh\'
- '\.aws\credentials'
- '\.azure\'
- '\gcloud\'
- '\.npmrc'
- '.env'
condition: selection_image and selection_target
falsepositives:
- npm legitimately reads .npmrc during authentication; alert on .npmrc only when paired with browser or SSH key paths from the same process tree.
level: high
// Hunt: package-manager or node process spawning shells/downloaders or touching credential stores
// Tables: DeviceProcessEvents + DeviceFileEvents (Defender XDR / Sentinel)
let lookback = 14d;
let suspiciousChildren = dynamic(["powershell.exe","pwsh.exe","cmd.exe","curl.exe","wget.exe","certutil.exe","bitsadmin.exe","mshta.exe","wscript.exe","cscript.exe","sh","bash","zsh"]);
let pmParents = dynamic(["node.exe","npm.exe","npm.cmd","npx.cmd","yarn.cmd","pnpm.cmd","node","npm"]);
let credPaths = dynamic(["Login Data","Local State","\\.ssh\\","/.ssh/",".aws/credentials",".azure/","gcloud",".npmrc","/Cookies"]);
let procHits =
DeviceProcessEvents
| where TimeGenerated > ago(lookback)
| where InitiatingProcessFileName in~ (pmParents)
| where FileName in~ (suspiciousChildren)
| project TimeGenerated, DeviceName, AccountName,
Parent=InitiatingProcessFileName, Child=FileName,
ChildCmd=ProcessCommandLine, ParentCmd=InitiatingProcessCommandLine,
ReportId, DeviceId;
let fileHits =
DeviceFileEvents
| where TimeGenerated > ago(lookback)
| where InitiatingProcessFileName in~ (pmParents)
| where FolderPath has_any (credPaths)
| project TimeGenerated, DeviceName, AccountName,
Process=InitiatingProcessFileName, FileAccessed=FolderPath,
ReportId, DeviceId;
union procHits, fileHits
| summarize FirstSeen=min(TimeGenerated), LastSeen=max(TimeGenerated), EventCount=count()
by DeviceName, AccountName
| where EventCount >= 1
| order by LastSeen desc
-- Velociraptor artifact: hunt for suspicious node/npm process trees and second-stage staging
-- Run across Windows, macOS, and Linux endpoints (developer workstations and build runners)
SELECT Pid, Ppid, Name, Exe, CommandLine, Username, CreateTime
FROM pslist()
WHERE (
-- Node/npm spawning shells or download utilities
(Name =~ '(?i)(node|npm|npx|yarn|pnpm)'
AND CommandLine =~ '(?i)(powershell|cmd\.exe|curl|wget|/bin/sh|/bin/bash|certutil|mshta)')
OR
-- Node executing inline/eval code or running from temp paths
(Name =~ '(?i)node' AND CommandLine =~ '(-e |--eval)')
OR
(Name =~ '(?i)node' AND Exe =~ '(?i)(/tmp/|/var/tmp/|AppData.Local.Temp)')
)
#!/usr/bin/env bash
# audit-npm-exposure.sh — retroactive audit for malicious-package exposure on dev/build hosts
# Checks for lifecycle scripts in installed deps, suspicious postinstall execution, and known-bad patterns.
set -euo pipefail
REPORT="npm_supply_chain_audit_$(date +%Y%m%d_%H%M%S).txt"
echo "=== npm Supply Chain Exposure Audit — $(hostname) — $(date) ===" | tee "$REPORT"
# 1) Find all package.json files with install-time lifecycle hooks (execution at npm install)
echo -e "\n[1] Dependencies with preinstall/install/postinstall hooks:" | tee -a "$REPORT"
find "$HOME" /srv /opt /var/lib -maxdepth 6 -name package.json -path "*node_modules*" 2>/dev/null | while read -r f; do
if grep -Eq '"(preinstall|install|postinstall)"' "$f"; then
echo " HOOK: $f" | tee -a "$REPORT"
grep -E '"(preinstall|install|postinstall)"' "$f" | tee -a "$REPORT"
fi
done
# 2) Audit npm global + local caches for recently added, low-reputation packages
echo -e "\n[2] Globally installed packages (review for unrecognized names):" | tee -a "$REPORT"
npm ls -g --depth=0 2>/dev/null | tee -a "$REPORT" || true
# 3) Flag node processes with network connections right now (possible live RAT/loader)
echo -e "\n[3] node/npm processes with active network connections:" | tee -a "$REPORT"
ss -tupn 2>/dev/null | grep -Ei 'node|npm' | tee -a "$REPORT" || echo " none" | tee -a "$REPORT"
# 4) Check shell history for typosquatted-style installs (manual install of unknown packages)
echo -e "\n[4] Recent manual npm installs from shell history:" | tee -a "$REPORT"
grep -Eh 'npm (install|i) ' "$HOME/.bash_history" "$HOME/.zsh_history" 2>/dev/null | tail -50 | tee -a "$REPORT" || true
echo -e "\n=== Audit complete. Review $REPORT. Cross-reference hook packages against npm advisories and your internal allowlist. ===" | tee -a "$REPORT"
# audit-npm-exposure.ps1 — Windows developer/build host audit for malicious npm package exposure
$report = "npm_audit_$env:COMPUTERNAME_$(Get-Date -Format yyyyMMdd_HHmmss).txt"
"=== npm Supply Chain Audit — $env:COMPUTERNAME — $(Get-Date) ===" | Out-File $report
# 1) Installed dependencies with install-time lifecycle hooks (the primary execution vector)
"`n[1] node_modules packages with preinstall/install/postinstall hooks:" | Out-File $report -Append
Get-ChildItem -Path "$env:USERPROFILE","C:\builds","C:\agents" -Recurse -Filter package.json -Depth 6 -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -match 'node_modules' } |
ForEach-Object {
$c = Get-Content $_.FullName -Raw -ErrorAction SilentlyContinue
if ($c -match '"(preinstall|install|postinstall)"\s*:') {
" HOOK: $($_.FullName)" | Out-File $report -Append
($c | Select-String -Pattern '"(preinstall|install|postinstall)"\s*:\s*"[^"]*"' -AllMatches).Matches.Value |
ForEach-Object { " $_" | Out-File $report -Append }
}
}
# 2) Suspicious child processes historically spawned by node/npm (Defender/advanced hunt alternative)
"`n[2] node/npm processes spawning shells or downloaders (last 7 days, from event log):" | Out-File $report -Append
Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4688; StartTime=(Get-Date).AddDays(-7)} -ErrorAction SilentlyContinue |
Where-Object { $_.Message -match '(node|npm|npx)\.(exe|cmd)' -and $_.Message -match '(powershell|cmd\.exe|curl|certutil|mshta|wscript)' } |
Select-Object -First 50 TimeCreated, Message | Out-File $report -Append
# 3) Persistence artifacts commonly abused by cross-platform RATs
"`n[3] Run-key and Startup-folder entries referencing node/npm or temp paths:" | Out-File $report -Append
$runKeys = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run','HKLM:\Software\Microsoft\Windows\CurrentVersion\Run'
foreach ($k in $runKeys) {
Get-ItemProperty $k -ErrorAction SilentlyContinue | Out-String |
Select-String -Pattern 'node|npm|Temp|AppData' -AllMatches |
ForEach-Object { $_.Line } | Out-File $report -Append
}
"`n=== Done. Review $report; escalate any unknown hook packages to IR before deleting (preserve evidence). ===" | Out-File $report -Append
Remediation
Immediate (today):
- Freeze and inventory. Export dependency manifests (
package.json,package-lock.json,yarn.lock,pnpm-lock.yaml) from all active repos and build pipelines. Diff installed package names against OpenSourceMalware's published indicators for this campaign and npm's security advisories. Any package you cannot attribute to a known publisher is suspect — AI-slop names are intentionally unfamiliar. - Hunt before you delete. Run the audit scripts and KQL query above across developer workstations, CI runners, and build containers. If you find a malicious package, do not just
npm uninstall— assume the install hook already executed. Treat the host as compromised: isolate it, capture memory/disk if your IR retainer supports it, and proceed to credential rotation. - Rotate credentials on any exposed host. This is an infostealer: rotate npm tokens (
.npmrc), cloud CLI credentials (AWS/Azure/GCP), SSH keys, browser-stored session tokens, and any secrets in.envfiles on the affected machine. Revoke CI/CD pipeline tokens exposed to tainted builds.
Short term (this week):
- Disable install-time script execution by default. Add
--ignore-scriptsto CI install steps (orignore-scripts=truein.npmrc), then explicitly allowlist the small set of packages that legitimately need build scripts (e.g., native modules using node-gyp). This single control kills the install-hook execution vector for the vast majority of malicious npm packages. - Gate the registry. Route all installs through an internal proxy/repository (JFrog Artifactory, Sonatype Nexus, Azure Artifacts upstream) with malware screening and package-age policies. Consider a minimum package age (e.g., block packages published <7 days ago) — slop-squat campaigns live and die in their first days, and this policy alone defeats most of them.
- Pin and verify. Enforce lockfile integrity (
npm ciin CI, never barenpm install), enable Sigstore/npm provenance verification where available, and alert on lockfile changes that introduce net-new transitive dependencies.
Structural (this quarter):
- Address dependency confusion. Scope your internal packages (
@yourorg/*), register those scopes publicly, and configure npm to resolve internal scopes only from your private registry. - Constrain AI-assisted development. Developers pasting AI-suggested
npm installcommands are a named vector in this campaign's model. Policy plus technical control: installs only from the vetted internal proxy, so a hallucinated package name fails closed. - Monitor egress from build infrastructure. CI runners and dev workstations should not have unrestricted internet egress. Alert on node/npm processes establishing connections to non-registry destinations — a loader pulling a second stage has nowhere to hide.
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.