InnoCTF
supply-chain Bug-class explainer

How a typosquatted npm package ships a Windows RAT through a single install

Abstract art of a lookalike npm package name resolving into a multi-stage Windows payload
A lookalike dependency name resolving into a multi-stage install-time payload.

A package named postcss-minify-selector-parser borrows the keyword space of a library that does over 127 million weekly downloads, then uses the npm install lifecycle to decrypt a dropper, write a PowerShell stager, and land a Windows remote access trojan. The cipher is fine. The trust model is the bug.

Researchers disclosed a small cluster of malicious npm packages published over the past month under a single account, including aes-decode-runner-pro, postcss-minify-selector, and postcss-minify-selector-parser. Download counts were modest, in the low hundreds each, but the targeting is precise: front-end and build-tooling developers on Windows. This is a textbook combination of two bug classes, dependency-name impersonation and install-time script abuse, stitched into one chain.

The name is the lure

The legitimate postcss-selector-parser is a transitive dependency of much of the PostCSS ecosystem. The impostor reuses the same tokens, postcss, selector, parser, css, so it surfaces in registry search and reads as plausible during a quick dependency review. It even declares the real package as a dependency, so installing the fake also pulls the genuine one and the install log looks ordinary. Typosquatting works because dependency names are matched by humans skimming, not by signatures.

A name that is one keyword away from 127 million weekly downloads is not a coincidence. It is the payload's first stage. recurring pattern across npm, PyPI, and RubyGems

The chain, stage by stage

The interesting part runs at install time, before any of your code imports the module. The package carried a large encrypted blob alongside an AES-256-GCM decoder. On install the decoder unpacked the blob in memory, and the result acted as a dropper: it wrote a PowerShell script to disk and executed it. That script reached out to a domain dressed up as a driver site, nvidiadriver[.]net, and pulled the next stage.

The final payload is a full Windows RAT. Reporting describes encrypted HTTP command-and-control, registry-based persistence, sandbox and VM detection via WMI and MAC-address checks against VMware, VirtualBox, and QEMU, a remote shell, file transfer, and Chrome credential theft through DPAPI. The staging keeps each piece small and defers the heavy logic to a server the attacker controls, a pattern sometimes called remote dynamic dependencies: the package itself looks thin, and the real code arrives only at install.

package.json (illustrative, reconstructed shape)
{
  "name": "postcss-minify-selector-parser",
  "version": "1.0.3",
  "dependencies": {
    "postcss-selector-parser": "^6.0.0"   # pulls the real one, for cover
  },
  "scripts": {
    "postinstall": "node ./lib/runner.js"  # decrypts blob, drops + runs PS1
  }
}

The lesson generalizes. A postinstall or preinstall hook that runs a local script, spawns a shell, or fetches a remote resource is executing arbitrary code on the developer's machine with the developer's privileges and tokens. The same shape recurs in update-channel compromises like the ShapedPlugin backdoor: a trusted automated step becomes the delivery mechanism.

Detecting it in a lab

Reproduce the recognition skill, not the malware. Spin up a disposable Windows VM with no real credentials, then practice spotting the markers before you ever run an install.

Defending real projects

Treat dependency installation as a trust boundary, the same way you would an untrusted network request. The crossing here is the moment npm runs someone else's lifecycle script on your host.

None of this requires new cryptography or a novel exploit. It is the predictable outcome of a registry that runs publisher-supplied code at install time and a review process that matches names by eye. The defender's habit worth building is to read every install hook as if it were a remote shell, because for one install, that is exactly what it was. For more on trusted layers turning into entry points, see when the perimeter becomes the payload, and the project ethics and disclosure policy for how we handle live-campaign reporting.

Disclosure: This article was researched and drafted with AI assistance and edited by the InnoCTF Editorial Team. It summarizes publicly reported findings for education and authorized testing only. Package names and indicators are reproduced as published; do not install the named packages outside an isolated lab.

Sources

  1. The Hacker News. "Malicious npm Packages Pose as PostCSS Tools to Deliver Windows RAT." Read the report
  2. Infosecurity Magazine. "Lookalike npm Package Hides a Multi-Stage Windows RAT." Read the article
  3. Cyber Security News. "Windows RAT Uses Encrypted HTTP C2 and Registry Persistence After npm Infection." Read the analysis
  4. npm Docs. "scripts and the package lifecycle (preinstall/postinstall)." Read the documentation