Polymarket / DebugBear - Vendor's Script Host Hijacked → Same URL Serves a Drainer → Drainer Rides Logged-In Trading Sessions → Positions Sold and Funds Swept Through Polymarket's Relayer → $3.9M Being Returned to Traders
On October 29, 2026 a performance-monitoring script that polymarket.com loaded from a vendor was replaced with a wallet drainer. Polymarket's status-page postmortem of October 30 describes the attack and does not name the vendor. Hudson Rock retrieved the drainer from cdn.debugbear.com, a Polymarket build that afternoon carried a commit removing the "debugbear rum script", and DebugBear told its customers to remove its real user monitoring (RUM) embed code. DebugBear later said its DNS records had been compromised through its domain registrar. The drainer acted only for visitors with a logged-in session: it found Polymarket's own order client in the page and used it to sell or transfer positions and sweep cash to an attacker wallet through Polymarket's relayer. Polymarket's Content Security Policy listed both DebugBear hostnames and no integrity hash pinned the file, so browsers ran the drainer as they had run the genuine script. Polymarket says it is returning $3.9M to traders. Two caveats belong next to the headline. The registrar explanation is DebugBear's own, and it has not named the registrar or said how the records were changed. And the $3.9M includes $1.2M of compensation for losses during the trading pause; on-chain analysts trace 28,636,284 POL, about $2.6M, to the attacker's wallet, not counting the positions it also took.
DebugBear's first notice on October 29 said it was impacted by an ongoing security incident and asked RUM customers to remove the embed code. A follow-up post gave the cause: "Our DNS server records were compromised through our domain registrar." In the same post DebugBear said it could not receive email sent to its domain, that its initial investigation showed the attack targeted the website of one specific customer, and that it had seen no indication of a compromise of its internal systems while it continued to investigate. It has not named the registrar or said how the records were changed.
The timing evidence fits that account without proving it. An archived response in Ohey's timeline shows Cloudflare returning error 521, origin not responding, for cdn.debugbear.com at 16:54:57 UTC. RuntimeWire reports, citing Ohey's founder Matt Kirkland, that the registry record for debugbear.com changed nine minutes before the script host began failing. The only source RuntimeWire lists is Ohey's write-up, which does not mention a registry change, and RuntimeWire says the change's details and cause remain unknown. CircleID, covering a separate registrar-level hijacking campaign against 12 cryptocurrency sites, says no common attacker, registrar or technical cause has been established between that campaign and this incident.
Registrar: not named
16:54:57 UTC: Cloudflare 521 for cdn.debugbear.com (archived response, per Ohey)
Registry record change: nine minutes before the host failed, per RuntimeWire citing Kirkland; not in Ohey's write-up
Internal systems: no indication of compromise so far, per DebugBear
Polymarket loaded cdn.debugbear.com/C7xcKKrjUnXK.js, and the URL never changed. Report URI compared the responses. Before the attack the file came from Google Cloud Storage, and every response carried Google's markers such as x-goog-hash. During it those markers were gone and the response carried an nginx-format ETag, from which Scott Helme concludes that whatever answered for cdn.debugbear.com was a different server from the bucket that normally serves the file. He decodes that ETag to a modification time of October 27, 2026 at 12:50:43 UTC. That date is his reading of a header sent by the server that served the drainer, and Ohey lists the same date from the same server. Hudson Rock saw the same date in the response's Last-Modified header and does not treat that server-supplied value as the start of the attack. Ohey records Cloudflare's Chicago edge caching the drainer at 17:27:45 UTC.
The shape matches Polyfill.io in 2024, where the domain behind a widely embedded script was sold and its new owner served malicious code to every site still loading it. Here, DebugBear says its DNS records were compromised through its domain registrar; it has not described any change of ownership. Polymarket had disclosed a separate vendor-script injection on June 25, 2026, which Decrypt reported at about $3M; cside notes that the shared pattern does not establish the same attacker or method.
Genuine file: 60,493 bytes, served from Google Cloud Storage
Drainer: 15,187 bytes, served by nginx
Drainer SHA-256: 0755e767504d8103f6c27b9e765a1026507a17c04513242adb503df7777879e5
ETag date: 27 Oct 2026 12:50:43 UTC, Report URI's decoding
The script fetched its configuration from data.debugbear.com, checked the visitor's location against Polymarket's geoblock endpoint and skipped blocked regions. It then read three keys that Polymarket's app writes to localStorage: polymarket.auth.params (login type and wallet address), polymarket.auth.proxyWallet (the deposit wallet) and poly_clob_api_key_map (trading API credentials). Without them it stopped. With them it sorted the account as a browser wallet (eoa) or an email login (magic). Ohey, Hudson Rock and Report URI describe the same gate.
If the configuration request failed, the script fell back to hard-coded values: the recipient 0x3F99139F1C5854bb628c2e576Aaf638e99dFfc18, a $10 minimum per account and per position, and POL as the payout coin (Ohey). Hudson Rock found that the script reads the trading API credential map, but it did not identify a routine that sends that map, a password, a seed phrase or a private key to the reporting endpoint.
Exits when: the keys are absent, or the visitor's region is blocked
Fallback config: recipient 0x3F99...fc18, $10 minimums, paid in POL
Secrets sent out: none identified by Hudson Rock
The drainer held no keys of its own. Ohey describes it scanning up to 400 buttons, links and containers for React fiber objects, walking the tree for up to five seconds or 60,000 nodes, and keeping any object with a postOrder method and a createOrder or createMarketOrder method. That object is Polymarket's order client, with its signer attached. If the client was not yet mounted, the script retried 15 times at 250 ms intervals and then reloaded the page at the user's wallet path.
Polymarket's postmortem says the script "used their active session to sell positions and move funds out of their wallet." Hudson Rock adds a caution about reading the code: the script requested signatures from the existing signer, and a wallet provider may prompt or reject, so a request in the code does not by itself show a silent signature. The on-chain transfers in step 06 show that many of the requests went through.
Match: postOrder plus createOrder or createMarketOrder, with a signer
If not mounted: 15 retries at 250 ms, then a page reload
Keys carried by the drainer: none
Ohey lists four endpoints on the vendor's data host: /metrics/init for configuration, /metrics/submit for the portfolio and each position before any transfer, /metrics/update for each cash sweep and /metrics/handle for each position transfer. Every report carried the deposit and login wallets, login type, operating system, a tracking ID, and the visitor's IP address and country taken from Polymarket's geoblock response.
Report URI reconstructed Polymarket's Content Security Policy from an archived copy of the homepage taken on the morning of the attack. script-src-elem allowed https://cdn.debugbear.com, and connect-src allowed https://data.debugbear.com along with *.polymarket.com, *.alchemy.com and *.fun.xyz. Report URI says that covers every request the drainer made, and Ohey says every host the drainer called other than DebugBear's was on the policy. No report-uri or report-to directive was visible, so a violation would not have been reported. As Helme puts it, "Only the bytes of the script changed."
Sent: wallets, login type, OS, IP, country, portfolio, transaction hashes
script-src-elem: https://cdn.debugbear.com
connect-src: https://data.debugbear.com, *.polymarket.com, *.alchemy.com, *.fun.xyz
CSP reporting: none visible in the archived copy
For browser-wallet accounts, every position worth more than $10 went to the recipient in one safeBatchTransferFrom call on the conditional-token contract. For email-login accounts, positions were sold with fill-and-kill orders at 97% of the current price, then 50%, then the market's minimum tick, and the proceeds joined the cash. Ohey describes the cash being swapped to POL using quotes from api.fun.xyz, wrapped in EIP-712 DepositWallet batches with a one-hour deadline and posted to Polymarket's relayer. Report URI also names Relay and Polymarket's Collateral Offramp in the conversion path.
The first POL reached the recipient at 17:10:23 UTC. Position tokens followed from 17:26:29 to 18:02:17 UTC in 36 transfers from 33 deposit wallets, which Report URI says were sent by 31 relayer addresses. By 18:15 UTC the recipient held 28,636,284 POL (Ohey); Report URI gives the same figure at 18:19:58 UTC and values it at about $2.6M. The published counts measure different things. Polymarket reports $2.7M taken from 726 wallets, as relayed by Bankless and CircleID. TheFatMbappe, cited by Ohey, traced 2.724M pUSD from 721 wallets. Bitquery, reading Polygon data up to 19:49 UTC and counting pUSD and USDC at $1, found $2.73M taken from 737 accounts, and says the attacker held a little less in POL after swap costs. A PolygonScan screenshot supplied to Hudson Rock showed about $2.19M at the recipient, which Hudson Rock says is not a verified total of stolen funds.
17:26:29 to 18:02:17 UTC: 36 position transfers from 33 deposit wallets
Recipient total: 28,636,284 POL, about $2.6M (Ohey; Report URI)
Polymarket's figure: $2.7M from 726 wallets (via Bankless)
Bitquery's count: $2.73M from 737 accounts, to 19:49 UTC
A Polymarket build carrying the commit "remove debugbear rum script" is timestamped 17:59:20 UTC, which Hudson Rock notes is a build time and not a verified deployment time. DebugBear posted its notice at 18:00:58 UTC (Ohey). At 18:05 UTC Polymarket put the exchange in cancel-only mode and paused relayer actions (Ohey, citing Unchained; BleepingComputer gives 2:05 p.m. ET). On-chain, Bitquery found the last drain of the hour at about 18:10 UTC, with Polymarket's relayer sending its last transaction 30 seconds later, and Hudson Rock's homepage capture at 18:12:06 UTC no longer contained the DebugBear filename. Between 18:28:32 and 18:38:16 UTC the recipient sent all its POL to three wallets (Ohey), which BleepingComputer valued at about $2.57M; the position tokens had already gone to a fourth wallet in 25 transfers between 17:45 and 17:56 UTC (Ohey). The relayer came back at 18:52 UTC and trading was fully back at about 19:00 UTC; one more account was drained through the relayer at 19:11 UTC (Bitquery). Polymarket's status page now carries an entry created on October 30 and backdated to October 29 (Identified 17:38 UTC, Resolved 18:06 UTC), and the status incident BleepingComputer linked on October 29 no longer resolves. BleepingComputer gave 3:05 p.m. ET, 19:05 UTC, for the resolution, so this page uses build, on-chain and press times.
Polymarket's postmortem lists what it did: it removed the script and cleared every cache, paused trading and the relayer until the site was clean, reset all active login sessions and referred the case to law enforcement. It is redeploying third-party scripts with additional integrity controls and continuous monitoring and verification, and gives stronger CSP, explicit version pinning and sandboxing as examples. It is returning $3.9M to traders, the $2.7M plus $1.2M for pause-related losses, and said that by the afternoon of October 30, 95% of affected wallets had been refunded in full. Bankless reports that the $1.19M goes to roughly 5,500 wallets that lost out during the pause or had positions taken, and that larger victims reportedly need to sign a release to be repaid; terms users shared, including ID checks and a broad non-disparagement clause, drew pushback, and Polymarket had not publicly addressed them.
18:05 UTC: cancel-only mode, relayer paused
About 18:10 UTC: last drain of the hour, relayer silent 30 seconds later (Bitquery)
18:28 to 18:38 UTC: POL sent on to three wallets
About 19:00 UTC: trading fully restored
19:11 UTC: one more drain after the relayer resumed at 18:52 (Bitquery)
Postmortem: caches cleared, sessions reset, law enforcement referral, $3.9M being returned
🛡 How to Defend Against This Chain
localStorage and found the order client in memory. Hudson Rock and cside both recommend removing unnecessary scripts from authenticated trading and signing pages, and cside adds that each remaining one should have a business reason, an owner and a removal procedure.'report-sha256' to script-src so a changed file at a known URL can raise an alert, and deploying Integrity-Policy-Report-Only to find scripts loaded without SRI before enforcing it.