Skip to content

Security Independently confirmed2 publishers 2 min read Published Updated

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

Send a correction

Illustration accompanying PhantomRaven hid its credential stealer in an npm dependency that scanners never fetch
Generated illustration

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.

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
Why these scores

Claim ledger

Ranked by verification strength, evidence, and original report placement.

  1. [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.

    Reported Supported Source: Koi SecurityView cited source
  2. [2]

    Koi Security named the campaign PhantomRaven.

    Reported Supported Source: Koi SecurityView cited source
  3. [3]

    "actively stealing npm tokens, GitHub credentials, and CI/CD secrets from developers worldwide"

    Reported Supported Source: Koi Security, in its published writeupView cited source
  4. [4]

    Opening one of the malicious packages on npm shows harmless source code: a shebang line and console.log('Hello, world!').

    Reported Supported Source: Koi SecurityView cited source
  5. [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.

    Reported Supported Source: Koi SecurityView cited source
  6. [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".

    Reported Supported Source: Koi SecurityView cited source
  7. [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.

    Reported Supported Source: Koi SecurityView cited source
  8. [8]

    Koi Security's risk engine, Wings, flagged packages making external network requests during installation, all to the same suspicious domain, in October 2025.

    Reported Supported Source: Koi SecurityView cited source
  9. [9]

    The campaign began in August 2025 with its first published packages, and npm detected and removed an initial 21 packages that month.

    Reported Supported Source: Koi SecurityView cited source
  10. [10]

    Between September and October 2025, 80 additional packages were uploaded and evaded detection.

    Reported Supported Source: Koi SecurityView cited source
  11. [11]

    Eighty of the packages were still active at the time of Koi Security's report, harvesting credentials from developers.

    Reported Supported Source: Koi SecurityView cited source
  12. [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.

    Reported Supported Source: Koi SecurityView cited source
  13. [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.

    Reported Supported Source: Koi SecurityView cited source
  14. [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.

    Reported Supported Source: Koi SecurityView cited source
  15. [15]

    More than 86,000 installs spread across 126 packages is an average of roughly 683 installs per package.

    Derived Supported
  16. [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.

    Derived Supported
  17. [17]

    The 80 packages still active at publication are about 63 percent of the 126 in the campaign.

    Derived Supported
  18. [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.

    Reported Insufficient Source: Koi Security, stated as a theoretical capabilityView cited source

Sources

2 independent publishers whose own reporting we read for this story.

  1. scworld.com

    1 article · September 21, 2026

    PhantomRaven malware distributed via npm package registry
  2. web.archive.org

    1 article · September 20, 2026

    Koi Security

Topics and entities