# How Parameter Labs Hijacked ChatGPT Accounts via HTML Previews

> Discover the detailed attack chain that let untrusted HTML previews take over ChatGPT accounts, the sandbox failures, and lessons for developers building LLM‑driven UIs.

- URL: https://ferdousazad.com/blog/how-parameter-labs-hijacked-chatgpt-accounts-html-previews
- Author: Ferdous Azad (Founder @ DataXgen AI)
- Published: 2026-11-02
- Updated: 2026-11-02
- Tags: security, LLM, ChatGPT, sandboxing, web‑exploits

## TL;DR  

Parameter Labs uncovered a multi‑stage exploit in ChatGPT’s “HTML previews” (formerly “Canvas”). By abusing the MathLive library’s postMessage interface, an attacker could inject unsandboxed iframes, bypass CSP via YouTube’s oEmbed endpoint, and run JavaScript in the parent page. The resulting code could read a signed‑in user’s private data and exfiltrate it, effectively hijacking any ChatGPT account that visited a malicious share link. OpenAI patched the bug and issued a $5 000 bounty.

---

## What’s new  

| Feature | Description | Security notes |
|---------|-------------|----------------|
| **HTML previews** (aka “Canvas”) | Allows developers to embed interactive custom HTML apps inside a sandboxed `<iframe>` on a ChatGPT conversation. | Intended to be isolated from the parent page and from OpenAI APIs. |
| **MathLive integration** | A JavaScript library used for rendering math graphs; includes a virtual keyboard that can be triggered via `postMessage`. | The message handler writes the payload into `innerHTML` of the parent, creating a path for HTML injection. |
| **CSP policy** | Blocks inline scripts and restricts external scripts to a whitelist of domains. | YouTube’s oEmbed endpoint is on the whitelist, unintentionally enabling script reflection. |

These components were introduced to make ChatGPT more interactive, but the combination opened a chain of trust violations that Parameter Labs turned into a full‑account takeover.

---

## How the attack chain works  

> [!NOTE]  
> The following diagram abstracts the flow; each arrow represents a single step in the exploit.

```mermaid
flowchart TD
    A["Attacker crafts malicious HTML preview"] --> B["Iframe loads MathLive library"]
    B --> C["Attacker sends postMessage to parent"]
    C --> D["Parent handler uses innerHTML to insert attacker HTML"]
    D --> E["Injected iframe lacks sandbox attribute"]
    E --> F["CSP allows script from YouTube oEmbed"]
    F --> G["YouTube oEmbed reflects attacker‑controlled JS"]
    G --> H["JS runs in ChatGPT context"]
    H --> I["Calls OpenAI APIs with user token"]
    I --> J["Exfiltrates data via <img src>"]
    J --> K["Attacker gains account control"]
```

### Step‑by‑step breakdown  

1. **Craft a malicious HTML preview** – The attacker creates an HTML snippet that loads the MathLive library (required for graph rendering).  
2. **Trigger the virtual keyboard** – From inside the sandboxed iframe, the attacker calls `postMessage` to the parent page, requesting the virtual keyboard.  
3. **Parent processes the message** – ChatGPT’s message‑handling code inserts the received payload into the DOM using `innerHTML`. No validation is performed, and the code does not restrict which sandboxed frames may invoke it.  
4. **Inject an unsandboxed iframe** – Because `innerHTML` can embed arbitrary tags, the attacker adds a new `<iframe>` without a `sandbox` attribute, breaking the isolation model.  
5. **Leverage CSP whitelist** – The CSP blocks inline scripts but permits loading scripts from a small set of domains, including `youtube.com`.  
6. **Use YouTube oEmbed for script reflection** – The attacker points the unsandboxed iframe to YouTube’s oEmbed endpoint, which returns a JSON payload that can contain a `<script>` tag with attacker‑controlled JavaScript.  
7. **Execute JavaScript in the ChatGPT page** – The reflected script runs with the same privileges as the ChatGPT UI, giving the attacker access to the logged‑in user’s session token and API keys stored in browser memory.  
8. **Fetch private data** – The malicious script calls OpenAI’s internal APIs (e.g., `/v1/users/me`) to retrieve conversation history, saved files, and other personal data.  
9. **Exfiltrate via an image request** – Data is encoded into a URL and sent to the attacker’s server using an `<img src>` tag, bypassing most CSP‑based data‑leak protections.  

The end result is a **shareable URL** that looks innocuous but, when opened by any signed‑in user, silently steals their account data or performs actions on their behalf.

---

## How it compares to prior ChatGPT sandbox issues  

| Aspect | Earlier Canvas bugs (2023‑2024) | HTML previews exploit (2026) |
|--------|--------------------------------|------------------------------|
| **Root cause** | Overly permissive `allow-scripts` in sandbox attribute. | `innerHTML` injection + CSP whitelist misuse. |
| **Attack surface** | Direct script execution inside iframe. | Cross‑frame `postMessage` → DOM injection → CSP bypass. |
| **Complexity** | Single‑step script injection. | Multi‑stage chain requiring three distinct components (MathLive, postMessage, YouTube oEmbed). |
| **Impact** | Limited to the iframe’s origin. | Full account takeover across any signed‑in user. |
| **Mitigation** | Tighten sandbox flags, disallow `allow-scripts`. | Validate `postMessage` origins, avoid `innerHTML`, restrict CSP whitelist. |

