← Back to all posts
News

Parameter Shows One Shared ChatGPT Link Could Hijack Accounts Through MathLive

October 31, 2026 · 16:05 UTC · News
Parameter Shows One Shared ChatGPT Link Could Hijack Accounts Through MathLive

TL;DR

Researchers at Parameter found that a public ChatGPT share link could run attacker JavaScript as chatgpt.com in the session of whoever opened it. The payload listed the victim's recent conversations, pulled a private chat the attacker was never given, and sent a piece of it to a server the researchers controlled. They reported it to OpenAI's bug bounty on October 18. OpenAI raised it from P2 to P1, fixed both of its web frontends by October 26 and paid $5,000. Nobody reports exploitation in the wild, and users have nothing to do. If you ship MathLive, or any widget that listens for postMessage, check your own code: the default that opened the door is still the documented default.


What happened

Parameter's Mukul Goyal published the write-up, "Do the Math: How We Took Over ChatGPT Accounts," on October 28, after the fix shipped. The Hacker News covered it today, and the Hacker News thread has passed 176 points.

The attack needed very little from the victim. They did not install anything, join a workspace or paste code. In the most reliable version they opened the link and clicked Preview once. In a later version Parameter got Preview to open automatically on share navigation, so opening the link was enough. The proof-of-concept alert fired about 4.5 seconds after the share loaded, and the automatic path reproduced three times in a separate account.

"Essentially, we could perform any action on behalf of the victim's ChatGPT account." (Parameter)

That limit is whatever ChatGPT's frontend APIs allow. The demo kept to a narrow slice on purpose: the conversation list request was capped at 20 items and the detail request used num_turns=2.

The chain, link by link

No single step here is a scandal. ChatGPT's Preview already runs untrusted HTML in a sandboxed frame on a separate domain, and Parameter says that sandbox held. The problem was a neighbor that took orders from anyone.

1. Shared chatgraph + HTML Preview 2. MathLive listenerorigin check: none 3. Layout = raw HTMLinserted via innerHTML 4. srcdoc iframeno sandbox attribute 5. YouTube JSONPCSP allowlist bypass 6. Signed-in APIsread private chats
Six steps, each reasonable alone. Steps 2 to 4 are the root cause; step 5 only got past the CSP.

1. The share carries two pieces

The attacker publishes a conversation with an ordinary interactive graph and an HTML Preview. The graph loads MathLive, an open-source math input library, into the main ChatGPT page.

2. MathLive listens to everyone

Loading MathLive installs a postMessage listener for its virtual keyboard. That listener did not check who sent the message. It is the upstream default: in MathLive's source, the virtual keyboard sets originValidator = 'none', the public type documents Default: "none", and validateOrigin() returns true straight away when the validator is 'none'.

Think of it as a mail slot in a locked door that accepts any envelope and follows whatever instructions are inside. The sandboxed Preview frame was still on the outside of the door, but it could post letters through the slot.

3. A keyboard layout made of HTML

The Preview frame sends a message that swaps the keyboard layout for attacker HTML, then tells MathLive to show the keyboard. MathLive writes that layout into the page with innerHTML, and its createHTML helper did not sanitize it.

4. An iframe with no sandbox

The injected HTML creates an iframe from srcdoc with no sandbox attribute. A frame like that inherits the parent's origin, so its code is chatgpt.com as far as the browser cares, and it can reach into the parent page.

5. The CSP allowlist

ChatGPT's Content Security Policy blocked inline scripts but allowed YouTube. YouTube's oEmbed JSONP callback echoed the attacker's callback name into its response before returning a validation error, so loading that URL as a script ran attacker code. The final callback was 5,533 characters, carried in a 7,309-character YouTube URL. A "validation error" that executes your payload first is one of the web's better jokes.

6. Acting as the victim

The code read the session bootstrap data, called ChatGPT's authenticated backend APIs, listed recent conversations and fetched a private one. It then sent a small selected piece to an attacker server through an image request.

How the fix went

