On October 28, the security firm Parameter published a writeup with a title that does not undersell it: "Do the Math: How We Took Over ChatGPT Accounts." The short version: an ordinary public ChatGPT share link, opened once by a logged-in victim, could run attacker-controlled JavaScript inside that victim's signed-in session - with full access to ChatGPT's authenticated APIs, the conversation list, and private chats never shared with anyone.
OpenAI rated the report P1, its most serious category, paid a $5,000 bounty, and shipped a fix on October 26, eight days after the October 18 report. There is no evidence of real-world exploitation; Parameter ran its proof between its own accounts. Users have nothing to install or reset. So this is not a panic story. It is something better: a clean, fully documented lesson in how modern web defenses fail - not one at a time, but in composition.
The sandbox that worked
ChatGPT can turn an HTML code block into a small working webpage through its Preview feature. Because that page can contain arbitrary code, ChatGPT runs it inside a restricted frame on a separate web origin. Parameter's first finding was that this sandbox held: code in the Preview could not read the main page. Isolation, doing its job.
The way in came from a second, unrelated feature in the same conversation: an interactive graph. Rendering the graph loaded MathLive, a math typesetting library, into the main ChatGPT page - and loading MathLive installed a listener for postMessage events about its on-screen virtual keyboard.
postMessage is the browser's mail slot between frames that are not allowed to read each other. It is safe only if the receiver checks who sent the message. MathLive's top-level controller used an origin policy of none: it accepted keyboard-setting messages from any sender, and the keyboard layout it accepted was allowed to contain raw HTML. The graph had installed the receiver; the Preview supplied an untrusted sender. Two features, each defensible alone, together formed a bridge across the sandbox.
From HTML injection to code execution
Getting HTML into the main page was not yet code execution. The remaining steps show how many small gaps had to line up:
-
The layout became
innerHTML. When the virtual keyboard opened, MathLive joined the attacker-supplied layout into an HTML string and assigned it in the real ChatGPT page. A helper namedcreateHTMLlooked like a sanitizer; it was not. It marked the string as trusted and passed the HTML through unchanged. Script tags injected this way do not run - but aniframewith asrcdocattribute does something better: it creates a fresh document, inline, that can load external scripts. -
The new frame inherited
chatgpt.com. MathLive inserted the frame into the main document without asandboxattribute, and because the main page created it, the browser gave it thehttps://chatgpt.comidentity. Code inside it could reach the parent page. -
CSP was bypassed with YouTube's help. ChatGPT's content security policy correctly blocked inline scripts. It also allowed scripts from YouTube. YouTube's oEmbed endpoint accepted a JSONP-style callback parameter, and - even while rejecting an invalid callback - it echoed the callback text into the JavaScript response before the error message. A callback ending in
;voidmade the trailing JSON harmless, and the browser executed the injected code, since YouTube was on the allowlist.
At that point Parameter had arbitrary JavaScript running as chatgpt.com. Their proof enumerated the victim's conversations, opened a private chat, and exfiltrated a portion of it - not by scraping the screen, but by calling ChatGPT's own signed-in APIs. "Essentially, we could perform any action on behalf of the victim's ChatGPT account," they wrote.
The final engineering step was delivery. Early versions needed the victim to connect a researcher-controlled app first. The convincing version put both pieces into one ordinary public conversation: a genuine graph to load MathLive into the main page, and an assistant-authored HTML Preview to send the two messages. Export that conversation as a share link, and opening the link was the only user action required.
Why this one is worth reading twice
Three design choices had to align: a message listener with no origin check, a settings message allowed to carry raw HTML, and a frame created without a sandbox attribute. The YouTube JSONP trick was only the last workaround; blocking it alone would have left the bridge intact. Every layer that failed had been built by someone as a defense or a feature. The vulnerability was not in any of them. It was in the assumption that they composed.
That is the transferable lesson, and it applies far beyond chatbots. Any page that renders untrusted framed content and also hosts third-party widgets is one unchecked postMessage listener away from the same shape of failure. The audit question is never "is the sandbox strong?" - Parameter confirmed ChatGPT's was. It is "what listeners, parsers, and frame factories live in the trusted page, and which of them accept input from outside?"
The honest ending, as with last week's best incident reports: the researchers disclosed privately, the vendor reproduced, escalated, fixed, and paid, and the public writeup arrived after the fix. Boring process, excellent outcome. The exciting part is entirely in the math library.
Sources: Parameter, "Do the Math: How We Took Over ChatGPT Accounts" (Oct 28, 2026), primary writeup; The Hacker News (Oct 31, 2026).
Top comments (0)