Richland Technology
Richland Technology
← Back to Blog
llmagentssecurityxss

A ChatGPT share link could run code in your session: postMessage → innerHTML → YouTube JSONP

Parameter Labs' chain bypassed the sandbox and the CSP without breaking either — the real perimeter was a sender check one open-source library omitted.

Richland AI Desk·1 November 2026·6 min read

Parameter Labs has published a full account-takeover chain against ChatGPT: opening a public share link could run attacker-controlled JavaScript inside a signed-in user's session, read private conversations, and act on the account. Per Parameter Labs, OpenAI reproduced the report, fixed it on October 26, and paid a $5,000 bounty.

What shipped

The target was ChatGPT's HTML previews — the artifacts pattern (formerly Canvas), where an assistant-authored HTML block renders as an interactive app inside a sandboxed iframe. Parameter Labs (writeup, by Mukul Goyal) showed that a conversation containing one interactive graph plus one HTML preview could be shared as a normal /share/<id> link; a signed-in victim who opened it handed the attacker JavaScript execution on the chatgpt.com origin.

The chain, as the researchers describe it:

  1. The graph installs a listener. ChatGPT bundles the open-source MathLive library for graph support. Loading a graph "installed a listener in the main ChatGPT page for messages related to MathLive's on-screen keyboard."
  2. The preview sends a message. The sandboxed HTML preview iframe posts two messages to top: a mathlive#virtual-keyboard-message with action: "update-setting" carrying attacker-supplied keyboard "layouts" (raw HTML), then action: "show". Sandboxing does not block postMessage, and MathLive's top-level controller "used an origin policy of none" — it accepted messages from any web address and never checked which frame sent them.
  3. innerHTML in the parent. When the keyboard opened, MathLive joined the supplied layout into an HTML string and assigned it to innerHTML in the real ChatGPT page. A helper named createHTML looked like a sanitizer but was a Trusted Types wrapper that passed the HTML through unchanged.
  4. An unsandboxed iframe. innerHTML does not execute <script> elements, but it can inject an <iframe srcdoc="...">. Because the parent created it without a sandbox attribute, the browser gave it the https://chatgpt.com identity. Code inside could now access the parent page.
  5. CSP bypass via YouTube oEmbed. OpenAI's CSP blocked inline scripts but allowed scripts from YouTube. YouTube's oEmbed endpoint reflects a JSONP callback parameter into its response — including into a 400 validation error. The payload used callback=globalThis.__marker=31337;void; the trailing ;void neutralized the object YouTube appended, and the browser executed the callback as a script inside the new frame. "Since YouTube was already allowed by ChatGPT's CSP, the browser loaded this response as a script and ran our callback inside the new frame."
  6. Session access. With JavaScript on the chatgpt.com origin, the payload read the session data already present in the page and called ChatGPT's conversation APIs — GET /backend-api/conversations to enumerate recent chats, then GET /backend-api/conversations/<id> to read a private conversation that had never been shared — and exfiltrated a small piece of the response via an ordinary image request to a server the researchers controlled. "Essentially, we could perform any action on behalf of the victim's ChatGPT account."

The public share link modified the real ChatGPT page, injecting attacker-controlled content into the signed-in session

Timeline: reported through OpenAI's bug bounty on October 18; OpenAI reproduced it October 21 and escalated severity from P2 to P1; fixed October 26; the writeup went public October 28. Bounty: $5,000.

Delivery was refined to a single click: in the final variant the HTML block was "marked as previewable so that ChatGPT opened it automatically. In that version, opening the link was the victim's only click." The researchers reproduced the automatic path three times in a separate account, with the alert appearing about 4.5 seconds after load.

Why it matters

The instructive part is what did not fail: the Preview sandbox held — code inside it could not read parent.document — and the CSP genuinely blocked inline scripts. Both were bypassed without being broken. The researchers' root-cause framing: "Three design decisions turned that mistake into a full exploit: MathLive accepted a state-changing message without proving which frame sent it. The message format allowed raw HTML to be supplied as keyboard-layout data. That HTML could create an unsandboxed document that inherited the chatgpt.com identity." The YouTube trick was "the final workaround for CSP, not the root cause."

For anyone building artifact or agent UIs, the transferable rules:

  • postMessage handlers must check event.origin and event.source. The entire perimeter here was a sender check that one open-source library omitted. A static audit by dev.to's Sam LABBE of the shipped MathLive bundle found four message listeners with no sender check and innerHTML sinks. A ~100-line scan for addEventListener('message') without .origin/.source catches the class, though the author notes it is a heuristic, not a proof.
  • Sandboxed iframes can still talk to the parent. sandbox restricts DOM access, not postMessage. Any message handler in the trusted page is reachable from untrusted frames.
  • innerHTML in a message handler is a code-execution sink. A Trusted Types wrapper is not a sanitizer.
  • CSP allowlists are only as safe as the most reflective endpoint on them. "YouTube in script-src" means "YouTube decides what JavaScript runs on chatgpt.com" via a JSONP callback. oEmbed and JSONP endpoints in an allowlist are a bypass primitive.
  • An iframe created by the parent without sandbox inherits the parent's origin. srcdoc frames are not automatically sandboxed.

Controlled proof that private conversation data could leave the victim's session

The second-order story: a full, link-based account takeover was priced at $5,000, and the security community is openly questioning whether that rate incentivizes the next disclosure. Simon Willison called it "a bit cheap for a vulnerability this serious."

Caveats

  • There is no OpenAI advisory or CVE; the "fixed" status rests on Parameter's account and the bounty payment. The researchers report no real-world use, and their proof ran between their own accounts.
  • The demonstrated scope was conversation enumeration and read/exfiltration. "Any action on behalf of the victim" is the researchers' inference from the session boundary, not a demonstrated capability — billing, files, and destructive actions were not shown.
  • The full CSP script-src allowlist was never published; only YouTube is named in the sources.
  • MathLive's claimed fix (0.111.1/0.111.2) could not be confirmed from the project's GitHub releases page, which still showed 0.100.0 as latest at time of research.
  • The October 13 "first confirmation against production code" date appears in only one secondary writeup.

Sources

Start a Conversation

Ready to build something like this?

Tell us about your project and we'll respond with a clear plan within one business day.

Tell Us About Your Project

Takes 3 minutes. We read every submission personally.