The 2026 exploit is more sophisticated because it combines a legitimate third‑party library (MathLive) with a CSP misconfiguration, illustrating how “trusted” dependencies can become attack vectors when their integration points are not hardened.

---

## How to try safe HTML previews (or reproduce the bug in a controlled environment)  

> [!TIP]  
> Never test the live exploit on production accounts. Use an isolated browser profile with no logged‑in OpenAI session.

Below is a minimal example of a **secure** HTML preview that respects the same sandboxing constraints but avoids the vulnerable `innerHTML` path. The code demonstrates how to listen for `postMessage` safely and inject content using DOM APIs that do not evaluate scripts.

```python
# Install the OpenAI Python client (if you haven't)
# pip install openai

import openai

# Create a shareable preview that only displays static HTML.
# Replace <YOUR_API_KEY> with a test key that has no real user data.
client = openai.ChatCompletion.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "user", "content": "Show a simple math graph using MathLive."}
    ],
    # The `artifacts` field is where HTML previews are attached.
    artifacts=[
        {
            "type": "html",
            "content": """
                <html>
                <head>
                  <script src="https://cdn.jsdelivr.net/npm/mathlive@0.88.0/dist/mathlive.min.js"></script>
                </head>
                <body>
                  <math-field id="mf"></math-field>
                  <script>
                    // Safe message handling – only accept messages from our own iframe.
                    window.addEventListener('message', (e) => {
                      if (e.origin !== location.origin) return;
                      const {type, payload} = e.data;
                      if (type === 'setLatex') {
                        document.getElementById('mf').setValue(payload);
                      }
                    });
                  </script>
                </body>
                </html>
            """
        }
    ]
)

print("Share URL:", client["share_url"])
```

**Key safety points in the snippet**

* The preview uses a **static script** from a trusted CDN; no dynamic script loading is allowed.  
* The `postMessage` handler checks `e.origin` against `location.origin`, preventing other iframes from injecting messages.  
* Content is inserted via the MathLive API (`setValue`) instead of `innerHTML`.  

If you need to test the vulnerability itself, spin up a local HTML file that mimics the MathLive `postMessage` call and observe that the parent page’s `innerHTML` insertion leads to an unsandboxed iframe. Do this **only** in a disposable environment with no real credentials.

---

## Limitations and open questions  

* **Dependency‑driven risk** – The exploit hinged on MathLive’s virtual keyboard exposing a `postMessage` entry point. Are other third‑party widgets (e.g., charting libraries, code editors) exposing similar surfaces? A systematic audit of all embedded libraries is required.  
* **CSP whitelist granularity** – OpenAI’s CSP allowed any script from `youtube.com`. Should CSP be expressed with stricter sub‑resource integrity (SRI) hashes or nonce‑based policies to prevent reflection attacks?  
* **Sandbox enforcement** – The current sandbox attribute on the preview iframe does not prevent the parent from writing to its own DOM. Would a “double‑sandbox” model (parent also sandboxed) mitigate this class of attacks?  
* **Bug bounty sizing** – Parameter Labs received $5 000 for a vulnerability that enables full account takeover. The community is debating whether the reward reflects the true risk. Future bounty programs may need tiered payouts based on potential impact.  
* **Detection** – The attack chain leaves minimal network traces because the exfiltration occurs via an `<img>` request. Are there reliable client‑side heuristics (e.g., sudden creation of unsandboxed iframes) that can flag malicious previews in real time?

---

## FAQ  

**Q: Can I still use HTML previews safely after the patch?**  
A: Yes. OpenAI now validates the origin of `postMessage` events, rejects any payload that attempts to set `innerHTML`, and removed YouTube from the CSP whitelist for preview contexts.

**Q: Does the exploit affect other OpenAI products like DALL·E or Whisper?**  
A: The vulnerability is specific to the ChatGPT UI’s HTML preview implementation. Other products that do not embed arbitrary HTML are not directly impacted.

**Q: Should I audit my own LLM‑driven web apps for similar issues?**  
A: Absolutely. Any feature that lets users supply HTML, JavaScript, or other executable content should be isolated with a strict CSP, avoid `innerHTML`, and verify message origins before acting on them.

---

## Key takeaways  

- **Sandboxing is only as strong as its integration points** – A single `innerHTML` call can collapse an entire isolation model.  
- **Third‑party libraries can unintentionally expose privileged APIs** (MathLive’s virtual keyboard).  
- **CSP whitelists must be reviewed for reflection vectors**; allowing domains like YouTube can enable script injection via oEmbed.  
- **Bug bounty rewards should reflect the severity of account‑takeover bugs**; the $5 000 payout was widely considered low.  
- **Defensive coding practices** – validate message origins, avoid `innerHTML`, and use SRI or nonces for script loading.  

By treating every user‑generated artifact as potentially hostile and enforcing defense‑in‑depth at the browser level, we can keep LLM‑enhanced UIs both powerful and safe.

## Sources

- https://simonwillison.net/2026/Oct/31/how-we-took-over-chatgpt-accounts/
