NEWS
Microsoft’s 14 npm Lures Were Built to Steal Tokens
Microsoft pulled 14 typosquatted npm packages published in four hours. The Bun-compiled payload stole npm tokens and cloud CI secrets that survive a takedown.
Microsoft said a single actor published 14 typosquatted npm packages in four hours on May 28, 2026, to steal cloud and CI/CD secrets. The same Bun-compiled harvester also lifted npm publish tokens, which is how a four-hour drop outlives a registry takedown.
The alias was vpmdhaj, a newly created maintainer tied to a39155771@gmail.com. Microsoft Defender Security Research said it fed the cluster to the npm team, and the user and the repos came down. The leftover problem is whatever those installs already copied off build hosts.
Fourteen Packages Landed in Four Hours
Every name in the cluster mimics OpenSearch, Elasticsearch, DevOps, or env-file tooling. Four packages sat under the @vpmdhaj scope. Ten were unscoped lookalikes, several pointing homepage, repository, and bugs fields at github.com/opensearch-project/opensearch-js. Versions jumped to strings such as 1.0.7269, 1.0.9108, and 2.1.9201 so a first-time listing would read as a long release history.
THE VPMDHAJ PACKAGE CLUSTER
| Package | Version | Shape |
|---|---|---|
| @vpmdhaj/elastic-helper | 1.0.7269 | Scoped lure |
| @vpmdhaj/devops-tools | 1.0.7267 | Scoped lure |
| @vpmdhaj/opensearch-setup | 1.0.7267 | Scoped lure |
| @vpmdhaj/search-setup | 1.0.7268 | Scoped lure |
| opensearch-security-scanner | 1.0.10 | Unscoped lookalike |
| opensearch-setup | 1.0.9103 | Unscoped, spoofed repo |
| opensearch-setup-tool | 1.0.9108 | Unscoped, spoofed repo |
| opensearch-config-utility | 1.0.9106 | Unscoped, spoofed repo |
| search-engine-setup | 1.0.9108 | Unscoped, spoofed repo |
| search-cluster-setup | 1.0.9104 | Unscoped, spoofed repo |
| elastic-opensearch-helper | 1.0.9108 | Unscoped, spoofed repo |
| vpmdhaj-opensearch-setup | 1.0.9102 | Unscoped, author-named |
| env-config-manager | 2.1.9201 | dotenv-style lure |
| app-config-utility | 1.0.9300 | generic config lure |
The 14 malicious packages Microsoft identified all shipped the same install-time stager and the same second-stage harvester. Teams that still need paste-ready strings can use the earlier package-by-package lockfile list, then grep package-lock.json, yarn.lock, and pnpm-lock.yaml for each name.
Why the 14 Packages Were Disposable
A typosquat dies when npm deletes it. A stolen publish token does not. Microsoft said the harvester validated npm tokens through /-/whoami and listed publish access through /-/npm/v1/tokens, then treated those tokens as the path into packages the victim already owned.
Stolen npm publish tokens enable downstream supply-chain pivoting, pushing malicious updates to packages owned by hijacked maintainer identities, expanding the campaign beyond the initial 14 packages.
Microsoft Defender Security Research, Microsoft Security Blog
That is the part a registry takedown cannot rewind. Anyone who installed a cluster package on or after May 28, 2026, on a laptop or a runner that held an npm token, an AWS role, a Vault token, or a GitHub Actions credential has a rotation job, even if node_modules is gone. The 14 names were bait for people who type OpenSearch helpers from memory. The payload was built for the secrets those people keep in CI.
The Stager That Downloads a Real Bun Runtime
Every package declared an automatic hook, so the malicious code ran at npm install with no require() from victim code. npm’s own docs describe how npm runs preinstall scripts as a built-in life-cycle step, which is why a lookalike tarball gets code execution the moment a lockfile resolves it.
Microsoft logged two stager generations that drop the same harvester.
GEN-1 VS GEN-2 STAGERS
| Piece | Gen-1 (1.0.7265 and below) | Gen-2 (1.0.7266 and above) |
|---|---|---|
| Hook | install, preinstall, and postinstall | A single preinstall hook |
| Loader | preinstall.js posts host facts to HTTP C2 | setup.mjs runs a bundled stage-2 under Bun |
| Network at install | Beacon to aab.sportsontheweb.net with header X-Supply: 1, then payload.bin | No actor C2; download of Bun v1.3.13 from GitHub Releases if bun is missing |
| Persistence | index.js respawns payload.bin on every require() | Payload ships in the tarball as opensearch_init.js or ai_init.js |
Gen-1 is noisy on a proxy. The JSON beacon carries hostname, platform, arch, Node version, USER, cwd, INIT_CWD, and the npm package name, then the C2 returns a gzipped binary that is chmod 0755’d and spawned detached with __DAEMONIZED=1. Gen-2 is quieter because the only outbound call during install can be a fetch of the official Bun JavaScript runtime, version 1.3.13, from github.com/oven-sh/bun/releases for Linux, macOS, or Windows. Defenders who only alert on odd domains during npm install watch a legitimate GitHub download and miss the bundled stage-2 that already sat in the tarball.
Microsoft Defender Antivirus quarantined setup.mjs at extract time in the lab, before the Bun hop ran. That helps hosts with cloud-delivered protection on. It does not help a CI image that installed the tarball with scripts enabled and no scanner on the runner.
AWS, Vault, and the npm Token Pivot
The second stage is a single-file Bun-compiled JavaScript binary of about 195 KB. Static review found stealers for five places secrets actually live on a cloud build box.
WHAT THE PAYLOAD TRIED TO STEAL
- AWS roles: It queries EC2 IMDSv2 at 169.254.169.254 and ECS task metadata at 169.254.170.2, reads env credentials, calls STS GetCallerIdentity and AssumeRole, and lists Secrets Manager across 16 or more regions with a bundled SigV4 signer.
- HashiCorp Vault: It reads VAULT_TOKEN and VAULT_AUTH_TOKEN from the environment.
- npm registry: It checks tokens with /-/whoami and enumerates publish access, which is the downstream pivot.
- GitHub Actions: It collects GITHUB_REPOSITORY and RUNNER_OS so a build runner is easy to rank against a laptop.
- CI self-defense: It sets __DAEMONIZED=1 to avoid re-entry and resets CI=false so build-aware code paths do not hold back.
Stolen STS sessions and Secrets Manager values open lateral movement in the account that ran the install. Stolen Actions material opens repo writes and workflow edits. Stolen npm publish tokens open the rest of that maintainer’s graph. Microsoft’s IMDSv2 hardening guidance from AWS is the matching control on the cloud side: hop limits, required tokens, and no open metadata from a Node process that should never talk to 169.254.169.254.
Hunt CloudTrail for GetCallerIdentity quickly followed by AssumeRole, and for ListSecrets or GetSecretValue walking regions from a developer IP or a build subnet. On disk, look for payload.bin under node_modules, detached children with __DAEMONIZED=1, and Node processes that fetched oven-sh/bun/releases during install.
Spoofed OpenSearch URLs Did the Social Work
The actor used three cheap trust tricks at once. Lookalike names such as opensearch-setup-tool and elastic-opensearch-helper sit next to real cluster helpers in a search box. Spoofed GitHub URLs make the npm website’s repository link open the genuine OpenSearch JS project. Inflated versions make a 0.0.1-quality drop look like year-three maintenance.
Microsoft said the keyword set points at a developer audience that already holds AWS and Elastic-cloud credentials. That is a tighter bet than spraying crypto-wallet typosquats. An OpenSearch helper on a laptop is a miss. The same helper on a GitHub Actions runner with an instance role and an npm_token secret is the design center.
Public posts after the advisory were mostly copies of Microsoft’s own URL. The cluster never became a mass-download story, because none of the 14 names were hijacked libraries with existing weekly traffic. New packages only bite people who mistype or copy a bad install line. The publish-token path is how a low-download lure still reaches packages that do have traffic.
May Already Ranked as the Busiest Worm Month
vpmdhaj was one campaign in a month that was already running hot. Phoenix Security’s Malware Package Intelligence corpus, covering June 2024 through June 2026, logged 14 campaigns and 346 indexed malicious packages in May 2026, more than the prior four months of 2026 combined. Mini Shai-Hulud Wave 2 produced 226 of those 346. Across the two-year set, npm held 79.3 percent of indexed IOCs, and the corpus assigned zero CVEs during active exploitation.
MAY 2026 IN THE PHOENIX CORPUS
- May campaigns: 14 distinct campaigns, including this typosquat cluster as one of them.
- May packages: 346 indexed malicious package-versions in 31 days.
- Worm share: Mini Shai-Hulud Wave 2 accounted for 226 of those 346.
- Registry mix: npm held 79.3 percent of IOCs across the June 2024-June 2026 corpus.
Microsoft Defender’s own signatures for this cluster include Trojan:JS/ShaiWorm, Trojan:JS/ObfusNpmJs, and Backdoor:JS/SupplyChain. The ShaiWorm label puts the 14 packages in the same family tree as the credential-worm waves that used preinstall, a Bun download from GitHub Releases, and npm-token republish through 2026. StepSecurity later described an August 4 worm it calls ChainDrop that poisoned 444 packages and 2,212 versions, downloading Bun v1.3.13 from the same official releases path and then republishing with stolen npm tokens. The May 28 tarballs were a small, original-name version of that loop.
npm v12 Turns Install Scripts Off by Default
Microsoft’s short-term advice was blunt: identify installs on or after May 28, 2026, pin known-good versions, run npm install --ignore-scripts (and the pnpm and yarn equivalents), rotate AWS, Vault, npm, and Actions secrets, and block aab.sportsontheweb.net plus any HTTP request with X-Supply: 1. That flag still works on older CLIs. The default finally moved under the people who run the registry.
THE CALENDAR AFTER MAY 28
- May 22, 2026: GitHub says staged publishing is generally available, so a publish can sit until a human 2FA approval, which cuts a stolen CI token off from an instant live release on packages that opt in.
- May 28, 2026: vpmdhaj publishes the 14 packages; Microsoft investigates and npm takes the user down.
- June 9, 2026: GitHub announces npm v12 breaking changes, 12 days after the cluster, with the new defaults already warning on 11.16.0 and later.
- July 8, 2026: GitHub tags npm v12 as latest, 41 days after the cluster, and turns the install-time defaults on.
allowScripts defaults to off: Dependency lifecycle scripts (i.e., preinstall, install, postinstall) and implicit node-gyp builds no longer run unless explicitly allowed.
GitHub Changelog, July 8, 2026
GitHub’s note that npm v12 turns allowScripts off also sets –allow-git and –allow-remote to none, and it starts a long deprecation of 2FA-bypass granular access tokens, with direct publish by those tokens aimed at around January 2027. Teams still on npm 11 should upgrade, or keep ignore-scripts on, because Gen-1 and Gen-2 both needed a life-cycle hook. Hosts that already installed the cluster still need token rotation; a default change does not unsay a secret that left the box.
Frequently Asked Questions
Who published the 14 malicious npm packages?
Microsoft attributed every package to a newly created maintainer alias, vpmdhaj, registered with a39155771@gmail.com. The npm profile and the packages were taken down after Microsoft’s feedback to the npm team, so current registry lookups should fail; lockfiles and build caches are the place those names still hide.
What file hashes identify the vpmdhaj stagers?
Microsoft published SHA-256 638788AFC4F1B5860A328312CAF5895ABD5F5632D28A4F2A85B09076E270D15D for the Gen-1 preinstall.js, 77D92EFE7AF3547F71FD41D4A884872D66B1BE9499EAA637E91EAC866911694D for the Gen-2 setup.mjs, and BFA149694EC6411C23936311A999163ADE54D6F38E2F4B0E3CFB8CB67BD7CFAA for payload.gz, the gzipped Bun stage-2. Matching any of those on disk is enough to start credential rotation, even if the npm name is already gone from the registry.
Does npm v12 stop this attack on older Node installs?
Only if the npm CLI itself is v12, or 11.16.0-plus with the new allowlist committed. Node version is not the switch. Microsoft also told operators to set ignore-scripts for pnpm and yarn, because those clients have their own install-script defaults and a v12 npm binary on one image does not protect a yarn runner beside it.
Which network indicators should be blocked after a suspected install?
Microsoft listed aab.sportsontheweb.net (and sportsontheweb.net) as Gen-1 C2, with the beacon and payload on http://aab.sportsontheweb.net/x.php over port 80, plus the campaign header X-Supply: 1. Gen-2 may never hit that host; hunt instead for Node fetching github.com/oven-sh/bun/releases/download and for later traffic to 169.254.169.254 or 169.254.170.2 from node or bun.
The 14 packages are gone from the registry. Any AWS role, Vault token, npm publish token, or Actions secret that sat on a host that installed them between May 28, 2026, and the takedown still has to be treated as copied, because that was the harvester’s job.
-
NEWS4 months agoWarzone Leaves Xbox One and PS4 After Season 06
-
NEWS3 months agoMicrosoft AI Was Set Free to Build Its Own Frontier
-
MICROSOFT 3653 months agoMicrosoft IQ Turns Workplace Data Into a Metered Agent Brain
-
NEWS3 months agoXbox Games Showcase 2026 Split the Catalog in Two
-
MICROSOFT 3653 months agoNadella Banned Addiction Talk While Scout Kept Heartbeat
-
NEWS4 months agoModern Warfare 4 Splits Its Audience Before the October Launch
-
NEWS3 months agoInfinity Ward Bets Modern Warfare 4 on a Paid DMZ
-
NEWS3 months agoDragonwilds Hits Xbox, but Steam Saves Stay Put
