Do the math: How we took over ChatGPT accounts
TL;DR Highlight
One public share link ran attacker JS in a victim's ChatGPT session and could leak private chats
Who Should Read
Frontend or security engineers building HTML preview features that rely on iframe sandboxes and postMessage
Core Mechanics
- The Preview sandbox itself held
- HTML Preview ran in a restricted frame on a different origin and could not read parent.document.
- The issue was another component in the main page that accepted its messages.
- MathLive never checked the sender
- A graph loaded MathLive and installed a postMessage listener in the main page.
- Its origin policy was none, and it did not require a previously approved window.
- Keyboard layouts accepted raw HTML
- The layout set via update-setting was assigned to innerHTML.
- createHTML only marked the string as trusted and passed it through unchanged.
- An unsandboxed iframe inherited chatgpt.com
- MathLive inserted a srcdoc iframe into the main document without a sandbox attribute.
- Created by the main page, it got the https://chatgpt.com identity and could reach the parent.
- CSP fell to YouTube JSONP
- CSP blocked inline JS but allowed YouTube scripts.
- The oEmbed endpoint echoed an invalid callback in its response, so ending it with ;void made it run.
- One public link became account-level access
- Putting the graph and HTML Preview in one public conversation kept the chain in the /share/<id> link.
- The code called authenticated APIs as the victim to list and read private chats and sent a small piece off-site.
Community Reactions
- Opinion: the bounty is too low even with the click needed
- Many commenters said $5,000 was far too little for this chain.
- Some worried others would stop reporting if bounties stay this small.
- Opinion: low bounties may be strategic
- One commenter said big companies may prefer lower bounties so fewer people go hunting.
- They argued $5k versus $50k hardly matters to the company.
- Criticism that the write-up was hard to follow
- One commenter said the JSONP section explained JSONP rather than the bypass.
- They also said it did not explain how the attacker controlled the Preview content in the first place.
- Experience with the authors' outreach
- One commenter said the company contacted them unsolicited about a vulnerability in a test environment, then pitched its services.
- They called it unwelcome without a bounty program, and illegal.
How to Apply
- Audit every postMessage listener
- Search your frontend for addEventListener('message') today.
- Check that each handler verifies both event.origin and event.source, starting with state-changing ones.
- Remove raw HTML from message-driven settings
- Find paths where a library puts postMessage data or settings into innerHTML.
- Accept only text or structured data, and sanitize if HTML is needed.
- Add sandbox to dynamically created iframes
- Find srcdoc and dynamic iframes that untrusted input can influence.
- Set sandbox by default so they don't inherit the parent origin.
- Test sharing and preview features across components
- Add scenarios where public sharing, previews, and third-party libraries meet on one page.
- Check whether any CSP-allowed external domain exposes a JSONP endpoint.
Try It
// Paste into Claude or ChatGPT: security review of frontend code
Below is our frontend code that uses postMessage.
{{code}}
Check the following.
1. Does each message listener verify both event.origin and event.source?
2. Is there any path where message data reaches innerHTML or another HTML sink?
3. Do dynamically created iframes (including srcdoc) have a sandbox attribute?
4. Do any CSP-allowed external domains expose JSONP endpoints with a callback parameter?
Show the problematic lines and fixed code.
Next: after fixing, send a forged message from an iframe on another origin and confirm it is rejected.Terminology
Related Papers
I Am the Reason OpenAI Is Killing Unlimited Chats
Connecting local MCP tools to regular ChatGPT chats gave a parallel coding-agent pool without Codex limits
GPT-5.6 Sol can beat Factorio on peaceful mode
GPT-5.6 Sol in Codex at low thinking finished a peaceful Factorio run with a rocket launch
AI isn't replacing SREs, it's deskilling them
Letting AI handle 95% of incidents may dull the skills SREs need for the critical 5%.
Backdoor in Claude Code for VS Code extension on the Cursor Marketplace
A tampered Claude Code extension on Cursor Marketplace planted an SSH backdoor
Building an AI-native engineering org
Automating each shipping step raised merges per engineer about 5x with reverts under 0.5%
We ran AI agent swarms in Minecraft and Mindustry
We put 50 agents in Minecraft and they invented ration ledgers and bureaucracy.
The Agent SDK
Cursor released Agent SDK, letting you define agents in code and deploy them with one command.
Rare, Relevant, and Still Wrong: How Memory Fails LLM Group Decisions
Picking group-discussion memories by rarity or relevance often flips the final LLM decision