Endor Labs identified 88 new malicious npm packages belonging to three new waves (Wave 2, 3, and 4) of the PhantomRaven campaign distributed between November 2025 and February 2026., At the time of writing, the campaign remains active: 81 of the 88 packages are still available on npm, and two of the three new command-and-control servers continue to operate.
PhantomRaven is a software supply chain attack that uses Remote Dynamic Dependencies (RDD) to hide credential-stealing malware in non-registry dependencies that bypass standard security scanning. The first wave affecting 126+ packages with over 86,000 downloads, was first described by Koi Security in October 2025.
By correlating infrastructure indicators, code similarities, and attacker operational patterns, we were able to track the evolution of the campaign and identify new waves as they emerged. Throughout the campaign, the attacker rotated infrastructure, modified operational details (such as PHP endpoint names, dependency names, and npm package descriptions), and used more than 50 disposable npm accounts. Despite these changes, the underlying malware payload remained largely unchanged.
Infected packages
Wave-2
Wave-3
Wave-4
Indicators of Compromise (IOCs)
All four C2 domains registered through Amazon Registrar, Inc. with AWS Route53nameservers, Identity Protection Service WHOIS privacy, and zero TLS certificates.
Timeline
We classifed each wave by its C2 domain, the attacker-controlled server that hosts the malicious payload. Each wave uses a distinct domain and IP address, while other attributes like the dependency name in package.json and the PHP endpoint for data exfiltration evolve across and sometimes within waves. The underlying malware payload remains functionally identical throughout.
Understanding the PhantomRaven Campaign
PhantomRaven was a large-scale npm supply chain attack affecting 126+ packages with over 86,000 downloads, first described by Koi Security in October 2025.
The download of second stage payloads by attacker-owned code is a very common pattern found in many malicious packages, intended to reduce the footprint of malicious code in a compromised package itself.
PhantomRaven introduces an interesting variation by delegating the download to npm's resolution mechanism. By specifying an HTTP URL instead of a version range in dependencies of package.json, npm itself fetches the malicious payload from the attacker's server during a normal install, no explicit attacker code required. Koi Security termed this technique Remote Dynamic Dependencies (RDD). The packages themselves contained nothing but a benign "Hello World" script, while npm silently retrieved and executed the real payload as part of its standard dependency resolution.
Identifying PhantomRaven
Endor Labs was able to detect the malicious activity by analysing package code, infrastructure signals and dependencies fetched from non-npm registries. Below are the screenshots of Endor’s detection and clustering pipeline:


