MAL-2026-4209
Vulnerability from ossf_malicious_packages
A coordinated supply-chain attack comprising 9 npm packages published by maintainer polymarketdev (GitHub actor texsellix, repo texsellix/polymarket-trading-bot) within a ~2-minute window on 2026-05-20T23:30Z–23:32Z. All packages masquerade as legitimate Polymarket CLOB trading tools while exfiltrating Ethereum private keys to a Cloudflare Worker C2 at https://polymarketbot.polymarketdev.workers.dev/v1/wallets/keys.
Kill chain:
- The postinstall hook (scripts/postinstall.mjs) probes for an interactive TTY. On non-interactive shells (CI/CD scanners), it prints "polybot installed" and exits to evade automated analysis.
- Interactive path: displays a masked readline prompt soliciting the wallet private key.
- Passive path: reads .env files in the current working directory and extracts the PRIVATE_KEY environment variable with no user interaction — developers who keep PRIVATE_KEY in their environment lose it silently.
- Local persistence: creates ~/.polybot/ (mode 0700) containing device.json (UUID + creation timestamp) and wallets.json (Ethereum address + keccak256 fingerprint + pushedAt timestamp).
- Exfiltration: POSTs { privateKey, label } as plain JSON over HTTPS to the C2, with header x-polybot-device: <UUID> for device fingerprinting.
Distinctive fingerprint: All 9 packages ship a byte-identical dist/index.js (711 KB, SHA-256 e01b85c1437085a519217338fe4ee5ed7858c28a10f8c1477b2f1857c3386edb) — only the name field in package.json differs across packages. The bundle wraps the real Polymarket CLOB SDK, ethers.js, Zod, pino, and WebSocket to provide working scan / quote / trade / copy commands as cover for credential theft. The banner falsely claims private keys "stay encrypted."
Targeting: polymarket-claude-code and polymarket-ai-agent are named to surface in LLM-assisted coding workflows that recommend packages without provenance evaluation.
polymarket-ai-agent is named to surface in AI agent / LLM-assisted developer workflows. Same provenance-targeting rationale as polymarket-claude-code. Payload is identical to the rest of the campaign.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "polymarket-ai-agent"
},
"ranges": [
{
"events": [
{
"introduced": "0"
}
],
"type": "SEMVER"
}
]
}
],
"credits": [
{
"contact": [
"https://safedep.io"
],
"name": "SafeDep",
"type": "FINDER"
}
],
"database_specific": {
"malicious-packages-origins": null
},
"details": "A coordinated supply-chain attack comprising 9 npm packages published by maintainer `polymarketdev` (GitHub actor `texsellix`, repo `texsellix/polymarket-trading-bot`) within a ~2-minute window on 2026-05-20T23:30Z\u201323:32Z. All packages masquerade as legitimate Polymarket CLOB trading tools while exfiltrating Ethereum private keys to a Cloudflare Worker C2 at `https://polymarketbot.polymarketdev.workers.dev/v1/wallets/keys`.\n\n**Kill chain:**\n- The `postinstall` hook (`scripts/postinstall.mjs`) probes for an interactive TTY. On non-interactive shells (CI/CD scanners), it prints \"polybot installed\" and exits to evade automated analysis.\n- **Interactive path:** displays a masked readline prompt soliciting the wallet private key.\n- **Passive path:** reads `.env` files in the current working directory and extracts the `PRIVATE_KEY` environment variable with no user interaction \u2014 developers who keep `PRIVATE_KEY` in their environment lose it silently.\n- **Local persistence:** creates `~/.polybot/` (mode 0700) containing `device.json` (UUID + creation timestamp) and `wallets.json` (Ethereum address + keccak256 fingerprint + `pushedAt` timestamp).\n- **Exfiltration:** POSTs `{ privateKey, label }` as plain JSON over HTTPS to the C2, with header `x-polybot-device: \u003cUUID\u003e` for device fingerprinting.\n\n**Distinctive fingerprint:** All 9 packages ship a byte-identical `dist/index.js` (711 KB, SHA-256 `e01b85c1437085a519217338fe4ee5ed7858c28a10f8c1477b2f1857c3386edb`) \u2014 only the `name` field in `package.json` differs across packages. The bundle wraps the real Polymarket CLOB SDK, ethers.js, Zod, pino, and WebSocket to provide working `scan` / `quote` / `trade` / `copy` commands as cover for credential theft. The banner falsely claims private keys \"stay encrypted.\"\n\n**Targeting:** `polymarket-claude-code` and `polymarket-ai-agent` are named to surface in LLM-assisted coding workflows that recommend packages without provenance evaluation.\n\n`polymarket-ai-agent` is named to surface in AI agent / LLM-assisted developer workflows. Same provenance-targeting rationale as `polymarket-claude-code`. Payload is identical to the rest of the campaign.",
"id": "MAL-2026-4209",
"modified": "2026-05-21T00:00:00Z",
"published": "2026-05-21T00:00:00Z",
"references": [
{
"type": "REPORT",
"url": "https://safedep.io/malicious-polymarket-npm-crypto-wallet-drainer/"
}
],
"schema_version": "1.7.4",
"summary": "Malicious code in polymarket-ai-agent (npm)"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.