PhantomRaven: the npm malware that collects bug bounties

CrowdStrike has reconstructed an npm campaign that uses remote dependencies declared as URLs to keep the malicious code out of the registry, steals CI/CD secrets and then monetises in a novel way: the attacker uses those secrets to find vulnerabilities and takes them to bug bounty programmes, collecting from at least nine companies. The code, with high confidence, was written with a model.

CybersecurityAIOpen SourceCybersecuritySupply chainnpmAIBug bountyCI/CDSBOMOpen SourceCrowdStrike
Contents
  1. The technique: the dependency is not in the registry
  2. What it steals
  3. The monetisation, which is the novel part
  4. The model’s signature, to be read with care
  5. What has already changed and what is on you
  6. What we think
  7. Sources
Four figures on the PhantomRaven npm campaign reconstructed by CrowdStrike
Figures from the CrowdStrike analysis. Sources at the end.

CrowdStrike Counter Adversary Operations has published its analysis of PhantomRaven, a JavaScript information stealer distributed through npm. The campaign had already been flagged by Koi Security and DCODX in late October 2025 and picked up by other researchers across later waves.

The new part is not the malware. It is how it gets monetised: the attacker uses the stolen secrets to find vulnerabilities inside the companies hit, then takes those vulnerabilities to official bug bounty programmes and collects the reward. CrowdStrike writes that it has been paid by at least nine entities across technology, retail and hospitality.

The technique: the dependency is not in the registry

The mechanism is the part worth understanding properly, because it explains why analysis tools were not seeing it.

The packages published on npm typosquat plausible names and contain harmless code: CrowdStrike describes a simple Hello, world! script. Anyone inspecting the published content would find nothing. The trick sits in package.json, where dependencies are declared as HTTP URLs rather than as references to the npm registry. On install, npm fetches that dependency from a server the attacker controls, and the real payload arrives from there.

Completing the chain is a preinstall script, which runs by itself during installation without anyone invoking it.

Two packages named in the analysis are transform-jsbi-to-bigint, published by the account jpdhellonpm1, and sort-imports-es6-autofix, published by jpd15.

The point that interests us most is a consequence the analysis does not stress but which matters to anyone building their own compliance chain: a dependency declared as a URL does not land in the lockfile as a registry package, so it does not enter the SBOM generated from it. The dependency inventory, which since 11 September the Cyber Resilience Act makes the pivot of everything and which from 2027 will be mandatory in the technical documentation, here shows a clean package with a dependency it cannot describe. This is not a defect of the SBOM: it is a limit on what an SBOM measures, and it should be known before treating one as a guarantee.

What it steals

The list is the one typical of an attacker aiming at development and build environments rather than end users: operating system type, architecture, hostname, IP addresses, process details, NodeJS version, username and email taken from the git and npm configurations, and above all the CI/CD environment variables of GitHub Actions, GitLab CI, Jenkins and CircleCI.

For the public IP address the malware queries api64.ipify.org. Exfiltration goes over HTTP, with both GET and POST requests and with an alternative WebSocket channel left unfinished.

Everything else rides on those environment variables. A pipeline token is the key that opens the registry, the repository and often the deployment environments: it is the same lesson as the PyPI chain inside Anthropic’s evaluations, where the party that lost its credentials was a security vendor’s scanner.

The monetisation, which is the novel part

Here the story changes genre. The attacker does not resell the stolen data: CrowdStrike notes it never appeared on stealer log markets, and infers that the stealer serves only to identify bug bounty opportunities.

The cycle runs like this. The stolen secrets open access to the company’s assets. Inside those assets the attacker looks for vulnerabilities. The vulnerabilities found are then submitted as legitimate discoveries to bug bounty programmes, on the platforms the analysis lists: Bugcrowd, Intigriti, YesWeHack, HackenProof and HackerOne. At least nine companies paid.

It is worth pausing on what that means. A bug bounty programme exists to buy information about vulnerabilities from people who find it legitimately, and it works because payment makes disclosure more attractive than exploitation. Here that mechanism becomes the collection channel for access obtained by theft: the defensive institution ends up paying the proceeds of the attack, and it does so without being able to tell the case apart from an honest report, because the vulnerability reported is real.

It is the same shape we stopped on over the Anthropic incidents, where the defensive practice of scanning new packages had become the vector. The step further here is that the defence becomes the source of revenue and not merely the conduit.

For whoever runs a programme, the practical consequence is that the provenance of the access becomes a question to ask. A valid report says nothing about how the researcher reached the system, and the rules of engagement of many programmes do not require showing it.

The model’s signature, to be read with care

CrowdStrike states with high confidence that the code was written with the help of a language model. Three pieces of evidence are offered: the statistical analysis of token patterns, the redundant comments on every global variable and on every function, the placeholder code left in the finished product. The most legible example is the fallback WebSocket channel, pointing at wss://yourserver.com/socket, an example address never replaced. An unusual design choice is also noted, namely exfiltrating the same data through both POST and GET.

On this kind of conclusion the caution we have applied elsewhere holds. Establishing the author of a text from how it is written is an inference rather than a measurement: redundant comments and forgotten placeholders existed long before the models, and statistical token analysis yields a probability rather than an identification. The claim is stated by CrowdStrike with its own confidence level, and it is right to report it that way, with the level attached.

What can be observed without inference is that the code works and that the campaign has been running for years: the activity dates back to November 2022.

What has already changed and what is on you

Part of the defence came from the registry. npm 12, released in June 2026, blocks by default the scripts that run during installation, warns and requires explicit approval. Anyone up to date has already defused the part that fires on its own.

The rest are your decisions, and CrowdStrike lists some worth reporting for what they are, ordinary measures that almost nobody applies in full.

  • Run installs with --ignore-scripts as the rule, not as the exception, re-enabling scripts only for the packages that genuinely need them.
  • Put a private registry between your pipeline and public npm, which is also the only point where a policy on what gets in can be applied.
  • Keep npm updated, because in this case the mitigation is already in the client.
  • Treat dependency confusion training as operational material rather than a slide.

We would add two that follow from the specific technique. Ban dependencies declared as URLs in manifests, or at least flag them in review: they are rare in a healthy project and are exactly what this campaign uses. And treat pipeline environment variables as secrets with an expiry, because that is what the attacker takes away and what opens everything else.

What we think

The technical lesson is in the blind spot. Tools that inspect the contents of a published package see nothing, because the package is clean; the SBOM built from the lockfile does not describe the remote dependency; the malicious code only arrives at install time, from a server the attacker controls and can switch off before analysis. Three layers of verification looking in the wrong place, not because they are badly built but because they measure the artefact rather than the behaviour of the installation.

For anyone working in cybersecurity this is why a static inventory and runtime observation are not alternatives. The first says what you should have installed, the second what actually happened when you installed it. The campaign lives in the space between the two.

The lesson about incentives is more awkward and concerns whoever runs a bug bounty programme. A reward paid for a real vulnerability, found through stolen access, is a payment that funds the intrusion which made it possible. Telling the cases apart requires asking how the system was reached, not only whether the flaw exists, and having a rule for what to do when the answer does not convince.

On the code being model-written, our reading is that it changes little defensively and a good deal on volume. Malware with verbose comments and forgotten placeholders is no more dangerous than hand-written malware. It is faster to produce, though, and two hundred packages published across successive waves is a production cost that was not previously within reach of a single actor.

Sources

Need support?Under attack?Service Status
Need support?Under attack?Service Status