Checkmarx has published research on an npm campaign it calls MALFEX, running from August 2023 to the present. Twelve packages were published, eight of them malicious, delivering the Overlord RAT and an information stealer.

Together they passed 40,000 downloads. One package, function-flag, accounts for more than 37,000 of those on its own.

As of 1 October, five of the packages had been removed. Three were still installable.

Three ways in, all of them ordinary

The campaign used three delivery paths, and none of them is clever.

The first puts loaders for the remote access trojan behind obfuscated scripts that run during npm install. That is the one worth repeating to anyone who has not thought about it: a package's install script executes before a developer has read a single line of what they just pulled in. The decision to trust is made after the code has already run.

The second triggers when the package is loaded rather than installed, deploying a Node.js stealer that goes after Discord clients, browsers and cryptocurrency wallets.

The third is the one that lasted longest. Each new version of function-flag shipped a different downloader, fetching its payload from a different location. Nothing about the package's behaviour stayed still long enough to be fingerprinted.

Three years is the finding

A campaign surviving three years in a public registry invites the assumption that it must have been sophisticated. It was not.

What it had was the gap between removal and knowledge. Checkmarx notes that only six packages have OSV advisories, with limited coverage of the multiple malicious versions of one of them. OSV is the open vulnerability database that a great deal of dependency scanning reads from. A malicious package with no advisory is, to most tooling, a package.

So the lifecycle looks like this. The package is published. It is downloaded for years. Eventually somebody notices and the registry removes it. The advisory either never appears or covers one version of several. And the scanners running in thousands of pipelines report nothing, because they are asking a database that was never told.

We have now written this same sentence three times in a fortnight from three directions. Unsloth fixed a code-execution flaw and declined an advisory, so no scanner will ever raise it. GlassWorm's extensions were delisted, which does not uninstall them from anybody's machine. And here, the packages were removed and the record of why is partial.

The registry is a distribution channel. The advisory database is the part that reaches the installed base, and it is the part that keeps being incomplete.

What 37,000 downloads means and does not

The number is large and it is not a count of victims.

Package download figures include mirrors, caches, continuous integration runs pulling the same dependency on every build, and automated scanners. A single project with a busy pipeline can account for thousands on its own. Nobody should read 37,000 downloads as 37,000 compromised developers.

What it does establish is reach: the package was in enough dependency trees, for long enough, to be fetched tens of thousands of times without anyone raising a hand. And given the payloads — a remote access trojan, and a stealer aimed at browsers and wallets — the machines that did run it were developer machines, which is the access worth having.

What to do

  • Check whether function-flag, function-color or cdn-img-fetch appear anywhere in your dependency trees, including transitively and including lockfiles for projects nobody builds any more.
  • Turn off install scripts by default. npm supports it, and it removes the entire first delivery path at the cost of some friction with packages that genuinely need to compile.
  • Do not treat a clean scan as an answer on its own. If your tooling reads OSV, it knows what OSV knows, and this campaign is a demonstration of the difference.
  • If you find one of these, rotate before you clean. Browser sessions, cloud tokens, registry credentials and wallet material, in that order.

What is not established

  • How many machines actually ran the payloads, as opposed to how many times the packages were fetched.
  • Who operates MALFEX, which the research does not attribute.
  • Why three of the packages remained installable, and whether that has changed since 1 October.
  • What was taken from affected developers over three years.