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.
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-2560755e767504d8103f6c27b9e765a1026507a17c04513242adb503df7777879e5. the clean version hashes to0525f046c83daa429e0f676d36650d51d9e283ba5cacfa56097c80bad586128b. - exfil and control:
data.debugbear[.]com/metrics/init,/metrics/submit,/metrics/updateand/metrics/handle. tracking ID4c6b64a0-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 starting0x5a2764and POL forwarded onward to three wallets starting0xe4c4,0x5392and0xc91e.
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.
- read the session. it pulled
polymarket.auth.params,polymarket.auth.proxyWalletandpoly_clob_api_key_mapout of localStorage. that is the logged-in user’s identity, proxy wallet and order API credentials. - 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. - sweep cash. it built EIP-712 DepositWallet batches and pushed them through Polymarket’s own relayer at
relayer-v2.polymarket.com/submit, swapping throughapi.fun.xyzon the way out. - take positions. for browser-wallet accounts it moved conditional token positions with
safeBatchTransferFromon the CTF contract0x4D97DCd97eC945f40cF65F87097ACe5EA0476045. 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. - 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
integrityhash 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.

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_mapdirectly. - 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.
defensive takeaway: third-party script sprawl is one of the first things a managed security setup inventories and locks down.
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
- ohey.ca (Kirk): Polymarket wallet drainer served from DebugBear’s script CDN
- Yeeth Security: Page Load Is a Trust Boundary, the Polymarket Script Drain
- DeFi Prime: Polymarket Drained Again
- 0xCERT-2026-0285: Polymarket DebugBear Supply-Chain Frontend Attack
- Coverage by The Hacker News, BleepingComputer and Hudson Rock (Alon Gal)
