Skip to content

Polymarket’s Performance Monitor Became a Wallet Drainer, and the CSP Waved It Through

Polymarket DebugBear Script Drainer: IOCs, Timeline, How It Worked

Polymarket’s performance monitor became a wallet drainer, and the CSP waved it through

nobody phished anyone. nobody signed a weird popup. you logged into Polymarket, a third-party real user monitoring script loaded like it does on every page, and for about 52 minutes on October 29 that script was a 15 KB drainer that sold or moved your positions using your own session. the content security policy allowed every host it touched, because every host it touched was supposed to be there.

What changed. independent researcher Kirk at ohey.ca published a full teardown of the drainer served from DebugBear’s script CDN to Polymarket. per that analysis the recipient wallet took in 28.6 million POL plus positions from 33 accounts, along with USDC.e and USDC. Polymarket removed the script, DebugBear posted a notice, and Polymarket says it is refunding affected users. The Hacker News, BleepingComputer and Hudson Rock have also covered it.

the timeline, in UTC because blockchains do not do Pacific time

  • 16:54:57: DebugBear’s CDN starts returning a Cloudflare 521 error.
  • 17:10:23: first stolen funds arrive at the recipient wallet.
  • 17:27: a Chicago edge has the malicious script cached.
  • 17:59:20: Polymarket removes the DebugBear script from its site.
  • 18:00:58: DebugBear posts a notice.
  • 20:37: the CDN hostname goes NXDOMAIN.

for the Pacific readers: first theft around 10:10 a.m. PT, removal right before 11 a.m. PT. a normal Thursday morning trading window.

the indicators, straight from the teardown

  • script: cdn.debugbear[.]com/C7xcKKrjUnXK.js, 15,187 bytes, SHA-256 0755e767504d8103f6c27b9e765a1026507a17c04513242adb503df7777879e5. the clean version hashes to 0525f046c83daa429e0f676d36650d51d9e283ba5cacfa56097c80bad586128b.
  • exfil and control: data.debugbear[.]com/metrics/init, /metrics/submit, /metrics/update and /metrics/handle. tracking ID 4c6b64a0-a7ef-4c0f-9c70-3a4e6f89065c.
  • entry function: startdebugger. unobfuscated. the attacker was not worried about being read, only about being fast.
  • recipient wallet: 0x3F99139F1C5854bb628c2e576Aaf638e99dFfc18, with positions forwarded to a wallet starting 0x5a2764 and POL forwarded onward to three wallets starting 0xe4c4, 0x5392 and 0xc91e.

how a monitoring script steals positions

this is the part every web team should read twice. the drainer did not need an exploit. it needed to run in the page, and RUM scripts run in the page by design.

  1. read the session. it pulled polymarket.auth.params, polymarket.auth.proxyWallet and poly_clob_api_key_map out of localStorage. that is the logged-in user’s identity, proxy wallet and order API credentials.
  2. find the app’s own signer. it walked React’s internal __reactFiber$ properties to locate Polymarket’s order client already wired to the user’s session. no new wallet prompt needed, because the app had already been granted what it needed.
  3. sweep cash. it built EIP-712 DepositWallet batches and pushed them through Polymarket’s own relayer at relayer-v2.polymarket.com/submit, swapping through api.fun.xyz on the way out.
  4. take positions. for browser-wallet accounts it moved conditional token positions with safeBatchTransferFrom on the CTF contract 0x4D97DCd97eC945f40cF65F87097ACe5EA0476045. for email-login accounts it placed SELL orders, first at 97 percent, then 50 percent, then minimum tick. dumping your position into the book is still a theft if someone else is on the other side.
  5. report back. portfolio and transaction details went to the /metrics/* endpoints, which look exactly like what a RUM product is supposed to send.

why the CSP did not save anyone

Kirk’s analysis makes the point that every host the drainer called was already on Polymarket’s content security policy. the DebugBear CDN and data endpoints were allowed because the product needs them. Polymarket’s relayer was allowed because the app needs it. the swap API was allowed because a feature uses it. a CSP is an allowlist of who can be talked to. it says nothing about what a trusted script says once it is allowed in.

the controls that would have mattered:

  • Subresource Integrity on third-party scripts. an integrity hash pinned to the clean file would have made the browser refuse a 15,187 byte surprise. yes, that breaks vendors who push updates whenever they like. that is the point.
  • self-host or proxy the RUM snippet at a pinned version, so a vendor CDN outage cannot become your incident.
  • keep trading credentials out of localStorage. anything in localStorage is readable by any script in the origin. httpOnly cookies and short-lived, scope-limited tokens exist for this reason.
  • do not run analytics on transaction pages. if a page can move money, it does not need a heatmap. sandbox monitoring in a separate origin or iframe if you must have it.
  • server-side anomaly brakes. 33 accounts suddenly selling at 50 percent of mark through the same relayer should trip something before the 52nd minute.

where the money sits now

we queried a public Polygon RPC endpoint, read only and no keys, for the recipient and forwarding wallets listed in the teardown. the recipient is nearly empty and has sent 34 transactions. the POL has been fanned out, and at least two of the forwarding wallets still hold millions of POL as of November 1. the position-forwarding wallet holds no POL. figures are live chain state at query time, not a valuation, and the sloth is not your blockchain forensics vendor.

Kali terminal running a read-only Polygon RPC script against the Polymarket drainer recipient wallet and four forwarding wallets, showing nonces and current POL balances, followed by a curl of Polymarket's content-security-policy header that no longer contains debugbear.
Live chain state, November 1. recipient 0x3F99…fc18 at nonce 34 with dust left. forwarding wallets still holding roughly 15.3 million, 4.3 million and 4.2 million POL. Polymarket’s current CSP header no longer mentions debugbear.

refunds and what victims should do

Polymarket has said it is refunding roughly $2.7 million to 726 affected wallets and about $1.19 million to around 5,500 wallets hurt by the trading pause, about $3.9 million total, per public coverage of the incident. if you were logged in during the window:

  • check your activity history for sells or transfers between roughly 17:10 and 18:00 UTC on October 29 that you did not place.
  • regenerate any API keys tied to your account. the drainer read poly_clob_api_key_map directly.
  • log out of every session and clear site data for polymarket.com in your browser.
  • ignore anyone who DMs you offering to “recover” your funds. recovery scams follow every crypto incident like seagulls follow a fishing boat. Polymarket’s refunds come from Polymarket, not from a helpful stranger with a Telegram handle.

this is not a crypto problem

replace “Polymarket” with your bank portal, your payroll SaaS or your EHR, and replace “drainer” with “session harvester”. most business web apps load a pile of third-party JavaScript for analytics, chat, A/B tests and session replay, every one of which runs with full access to the page. the developer tooling world keeps relearning the same lesson, see a supply chain bug in GitPython. the trust boundary is not your perimeter. it is every script tag.

if this pattern looks familiar from the other side of the scam world, the task gig job crypto scams we covered rely on the same shortcut: get the victim’s own trusted session to do the transfer.

the sloth take

Polymarket paid a monitoring vendor to watch its page load. for 52 minutes, the vendor’s script watched very closely and then helped itself. pin your third-party scripts with SRI, pull analytics off pages that move money, and stop storing keys where any script can read them. the cheapest supply chain control on the web is a hash in an attribute, and almost nobody uses it.

if your customer-facing app loads third-party JavaScript on pages that move money or data, get it reviewed before a vendor CDN does it for you. Book a slot with Dark Edge Consulting.

sources