The Polymarket website that drained roughly $2.94 million from users last week was the real one. Same domain, same certificate, same smart contracts running behind it. Nothing on Polymarket's own servers was touched. The theft rode in on a piece of JavaScript the browser loaded from one of Polymarket's third-party vendors, and that vendor had been compromised. This is the uncomfortable shape of a frontend supply-chain attack: every control you run on your own infrastructure can read green while attackers serve poisoned code to your users from a dependency you do not directly control.
What the attackers changed on the page
Around June 23 to 25, 2026, attackers slipped malicious code into Polymarket's site through a compromised dependency, and the site handed it to a subset of visitors. Polymarket described it as code injection that hit its website "for some users," the same selective delivery that let a backdoored content-delivery script fire only for logged-in admins in an earlier case. Blockchain investigators at PeckShield and Specter read the same activity as a phishing campaign: the injected script prompted users to approve transactions that quietly moved their funds to the attacker. Both descriptions point at one thing, a script that turned the legitimate site against the people using it.
The numbers are small but sharp. Security Affairs reported about $2.94 million stolen in Polymarket's dollar-pegged token; visual-analytics firm Bubblemaps counted fewer than 15 affected accounts. The attacker bridged the proceeds from the Polygon network to Ethereum and converted them into roughly 1,893 ETH, per BleepingComputer. Polymarket said it contained the incident, removed the affected dependency, and is refunding affected users in full, which TechCrunch also reported.
Why none of your server-side tooling would have caught this
Here is the part that should change how you think about monitoring. The malicious code never ran on Polymarket's infrastructure. It ran in the user's browser, delivered as part of the page. A web application firewall sees normal requests. Server logs show a normal session. Backend endpoint tooling has nothing to flag, because nothing on the backend changed. The compromise lived entirely in the slice of the application that gets handed to a third party and then shipped to every visitor.
That is a real blind spot for most security programs. We pour budget into the server side and treat the frontend as static content that just renders. A poisoned vendor script breaks that assumption. The trust boundary has quietly moved from your data center to the browser tab, and very few teams have any detection there at all. The same gap shows up whenever the dangerous code is something you did not write yourself, whether it is a connected app that handed your data over or packages that passed inspection and then turned hostile.
Card skimmers have done this for years. The only new part is the wallet.
None of the technique is new. Groups grouped under the name Magecart have skimmed payment-card details off checkout pages for the better part of a decade using exactly this move: compromise a third-party script, an analytics tag, a chat widget, a payment helper, and let the merchant's own site serve it. British Airways and Ticketmaster both lost card data this way. We have watched the same trust failure in the WordPress world, where a paid plugin update shipped clean and then backdoored the sites that installed it. The mechanics at Polymarket are the same. The asset is just a crypto wallet instead of a card number.
What the wallet changes is the clock. A skimmed card number has to be sold, tested, and cashed out, and a bank can claw the charge back. A signed blockchain transaction settles in seconds and cannot be reversed. So the same client-side compromise that used to mean "card data is now for sale somewhere" means "the money is already gone and bridged to another chain" when the asset is crypto. If your product has a checkout, a payment form, or a wallet connect, you are in scope for this pattern, whether or not you touch crypto at all.
Pin every third-party script, and watch the ones you cannot pin
The fix is not exotic. It is unglamorous and usually skipped. Treat the code your page loads from other people as hostile by default and constrain it:
-
Inventory every external script. You cannot defend dependencies you have never listed. Enumerate every
scripttag the page pulls from a domain you do not control: analytics, tag managers, A/B tools, chat widgets, payment helpers. -
Add Subresource Integrity (SRI) where you can. An SRI hash on a
scripttag tells the browser to refuse the file if its contents change. A vendor whose code is swapped underneath them fails the check instead of running it. It does not cover scripts that legitimately change on every load, which is exactly why those need the next two controls. -
Ship a strict Content Security Policy. A tight
script-srcallowlist limits which origins are allowed to execute code at all, so an injected script pointed at an attacker domain is blocked even when it reaches the page. -
Monitor the page as your users receive it. Synthetic clients that load the real site and diff the served scripts, or client-side runtime monitoring, are the only things that notice a poisoned dependency, because your server never will.
And treat reimbursement as customer service, not remediation. Refunding fewer than 15 users is the cheap part. The affected dependency is gone, but unless the script-level controls go in, the next compromised vendor serves the next poisoned payload to the same browsers. The code you did not write, running with your name on it, is still your incident.