PhantomRaven hid its credential stealer in an npm dependency that scanners never fetch
Koi Security counted 126 npm packages and more than 86,000 installs since August 2025, with 80 still live when it published. npm pulled the stealer from the attacker's host at install time. That put it outside the package a scanner reads.
The Watch · Security desk

What happened
- Koi Security counted 126 malicious npm packages with more than 86,000 combined installs, collecting npm tokens, GitHub credentials and CI/CD secrets from developers.
- Eighty further packages went up across September and October 2025 after npm removed the campaign's first 21 in August, and that later batch evaded detection.
- The published packages contain a hello world script, and the payload is declared as an HTTP URL dependency that npm fetches from the attacker's host at install time.
- npmjs.com and security scanners do not follow those URLs, so the registry and dependency analysis tools report the packages as having 0 dependencies.
Why it matters
- Who's exposed exposure The credentials in reach belong to the build system: a runner installing a transitive dependency executes the payload with whatever publishing and repository access the job was granted.
- What it limits constraint A clean dependency tree and a pinned lockfile stop answering the question being asked, because the URL's contents can change between installs while the manifest stays identical.
- What's now possible capability Server-side control of the payload gives the operator a per-request choice of what to deliver, including a clean period long enough to pass scans; Koi presents that as theoretical.
- Who has to decide decision With 80 packages live at publication, teams are choosing between takedown-and-rotate and waiting, and there is no version bump to apply.
Next story · Security · Oct 6
Checkmarx finds malicious npm package function-flag still installable after 37,000 downloadsConfirmed3 publishers
Koi Security says the HTTP dependency is re-fetched from the attacker's host on every `npm install`, uncached, unversioned and unrecorded in any lockfile [12]. So the contents behind that URL can change between installs while the manifest stays identical, and last week's build and today's can run different code.
Behaviour is what caught this one. Koi's Wings engine flagged packages making outbound requests to one domain during installation, in October 2025 [8]. Static review of the published package finds a hello world script and nothing else [4]. npmjs.com leaves HTTP dependency specifiers unresolved and scanners walk past them, so the registry page reports 0 Dependencies [6].
More than 86,000 installs across 126 packages averages about 683 per package [15]. The published timeline dates 101 of them: 21 removed by npm in August 2025, and 80 uploaded across September and October that got through [9][10][16]. That leaves 25 undated. Eighty were still live when Koi published, 63 percent of the set [11][17].
What Koi observed is install-time traffic to a single domain and credential collection; the company wrote that the packages were "actively stealing npm tokens, GitHub credentials, and CI/CD secrets from developers worldwide" [8][3]. Koi offers the selective-serving scenario as theory: check the requesting IP, return benign code to researchers on VPNs and the payload to corporate networks, or serve clean code for weeks to pass scans and flip later [18].
npm runs the code on its own. The fetched package declares a `preinstall` hook that runs `node index.js`, and npm executes it automatically, unprompted and with no user interaction [13]. It works the same at depth: a package three levels down the tree still runs its preinstall on the machine doing the install [14].
Two controls sit on that chain. Egress filtering on build runners breaks the fetch from `packages.storeartifact.com` [5][12]. Refusing to run lifecycle scripts at install breaks the execution step [13]. Dependency-tree analysis reads an empty tree and sits outside the chain [6].
The operator was careless about identity. Koi lists sequential free-mail accounts from jpdtester01@hotmail.com through jpdtester13@gmail.com, and registry usernames including npmhell and npmpackagejpd [7]. The delivery technique kept working through 80 further uploads after npm took down the first 21 [9][10].
What to watch
- Whether npm changes how it treats HTTP dependency specifiers, or begins resolving them so scanners can see them.
- Whether the 80 live packages come down and whether packages.storeartifact.com stays reachable.
- Whether a second operator reuses remote dynamic dependencies from a different host.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence60
- Adoption35
- Hype gap+20
- Incentives55
- Confidence60
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
Koi Security identified 126 malicious npm packages with more than 86,000 combined downloads, stealing npm tokens, GitHub credentials and CI/CD secrets from developers worldwide.
- [2]
Koi Security named the campaign PhantomRaven.
- [3]
"actively stealing npm tokens, GitHub credentials, and CI/CD secrets from developers worldwide"
- [4]
Opening one of the malicious packages on npm shows harmless source code: a shebang line and console.log('Hello, world!').
- [5]
npm supports HTTP URLs as dependency specifiers, and the campaign used entries such as "ui-styles-pkg": "http://packages.storeartifact.com/npm/unused-imports", which npm fetches from the external URL rather than from npmjs.com.
- [6]
npmjs.com does not follow those URLs, security scanners do not fetch them and dependency analysis tools ignore them, so to automated security systems the packages show "0 Dependencies".
- [7]
The attacker used sequential free-provider email accounts from jpdtester01@hotmail.com and jpdtester02@outlook.com through jpdtester13@gmail.com, with usernames such as npmhell and npmpackagejpd, all apparently controlled by the same person.
- [8]
Koi Security's risk engine, Wings, flagged packages making external network requests during installation, all to the same suspicious domain, in October 2025.
- [9]
The campaign began in August 2025 with its first published packages, and npm detected and removed an initial 21 packages that month.
- [10]
Between September and October 2025, 80 additional packages were uploaded and evaded detection.
- [11]
Eighty of the packages were still active at the time of Koi Security's report, harvesting credentials from developers.
- [12]
Every time someone runs npm install, the remote dynamic dependency is fetched fresh from the attacker's server: not cached, not versioned, not locked.
- [13]
The malicious package fetched via the remote dynamic dependency declares "preinstall": "node index.js", and npm runs lifecycle scripts automatically with no prompt, no warning and no user interaction.
- [14]
The preinstall script executes regardless of how deep in the dependency tree the package sits, including a package depended on by a package depended on by another package.
- [15]
More than 86,000 installs spread across 126 packages is an average of roughly 683 installs per package.
- [16]
The published timeline assigns dates to 101 of the 126 packages (21 removed in August 2025 plus 80 uploaded in September and October), leaving 25 undated.
- [17]
The 80 packages still active at publication are about 63 percent of the 126 in the campaign.
- [18]
Koi Security says that in theory the attacker could check the IP address of every request and serve different payloads, benign code to security researchers on VPNs and malicious code to corporate networks, or return clean code for weeks to build trust and pass security scans before serving the malicious version.
Sources
2 independent publishers whose own reporting we read for this story.
- scworld.comPhantomRaven malware distributed via npm package registry
1 article · September 21, 2026
- Koi Security
web.archive.org
1 article · September 20, 2026