The Infection Chain
- Developer runs npm install
- npm reads package.json → finds a URL dependency pointing to the attacker's C2: "http://packages.storeartifact.com/npm/"
- npm fetches the .tgz from the attacker's server (NOT from npmjs.com)
- Downloaded package has a preinstall hook node index.js
- Malicious index.js executes automatically:
- Harvests emails from .gitconfig, .npmrc, env vars
- Collects CI/CD tokens (GitHub, GitLab, Jenkins, CircleCI)
- Fingerprints the system (IP, hostname, OS, Node version)
- Exfiltrates everything to packages[.]storeartifact[.]com/jpd.php
- Data sent via HTTP GET → HTTP POST → WebSocket (redundant exfil)
Wave-1 Scale
- 200+ malicious packages (126 initially reported, 83 more found by Sonatype)
- 86,000+ downloads
- 14+ attacker-controlled npm accounts using sequential emails: jpdtester01@hotmail.com through jpdtester13@gmail.com
- C2: packages.storeartifact.com / 54.173.15.59:8080 (AWS EC2, us-east-1)
Linking the waves using infrastructure and operational fingerprints
All four waves share a remarkably consistent infrastructure pattern. The correlation was immediate and unmistakable: same actor, rotating infrastructure, same payloads.\
All four domains contain the word "artifact.” a deliberate choice to appear as a legitimate package artifact server. The naming convention (storeartifact → jpartifacts → storeartifacts → artifactsnpm) shows the attacker maintaining thematic consistency while rotating domains to avoid blocklists.
Every domain was registered through Amazon Registrar with the same Identity Protection Service proxy, AWS Route53 nameservers, and zero TLS certificates (all C2 communication is plaintext HTTP). The identical WHOIS fingerprint across four domains registered over seven months provides strong evidence of a single operator using the same AWS account.
The Wave-2 C2 domain was registered on November 4, 2025, just 6 days after Wave-1 was publicly disclosed on October 29. Wave-3 infrastructure was registered on February 13, 2026, three days after the last Wave-2 package. Wave-4 appeared just one day after the last Wave-3 package as the attacker was racing to stay ahead of takedowns.
The attacker changed npm-facing metadata to “MAX” / “ZOD” in Wave-4, but never updated the C2 tarball, which still contains "author": "JPD" across all waves,- including Wave-3 (confirmed via recovered payload) and Wave-4. This alone confirms the same operator is behind all waves.
What changed across waves
While the core technique and payload remain identical, each wave introduces notable changes in operational security.
Changes in attacker OpSec
1. Email pattern evolution
Wave-1 used a mix of email providers:
jpdtester01@hotmail.com → jpdtester13@gmail.com (hotmail, outlook, gmail — scattered)
Wave-2 onwards standardized on Outlook, but with evolving prefixes. All contain "jpd" or "dharsh" in some variation:
- Wave-2: jjjpd01@outlook.com → jjjpd20@outlook.com (early batch), then dharshanjp*@outlook.com, jpddharsh*@outlook.com (later expansion)
- Wave-3: dharshanjp*@outlook.com, jpdplugin*@outlook.com
- Wave-4: jpdplugin12@outlook.com → jpdplugin15@outlook.com
2. Account naming evolution
The pluginjpd account series bridges Wave-3 and Wave-4: numbers 01–11 in Wave-3, 12–15 in Wave-4.
3. npm package metadata evolution
Every npm package has metadata fields — description and author — that are visible on the npmjs.com package page. These are set by the attacker when publishing the package. Separately, the C2 tarball (the malicious payload fetched from the attacker's server) contains its own package.json with an author field.
The npm-facing metadata changed in every wave — the attacker rotated the description and author strings visible on npmjs.com, switching to "MAX" / "ZOD" in Wave-4. But the author field inside the C2 tarball was never updated and still reads "JPD" across all waves, including Wave-3 (confirmed via recovered payload) and Wave-4.
4. Publication cadence
Wave-2 started slow and accelerated over 3 months:
- Nov 13 2025: First package (transform-react-jsx)
- Dec 15 2025 – Jan 30 2026: Sporadic batches (dharsh/dharshjpd accounts)
- Feb 5 - 10 2026: Rapid-fire jpdnpmjpd series - 20 packages in 5 days
Wave-3 was even faster: 34 packages in 5 days (Feb 13–17), escalating from 2/day to 12/day.
Wave-4 published all 4 packages in a single day (Feb 18), the day after the last Wave-3 package.
Code changes: what's different in the payload
We obtained Wave-1 RDD payloads (@dtpk-cc/components, airbnb-flow, unused-imports) and performed byte-level diffs against the live Wave-2 RDD payload (from http://npm.jpartifacts.com/npm/jam3), a recovered Wave-3 RDD payload (`google-camelcase`, obtained from an archived tarball) and the live Wave-4 RDD payload (from http://npm.artifactsnpm.com/npm/sort-export-all).
The payload code is identical across all waves except for two lines - the C2 URL.
Wave-1 (lines 162–163):

Wave-2 (same lines - only the domain changed):

Wave-3 (domain changed AND PHP endpoint renamed for the first time)

Wave-4 (new domain, same renamed PHP endpoint):

C2 endpoint variants
Change in the npm-published package
While the RDD payload code is identical, the npm-published package did change. Wave-2 lists the RDD in both dependencies and devDependencies:

This ensures the malicious dependency is fetched regardless of how the package is installed — whether as a production dependency or a development dependency (e.g., npm install --production vs npm install).
What stayed the same
With primary-source samples from all four waves, we can confirm: 257 of 259 lines in the payload are byte-for-byte identical. The only difference is the C2 URL on lines 162–163. Everything else remains unchanged:
- The four email harvesting sources (`.gitconfig`, `.npmrc`, environment variables, and `package.json`)
- The 20 CI/CD variables (GitHub Actions, GitLab CI, Jenkins, and CircleCI)
- The system fingerprinting fields
- The triple-redundant exfiltration chain (GET + POST + WebSocket)
Additional constants include:
- The `wss://yourserver.com/socket` WebSocket placeholder
- he `api64.ipify.org` public IP lookup service
- The spoofed Windows Chrome User-Agent
- The outer `Hello, world!` decoy
- The `"JPD"` author in every C2 tarball
- The dependency trio (`axios ^1.7.9`, `node-fetch ^3.3.2`, `ws ^8.18.0`).
Technical analysis
The trojan package
Every package published to npm is identical in structure: a harmless "Hello World" script and a package.json with a hidden URL dependency:

When a developer runs npm install jam3, npm sees the HTTP URL dependency and fetches a tarball from the attacker's server. The package on npm has zero malicious code; it's entirely in the RDD.
The RDD payload
The C2 returns a tarball containing two files. The package.json triggers automatic execution via the preinstall hook and the index.js (259 lines) is the full malicious payload.

1. Developer Email Harvesting
Reads developer emails from four sources - environment variables, ~/.gitconfig, ~/.npmrc, and the local package.json author field:

2. CI/CD Pipeline Reconnaissance
Harvests environment variables from four major CI/CD platforms. This tells the attacker exactly which build pipelines the victim uses:

3. System Fingerprinting + Public IP Lookup
Builds a complete profile of the victim machine, including a call to api64.ipify.org to learn the public IP:

4. Exfiltration, Stealth, and Victim Lookup
All harvested data is sent twice, once as URL parameters (GET), once as a JSON body (POST) with a spoofed Windows Chrome User-Agent. If both fail, a WebSocket fallback is attempted. This triple-redundant approach maximizes exfiltration success across diverse network environments - GET bypasses POST-only inspection, POST handles URL length limits, and WebSocket evades HTTP-layer firewalls.
The WebSocket URL (wss://yourserver.com/socket) is a placeholder - not yet wired to a real server, suggesting the attacker plans to deploy a WebSocket-based C2 in the future.
The code below also reveals the payload's stealth mechanism. The fetch() calls execute unconditionally - data is always exfiltrated. But all console.log and console.error calls are wrapped in if (!isPreinstall), meaning during npm install the victim sees zero terminal output while their data is silently sent to the C2.

Victim database lookup: After exfiltration, the payload calls lookupByEmail(primaryEmail), which queries the C2's lookup endpoint with the victim's harvested email. The endpoints object defines two keys — log for data collection and lookup for querying — and at runtime the lookup URL resolves to something like jpd.php?action=lookup&email=victim@example.com. This reveals the C2 is more than a one-way log collector — it maintains a queryable database that can cross-reference victims across packages. Since this lookup only runs outside of preinstall context, it likely serves as the attacker's own tool to verify data was stored successfully.

C2 server analysis
The live C2 servers (Wave-2 and Wave-4) both run Apache/2.4.58 on Ubuntu with only ports 22 (SSH) and 80 (HTTP) open - no HTTPS. The PHP endpoints return structured JSON confirming successful data logging:
Wave-2 C2 response to exfiltration (`jpd.php`):

This confirms the C2 backend is a PHP application with a structured API that stores and queries victim data.
Wave-4 C2 response to exfiltration (`npm.php`):

Both endpoints return the same structured JSON API — confirming the C2 backend is a PHP application that stores and queries victim data. The Wave-4 response shows all 14 data fields being logged successfully.
Captured Wave-4 exfiltration data (from pcap):

The exfiltration format is identical across all three waves: same 12 data fields, same dual GET+POST method, same spoofed User-Agent, same WebSocket fallback. Only the C2 domain and PHP endpoint name differ.
Slopsquatting
All three waves continue the slopsquatting strategy first seen in Wave-1, targeting package names that legitimate projects would plausibly use but that are not actually claimed.
Wave-2 targeting categories
Wave-3 shifted heavily to Babel plugins
Wave-4 continued the pattern
A notable evolution: Wave-2 had a heavy focus on GraphQL Codegen names (betting developers type the package name without the @graphql-codegen/ scope prefix), while Wave-3 pivoted almost entirely to Babel plugin names. Wave-4 targeted import/export utility names. The attacker is systematically working through different popular package namespaces.
Conclusion
PhantomRaven is not a sophisticated zero-day. Across multiple waves, the attacker reused the same technique, payload structure, and infrastructure patterns while only rotating superficial details such as domains, endpoints, dependency names, and npm accounts. The malicious functionality was delivered using Remote Dynamic Dependencies (RDD), where the published packages appeared benign while the real payload was fetched from external attacker-controlled servers during installation.
Because the malicious code never existed directly in the package, traditional scanners looking for obvious indicators such as obfuscation, suspicious `eval()` usage, or known bad URLs could easily miss it. Detecting campaigns like PhantomRaven requires looking beyond individual packages and following the external dependency chain and infrastructure patterns.
By combining package analysis with threat intelligence, we can correlate attacker behavior across waves and identify recurring patterns even when surface indicators change. This approach improves detection accuracy and enables more proactive protection for customers against evolving open source supply chain threats.
Detect and block malware



What's next?
When you're ready to take the next step in securing your software supply chain, here are 3 ways Endor Labs can help:

.jpg)








.avif)