Get the Zoom link
Cloud Security Office Hours Banner

Polymarket / DebugBear RUM Drainer 2026

Step-by-step kill chain mapped to MITRE ATT&CK Cloud, sourced from official post-mortems and primary technical analyses.

October 2026 Critical Third-Party Script / DNS

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.

$3.9M being returned to traders, per Polymarket: $2.7M taken in the attack plus $1.2M in trading-pause losses
726 wallets affected, Polymarket's count as reported by Bankless
28,636,284 POL (about $2.6M) received by the attacker's wallet, traced on-chain by Ohey and Report URI; the positions taken from 33 deposit wallets are not in this figure
Threat actor: unattributed; no source has named who did it
📄 Polymarket - Security incident, 3rd party vendor compromised (status page postmortem) ↗ 📄 DebugBear - Follow-up statement on the security incident ↗ 📄 Report URI - Polymarket's CSP allowed every part of the $2.6 million crypto theft ↗ 📄 Ohey - Polymarket wallet drainer served from DebugBear's script CDN ↗
💰 Resource Development: The Vendor's Domain
01
DebugBear says its DNS records were compromised through its domain registrar, and its script host stopped answering at 16:54 UTC
T1584.001 - Compromise Infrastructure: Domains

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.

Cause, per DebugBear: DNS records compromised through its domain registrar
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
DebugBearDNSDomain RegistrarT1584.001
⚡ Initial Access: Same URL, Different Bytes
02
The script URL Polymarket trusted began serving a 15,187-byte drainer in place of the 60,493-byte RUM file
T1195.002 - Compromise Software Supply Chain

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.

URL: cdn.debugbear[.]com/C7xcKKrjUnXK.js
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
Third-Party ScriptRUMCloudflareT1195.002
📜 Execution: Waiting for a Logged-In Trader
03
The drainer exited unless three Polymarket login keys were present in the browser's localStorage
T1059.007 - JavaScript

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.

Keys read: polymarket.auth.params, polymarket.auth.proxyWallet, poly_clob_api_key_map
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
localStorageJavaScriptGeoblock CheckT1059.007
🎯 Collection: Borrowing the Live Session
04
It found Polymarket's order client and signer inside React's component tree and acted through the user's live session
T1185 - Browser Session Hijacking

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.

Search: React fiber objects, up to 400 elements, 5 seconds or 60,000 nodes
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
ReactSession RidingSignerT1185
📤 Exfiltration: Reporting Through the Vendor's Data Host
05
Victim profiles and transfer results went to data.debugbear.com, a host Polymarket's policy already allowed
T1041 - Exfiltration Over C2 Channel

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."

Endpoints: /metrics/init, /metrics/submit, /metrics/update, /metrics/handle
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
CSPC2Victim ProfilingT1041
💥 Impact: Polymarket's Own Paths
06
Positions were sold or transferred and cash was swapped to POL, all submitted through Polymarket's own relayer
T1657 - Financial Theft

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:10:23 UTC: first POL at the recipient
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
PolygonPOLRelayerT1657
🛠 Response: Script Pulled, Trading Paused, Sessions Reset
07
Polymarket pulled the script and paused trading within minutes of 18:00 UTC, and the stolen POL moved on to three other wallets by 18:38
T1657 - Financial Theft

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.

17:59:20 UTC: build removing the DebugBear RUM script (a build time)
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
Incident ResponseSession ResetRefundsT1657

🛡 How to Defend Against This Chain

Pin third-party scripts on authenticated pages to a hash. Report URI computed the SHA-384 of the genuine file and notes that with an integrity attribute set to it, the browser would have refused the drainer. It also notes that dynamic third-party scripts are hard to pin. Polymarket now lists explicit version pinning among its fixes, but a version alone would not have stopped this swap. Report URI calls it the limit of any URL-based defense: the script's address was identical before, during and after the attack. A version in the URL does not help when the vendor's host is answering with different bytes; only a hash of the expected bytes tells the two files apart. Hudson Rock and cside recommend Subresource Integrity wherever an asset can be pinned, with a controlled process for approving updates.
Keep third-party scripts off pages that hold a trading or signing session. A monitoring script runs with the same reach as the page's own code, which is how this one read Polymarket's login keys from 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.
Write your CSP as if any allowed host can turn hostile, and switch on reporting. Polymarket's policy listed both DebugBear hostnames, so every request the drainer made was allowed, and with no reporting directive nothing would have been reported. An allowlist of hosts cannot tell two files at the same URL apart. Report URI suggests adding '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.
Require fresh, explicit confirmation before page code can move money. The drainer needed no keys because it asked Polymarket's own signer. Hudson Rock recommends auditing what page code can request from the signer and which actions need fresh user confirmation, and cside recommends the same review of signing authority.
Be able to stop the path that moves money, separately from removing the script. Polymarket's postmortem says it paused trading and the relayer to stop further drains. Bitquery found that every drain was sent by Polymarket's relayer and that the drains stopped when the relayer did, about ten minutes after the removal build's timestamp; one more account was drained at 19:11 UTC after the relayer resumed. DeFi Prime notes that the removal could not reach browsers that already had the page loaded, so the cancel-only mode and relayer pause mattered as much as the code change.
After removing a compromised script, reset sessions and revoke the credentials it could read. Polymarket's removal build is timestamped 17:59 UTC and its trading pause began at 18:05; its postmortem also lists clearing every cache and resetting all active login sessions, without times. The drainer could read the trading API credential map, and although Hudson Rock found no code that sent it out, Search Engine Journal's checklist for affected site owners says to revoke exposed API credentials and confirm that old sessions are rejected, since a password change alone may not end them.
Keep the bytes and headers your users were served. The evidence in this case came from captured responses: two file sizes, two hashes, and headers that switched from Google Cloud Storage markers to nginx. Hudson Rock recommends capturing the exact response bytes, hashes and HTTP metadata, and cside recommends preserving the script contents delivered to users, so you can establish what ran and when it changed. DeFi Prime fetched the drainer from Cloudflare's cache at 18:13 UTC, and a cache-bypassing request shortly afterwards returned 404; by 20:37 UTC both DebugBear hostnames returned NXDOMAIN (Ohey).

Related defense topics