Skip to content

Build1 publisher2 min readPublished

Poisoned vite.config.js turns git pull and npm run build into malware

Malware that steals a developer's Git credentials force-pushes a poisoned vite.config.js to every reachable branch, a dozen rewritten in a minute in one case. A routine git pull and build then runs it, and dependency scanners never see the change.

The Engineer · Build desk

Illustration accompanying Poisoned vite.config.js turns git pull and npm run build into malware

What happened

  • Malware on one team member's machine steals that machine's Git hosting credentials and force-pushes a poisoned commit to every branch of every repository the credentials can reach.
  • Only vite.config.js is rewritten, with obfuscated code appended to its end, while package.json and node_modules are left untouched.
  • When a teammate runs the ordinary git pull followed by npm run build, the payload starts as the build loads the config and opens a connection to an external server.
  • In one investigated case a poisoned commit overwrote the develop branch 14 minutes after a real merge, and a dozen or so branches were rewritten within about a minute, the shortest gap two seconds.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • constraint Dependency audit tools read package.json and walk node_modules, so a payload living in a config file every project builds is outside what they scan.
  • exposure Because production and staging branches are in scope, any repository wired to CI/CD carries the poison from a developer's pull into deployed environments.
  • capability The C2 address is read from a public blockchain, so seizing a server or blocking a domain does not remove it; the operator writes a new address and the loader finds it.

Run `npm run build` on a Vite project and the tool loads vite.config.js and executes it as a module before it bundles a single file. The attack forges a legitimate-looking merge commit and appends its payload to the end of that file, after a long run of whitespace that follows the closing `});` line [2][10].

Nobody opens it, for three reasons [11]. It sits at the bottom of the file, so a diff looks as if only the `});` line changed. Several hundred characters of whitespace push it off the right edge of the editor. And `git diff --stat` shows `1 file changed, 2 insertions(+), 3 deletions(-)`. Most reviewers stop at a line like that [11].

The appended code is obfuscated in two layers [12]. A character swap rebuilds a string table, then a dictionary expansion assembles the real code and runs it through the `Function` constructor, which keeps its keywords out of a static search [12].

Stage two reads the command-and-control address off a public blockchain [13]. The loader opens an Ethereum JSON-RPC connection, walks back through recent blocks, finds a transaction whose sender address contains a specific string, and builds the C2 address from that transaction's recipient [13]. Three RPC endpoints are tried in order, so one going offline does not stop it [14].

This is called EtherHiding, and reported variants also use TRON, Aptos and BNB Smart Chain [16]. Because the ledger is append-only, an address written to it stays there. Once it is running, the resident process reads code from the C2 and passes it to `eval`, so it can be handed new instructions at any time and run arbitrary code [6][17]. It refuses to run a second copy of itself and can be silenced with a `-skipwarn` startup flag [17].

The push is scripted and fires the instant the stolen credentials are available [8]. Security vendors have tracked the campaign against the npm and Vite ecosystem under names including ChainVeil, ViteVenom and PolinRider [3].

What to watch

  • Whether the vendors tracking ChainVeil, ViteVenom and PolinRider publish indicators, including the sender-address string the loader matches on.
  • Whether Git hosting providers push force-push protection or required-review defaults that would block the branch overwrite.
  • Whether more EtherHiding variants move to further chains, widening the set of public RPC providers defenders would need to watch.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories