ViteVenom: 7 fake-Vite npm packages pull a RAT from a blockchain C2
Contents
TL;DR
What happened Checkmarx flagged 7 malicious Vite-lookalike npm packages as ViteVenom: @uw010010/vite-tree, @vite-tab/tab, @vite-ln/build-ts, @vite-mcp/vite-type, @vite-pro/vite-ui, @vitets/vite-ts, @vite-ts/vite-ui. Affected: dev machines and CI that installed one and ran its bundled CLI
Trigger The malicious code lives in bin/vite.js. It runs when you execute that CLI via package.json bin — not at install time and not on a normal package import. It uses Tron, Aptos, and Binance Smart Chain as a C2 (the attacker’s command-and-control / config-retrieval path) to fetch a RAT (remote access trojan) in stages
What to do Remove the packages, then check lockfiles, internal npm proxies, and CI caches; look for traffic to 198.105.127[.]210, suspicious appends to .bashrc / .zshrc / .profile, and exposure of GitHub / npm / cloud / LLM API keys
Checkmarx Zero reported 7 malicious npm packages targeting the Vite ecosystem, naming the campaign ViteVenom.
The Hacker News covered it on July 17, 2026.
The names lean on the legitimate @vitejs/* and @vitest/* scopes — think @vite-pro/vite-ui, @vite-ts/vite-ui, @vitets/vite-ts.
In a package.json where Vite itself and Vitest are already common, these names blend into the dependency list without looking out of place.
As I covered in the Vite 8.0 article, Vite sits at the entrance of the frontend build.
React plugins, TypeScript transforms, the Rolldown migration, Vitest integration — a lot of surrounding packages, a lot of plausible names.
The attacker parked names there that aren’t the official scope but read like the official scope.
The 7 packages Checkmarx listed:
| Package | Malicious versions per Checkmarx | Downloads per Checkmarx |
|---|---|---|
@uw010010/vite-tree | 3.4.2, 3.4.3, 3.6.1 | 1,070 |
@vite-tab/tab | 3.15.10 | 289 |
@vite-ln/build-ts | 5.15.10 | 252 |
@vite-mcp/vite-type | 6.44.1 | 239 |
@vite-pro/vite-ui | 2.5.10 | 200 |
@vitets/vite-ts | 1.5.10 | 194 |
@vite-ts/vite-ui | 6.44.1 | 176 |
This table lists the known versions Checkmarx published on July 14 and does not cover every version. Inspecting the @vite-tab/tab@5.7.0 tarball on July 19, 2026, the bin/vite.js published July 7 carried the campaign marker *5-7 and a different obfuscated self-executing block. Because compromised versions exist beyond Checkmarx’s reported 3.15.10, remove the 7 at the package level rather than pinning to the table’s versions.
Checkmarx says it reported the 7 to npm on July 3, 2026, and that they were removed the same day.
But rechecking the npm registry on July 19, 2026, only @vite-ln/build-ts had been replaced with 0.0.1-security, and the malicious 5.15.10 returned 404. For the remaining 6, the metadata and tarballs for the malicious versions above were still retrievable.
“All 7 removed” doesn’t match the current registry state. OpenSourceMalware likewise notes some remain on npm while others were removed or replaced with a security placeholder. Don’t assume removal — reconcile lockfiles, internal npm mirrors, and CI caches against the npm registry.
Not @vitejs, but @vite-pro and @vite-ts
The legitimate Vite project publishes packages under the @vitejs/* scope, like @vitejs/plugin-react.
ViteVenom did not hijack that scope directly.
It created new, nearby scopes: @vite-pro/*, @vite-ts/*, @vite-mcp/*, @vitets/*.
Not all 7 scopes resemble @vitejs.
In Checkmarx’s classification, @vite-pro, @vite-ts, and @vite-mcp evoke @vitejs, and @vitets is one character off @vitest. @vite-tab and @vite-ln just read like generic Vite tooling, and @uw010010 has no direct impersonation target in the scope itself — @uw010010/vite-tree leans on Vite in the package name instead.
This is close to the Rollup polyfill-lookalike npm case.
There, the package mirrored the README and vocabulary of the legitimate rollup-plugin-polyfill-node.
Here too, instead of a one-character variant of the official scope, the attacker builds separate scopes out of Vite’s surrounding vocabulary.
Simple typosquat (fake packages made to resemble a real name) detection keys on similarity to the legitimate name, so this shape slips past it.
@vitets reads like @vitest with the last character dropped, and the package name vite-ts looks like TypeScript tooling.
In the Vitest API server RCE article, I covered a vulnerability where Vitest’s development API could execute code on a local machine.
This is malicious packages rather than a vulnerability, but both share a property: a “dev-time only” tool reaching the credentials on a developer machine.
bin/vite.js pulls the next stage from a blockchain
In Checkmarx’s analysis, all 7 packages carry the same malicious block in bin/vite.js, differing only in the campaign marker.
The code renames require and module to short aliases like global.r and global.m, and pulls strings out by array index.
Static scanning for child_process or URLs doesn’t surface the values that matter.
The trigger flow differs from install-script malware.
Instead of executing immediately in preinstall or postinstall, the Vite-style bin/vite.js reaches for the next stage when it runs. In the package.json inside the tarballs still on npm, the malicious versions set main to ./dist/node/index.js and bin mapped vite or vite-tree to bin/vite.js. There’s no reference from the normal import entry to the malicious file, so “triggers on import” is wrong — “triggers when you run the bundled CLI” is accurate.
Checkmarx confirms ViteVenom delivers the same 77KB RAT as ChainVeil. That RAT exits early — no C2 connection, no exfiltration — if process.env.CI is set, or if the hostname is localhost or RUNNERADMIN. Under a standard CI the final stage may stop, but the loader itself still runs, and there’s no guarantee the condition always holds on self-hosted runners, so this isn’t grounds to treat CI as harmless.
flowchart TD
A["Developer adds a fake-Vite package"] --> B["npm install<br/>install script is not the entry point"]
B --> C["Run the bundled CLI<br/>bin/vite.js is evaluated"]
C --> D["Stage 2A<br/>fetch a BSC transaction hash<br/>from Tron, Aptos on failure"]
D --> E["XOR-decrypt BSC input and eval<br/>fetch RAT via shared Tier-2"]
C --> F["Stage 2B<br/>fetch a BSC transaction hash<br/>from a different Tron, Aptos on failure"]
F --> G["XOR-decrypt BSC input and<br/>run via detached node -e"]
G --> H["Fetch RAT directly from<br/>198.105.127[.]210 /$/boot"]
E --> I["RAT launches<br/>reverse shell and credential theft"]
H --> I
With a blockchain C2, the thing you’d want to block is not a domain or a single server but transaction data spread across multiple chains.
The attacker has the loader pull a BSC transaction hash from Tron transaction data, then decrypt the encrypted payload (the malicious code itself) embedded in the BSC input.
If Tron fails, it uses the same BSC hash placed in the recipient of a zero-value Aptos transfer.
Stage 2A follows a shared Tier-2 from the decrypted config. Stage 2B runs the loader obtained via a different Tron/Aptos/BSC route through a detached node -e, and pulls the RAT directly over HTTP from 198.105.127[.]210 at /$/boot. The HTTP path is not a simple fallback tried right after Stage 2A — it’s Stage 2B’s own route.
With a normal C2, taking down the domain, stopping the hosting, or blocking the IP kills most of it.
Here, the pointer to the payload stays on a public blockchain.
You can’t delete the wallets or the transactions, so the defensive side has to follow the API access, the post-decryption traffic, and the RAT’s behavior.
Checkmarx assesses ViteVenom and June’s ChainVeil as the same SuccessKey actor’s lineage.
The basis is that the Tier-2 Tron wallet, Aptos account, XOR key, and final RAT all match.
Checkmarx itself notes it can’t technically rule out a service model that rents shared infrastructure to multiple actors.
This stays at “likely the same operator.”
On July 17, 2026, OpenSourceMalware tied ChainVeil/ViteVenom to PolinRider, linked to North Korea’s Lazarus Group. The basis is that ChainVeil’s two Tron wallets, Aptos address, and two XOR keys exactly match IoCs the same organization published for PolinRider in March. The Hacker News added this view in its July 19 update. This doesn’t contradict Checkmarx’s “SuccessKey” tracking name — it’s a different researcher assessing a name and nation-state attribution for a broader campaign that includes both. The Checkmarx source makes no nation-state attribution, so for now treat this as “OpenSourceMalware’s assessment.”
It operates outside npm v12 and age gates
The defenses I laid out in the npm v12 allowScripts article block preinstall, install, postinstall, and the implicit node-gyp rebuild when they aren’t allowlisted.
That handles postinstall-triggered malware like Mastra’s easy-day-js or the axios-adjacent plain-crypto-js.
ViteVenom’s entry point is outside that.
It’s not an install script — it’s an installed CLI that runs later.
Even with allowScripts enabled, the decision to run bin/vite.js is on the project/developer side, not in npm’s install stage.
The release-age gate is also limited here.
As I wrote in the pnpm 11 minimumReleaseAge article, not fetching a freshly published version for some period makes it easier to avoid malicious versions that get removed within hours.
Per Checkmarx, these 7 were published in the narrow window around June 29 through July 3, 2026, so if a sufficiently old candidate exists within the version range, initial retrieval can be delayed. pnpm 11’s built-in default is 1,440 minutes, but it’s non-strict: if no candidate qualifies, it can fall back to a newer version. To force a hard failure, set minimumReleaseAgeStrict: true explicitly.
On the other hand, a package detected days after publication, or a version already pinned in the lockfile, is a separate problem.
npm’s staged publishing also adds an approval point to a legitimate maintainer’s publish flow.
When the attacker creates their own new scope and package, as with ViteVenom, the legitimate project’s staged publishing isn’t involved.
Provenance is the same: it tells you which CI built it, but not whether you should be using that package name at all.
What’s left is confirming whether a package name should be trusted, and when the code runs.
Is a non-@vitejs/* fake-Vite scope present.
Are you calling a CLI that resembles Vite or Vitest from a package script.
In a PR that adds a dependency, do the npm registry publisher, scope, homepage, and repository match the legitimate project.
Check these during lockfile-diff review.
Impact assessment starts with package names and dotfiles
Check whether the 7 names appear in your lockfiles.
rg '@uw010010/vite-tree|@vite-tab/tab|@vite-ln/build-ts|@vite-mcp/vite-type|@vite-pro/vite-ui|@vitets/vite-ts|@vite-ts/vite-ui' package-lock.json pnpm-lock.yaml yarn.lock
npm ls @uw010010/vite-tree @vite-tab/tab @vite-ln/build-ts @vite-mcp/vite-type @vite-pro/vite-ui @vitets/vite-ts @vite-ts/vite-ui
@uw010010/vite-tree mixes benign and malicious versions within the same package.
Checkmarx marks 3.4.1 and 8.1.0 benign, and 3.4.2, 3.4.3, 3.6.1 malicious.
But that only means “that version’s tarball doesn’t contain the same malicious code.” OpenSourceMalware treats all 7 as attacker-controlled and advises against using even versions other than the known-malicious ones. Given the additional @vite-tab/tab@5.7.0 finding, other compromised versions not on latest or the published lists may exist.
The main ViteVenom-specific and shared IoCs:
| Type | Value | Purpose |
|---|---|---|
| File/marker | bin/vite.js / global.i='*5-*' | Common entry point across the 7 Checkmarx-reported versions. The additionally checked @vite-tab/tab@5.7.0 uses global.o='*5-7' |
| C2 | 198.105.127[.]210:443, :80 | WebSocket, upload, /$/boot |
| Related C2 | 166.88.54[.]158:443 | ChainVeil’s primary C2. Checkmarx recommends blocking it as a same-operator IoC |
| Related C2 | 23.27.202[.]27:443, :27017 | ChainVeil’s tertiary C2 and MongoDB. Same shared-operator IoC |
| Static IoC | 4606094, ?.? | Obfuscation seed and BSC payload delimiter in Checkmarx-reported versions |
| XOR key | 2[gWfGj;<:-93Z^C, m6:tTh^D)cBz?NM], ThZG+0jfXE6VAGOJ | Decryption keys for Stage 2A, Stage 2B, and the HTTP loader |
| Tier-1 Tron | TCqf6ZkaQD84vYsC2cuu1jRwB6JveTaRrF | Stage 2A BSC pointer |
| Tier-1 Tron | TFMryB9m6d4kBMRjEVyFRbqKSV1cV2NcpH | Stage 2B BSC pointer |
| Tier-1 Aptos | 0x9d202c824402ca89e9aaccd2390b6f8b332ae743caa1469c695feb2781d56519 | Stage 2A fallback |
| Tier-1 Aptos | 0x3d2075f97b7b1e3234bd653779d21c605d7d8c6ec9c98d983880be5c7f4f9471 | Stage 2B fallback |
| BSC transaction | 0x5ab85abe6c67adb94322e5700a36915c38d1db1e604920da8aa4fcb530408af0 | Stage 2A payload |
| BSC transaction | 0xbcc976e1c8f3dfd93e146ff424836a9635ab36d991a54675635d7fdf30e60616 | Stage 2B payload |
| Shared Tier-2 Tron | TA48dct6rFW8BXsiLAtjFaVFoSuryMjD3v | Final RAT pointer shared with ChainVeil |
| Shared Tier-2 Aptos | 0x533b2dbcaeff19cd1f799234a27b578d713d8fcaa341b7501e4526106483e0b1 | Fallback shared with ChainVeil |
In an on-chain check on July 19, 2026, hex-decoding and reversing the Stage 2A Tron data yields the BSC transaction above, and it matches the Aptos transfer recipient. The BSC RPC also shows both Stage 2A/2B transactions and the data-bearing inputs. A record persisting on-chain is not the same as RAT delivery or C2 connection still succeeding today — for safety, the latter was not connection-tested.
On machines and CI runners that turn up positive, removing the package leaves RAT traces and credential exposure to check.
Checkmarx lists 198.105.127[.]210 as ViteVenom’s C2 server.
It also recommends checking for a detached node -e, a machineId in a hidden directory, and suspicious appends to .bashrc, .zshrc, and .profile. The shell-config injection is hidden behind 200+ characters of whitespace.
This is because the RAT carries a reverse shell (connects back to the attacker to open a shell), credential harvesting, file exfiltration, and persistent backdoor injection.
Extend the check beyond Node.js processes and shell history to dotfiles and credentials.
The RAT Checkmarx analyzed collects .bashrc, .zshrc, .profile, all of ~/.ssh/, and ~/.npmrc, and on macOS reads OAuth tokens and passwords from a Keychain export. It also has arbitrary shell command, arbitrary JavaScript, and single-file and full-directory upload capabilities. If GitHub tokens, npm tokens, cloud CLIs, Kubernetes, Vault, .env, or LLM API keys were used on the same machine, treat them as exposed and revoke/reissue them from a separate, clean machine after isolation.
If a Vite project’s CI hit it, reconcile the job’s environment variables, self-hosted runner persistent storage, internal npm proxy, Docker layers, and the pnpm store.
References
- Checkmarx Zero: Sequel to ChainVeil npm Malware Targets Vite Ecosystem
- The Hacker News: Seven Malicious Vite npm Packages Use Blockchain C2 to Deliver a RAT
- Checkmarx Zero: ChainVeil: A Malicious npm Supply Chain Attack by SuccessKey
- OpenSourceMalware: ChainVeil and ViteVenom are DPRK’s PolinRider Campaign