Parameter found the MathLive message-to-HTML path in production code on October 13 and had arbitrary JavaScript a day later. The report went in on October 18, and OpenAI had the issue closed eight days after that.

disclosure timeline, October 2026 13: path found 14: JS runs 16: cross-account 18: reported 21: P2 to P1 22: 1 of 2 fixed 23: CSP change 26: fixed, $5,000 28: published 8 days from report to fix
From first confirmation to a P1 fix on both frontends took under two weeks.
  • October 21: OpenAI reproduced both the code execution and the private-chat proof and raised severity from P2 to P1.
  • October 22: one of the two ChatGPT web frontends was fixed.
  • October 23: CSP changes blocked part of the chain while the second frontend was still being updated.
  • October 26: both frontends rejected the reported messages. OpenAI marked the issue resolved and awarded $5,000 in total.

The fix is server-side. According to The Hacker News, users do not need to install, reset or change anything. Parameter ran its private-chat proof between its own separate accounts, not against real users.

The bounty drew grumbling on Hacker News, where one commenter found $5,000 very low and worried that small payouts discourage reports. For a P1 that reads private chats, it is a modest number.

Why blocking YouTube was not the fix

The easy patch would have been to drop YouTube from the CSP and move on. Parameter argues that would have left the HTML-injection path open for the next allowlisted endpoint that echoes input. The researchers name three causes, and none of them is YouTube:

  • The MathLive receiver acted on a state-changing message without checking who sent it.
  • The message format allowed raw HTML as keyboard-layout data.
  • That HTML could create an unsandboxed document that inherited the ChatGPT origin.

CSP did its job as a backup: it made the attackers work for step 5. It should not be the only thing between an untrusted frame and your session.

What to check in your own app

Any app that renders model output next to rich widgets (math editors, charts, embeds, previews) can end up with the same shape: untrusted content in one frame, a trusting listener in another.

If you embed MathLive, set the validator yourself instead of relying on the default. The type accepts 'none', 'same-origin' or a function that receives the origin and returns a boolean:

// MathLive: refuse virtual-keyboard messages from other origins
window.mathVirtualKeyboard.originValidator = 'same-origin';

// or an explicit allowlist
window.mathVirtualKeyboard.originValidator =
  (origin) => origin === 'https://app.example.com';

Then audit the rest of your listeners against Parameter's recommendations:

  • Check the sender: verify both event.origin and the exact event.source window before acting on a cross-window message.
  • No HTML over the wire: keep raw HTML out of cross-window settings and configuration messages.
  • Sandbox generated frames: add sandbox to any dynamically created document that an untrusted frame can influence.
  • Audit your CSP allowlist: any allowlisted host with a JSONP or callback endpoint can become script execution.

The write-ups do not mention a change to MathLive itself. The latest MathLive release, 0.111.1 on October 9, fixed separate XSS issues in LaTeX input handling, and the current source still defaults the keyboard's originValidator to 'none'.

Key Takeaways

  • Opening a malicious public ChatGPT share link, plus at most one Preview click, could run attacker JavaScript as chatgpt.com and read private chats.
  • The chain was MathLive's unchecked postMessage listener, raw HTML as keyboard-layout data, an unsandboxed srcdoc iframe, and a YouTube JSONP endpoint to get past CSP.
  • Parameter reported it on October 18. OpenAI rated it P1, fixed both web frontends by October 26 and paid $5,000. No exploitation in the wild is reported, and users have nothing to do.
  • MathLive's virtual keyboard still documents originValidator: "none" as its default. If you embed it, set 'same-origin' or an allowlist function.
  • Check the origin and source of every cross-window message, sandbox generated frames, and treat CSP as a second line of defense.

Sources: Parameter: Do the Math: How We Took Over ChatGPT Accounts, The Hacker News, MathLive on GitHub, OpenAI bug bounty on Bugcrowd, Hacker News discussion

AIOpenAIChatGPTSecurityXSSpostMessageBug BountyWeb Security
CONSOLE
$