A shared ChatGPT link could take over accounts via MathLive's virtual keyboard; OpenAI paid a $5,000 bounty
A public ChatGPT share link was enough to run JavaScript as chatgpt.com in the victim's own session, read their session token and pull their recent conversations. Mukul Goyal of the security firm Parameter published the full chain on October 28. OpenAI fixed it on October 26 and paid a $5,000 bug bounty.
In the worst variant, opening the link was the only click. Goyal says the auto-preview version of the exploit worked three times out of three and finished in about 4.5 seconds.
How the chain worked
The chain combined a third-party math editor with ChatGPT's own HTML preview and Content Security Policy:
- ChatGPT's interactive graphs load MathLive, an open-source math input library. MathLive's virtual keyboard installs a
postMessagelistener in the main chatgpt.com page, and its origin check was set to"none", so it accepted messages from any frame. - ChatGPT's HTML Preview runs model-generated HTML in a sandboxed iframe. That sandbox can still post messages to its parent. The attacker's page sent a
mathlive#virtual-keyboard-messagewith actionupdate-setting, carrying a keyboard layout whose markup was arbitrary HTML, followed byshow. - MathLive renders layout markup with
innerHTML, passing it throughMathfieldElement.createHTML, which by default returns the string unchanged rather than sanitizing it. - The injected HTML included an
<iframe srcdoc>without a sandbox attribute. Such an iframe inherits the chatgpt.com origin, so script in it ran with the user's session. - ChatGPT's Content Security Policy allowed YouTube's oEmbed endpoint, which supports a JSONP
callbackparameter. That turned an allowed YouTube URL into a way to execute attacker-chosen JavaScript.
The proof-of-concept payload read the session token from the page's #client-bootstrap element, called /backend-api/conversations to list the last 20 chats, fetched their content, and sent the result out through an image request.
Timeline
Goyal confirmed the path on October 13 and reported it on October 18. OpenAI reproduced it on October 21 and raised the severity from P2 to P1, shipped partial fixes and CSP changes on October 22 and 23, and marked the issue resolved on October 26.
The library default has not changed
OpenAI's fix covers ChatGPT. We checked MathLive itself. On the main branch of arnog/mathlive, whose latest commit is from October 9, the defaults the chain relied on are still in place:
- the virtual keyboard's
originValidatoris initialized to"none", andvalidateOrigin()accepts every origin in that case; - the keyboard listens for messages on
window.topwhen it can reach it; update-settingmessages can replace the keyboardlayouts, and a layer'smarkupstring is inserted into the keyboard's HTML as is;MathfieldElement.createHTMLdefaults to(x) => x.
These are documented defaults rather than a hidden bug. MathLive's docs recommend a sanitizing createHTML when displaying untrusted content, and originValidator can be set to "same-origin" or a function. But a page that loads MathLive with its defaults and also frames content it does not control has the same shape of exposure ChatGPT had. We found no MathLive advisory or issue about this so far. If you ship MathLive next to untrusted iframes, setting both options is a small change:
MathfieldElement.createHTML = (html) => DOMPurify.sanitize(html);
window.mathVirtualKeyboard.originValidator = "same-origin";
Goyal's write-up has the full payloads and screenshots of each step.
Comments
No comments yet. Be the first.