# Open WebUI: Stored web worker XSS via Pyodide

Open WebUI's same-origin Pyodide worker lets a shared chat payload make credentialed API calls as the victim, creating a server-side Function for RCE.

- Payout: Research
- Severity: high
- Program: Open WebUI
- Found by: gg0h
- Disclosed: 2026-07-24
- CVE: CVE-2026-59214
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-4r2p-27mh-5m22
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-4r2p-27mh-5m22
- Tags: area:web domain:web impact:ato impact:privesc impact:rce kind:research platform:ghsa sev:high tech:python technique:chained vuln:access-control vuln:rce vuln:xss

**Target:** Client-side Pyodide web worker in Open WebUI chat code execution
**Root cause:** Pyodide runs in a same-origin web worker, so Python's js bridge and pyfetch inherit the page origin and send the victim's session cookie on requests to the app's own API.
**Steps:**
1. Store Python payload in a chat message and share the chat with the victim.
2. Victim opens the shared chat and clicks Run, executing the Python in the same-origin Pyodide worker.
3. Payload calls `pyfetch('/api/v1/functions/create', method='POST', credentials='include', ...)` with a body whose `content` is `import os; os.system('<attacker command>')`.
4. Created Function/Tool executes server-side Python, yielding RCE.
**How found:** Reporter recognized that Pyodide's js bridge and pyfetch operate in a same-origin worker, giving Python the same reach as inline JavaScript with the victim's cookies, so no separate XSS sink was needed.
**Exploitation:** Low-privileged attacker shares a chat containing Pyodide Python; victim (admin or holder of workspace.functions/workspace.tools) clicks Run; the payload issues authenticated same-origin POST to /api/v1/functions/create with a malicious content body, which then runs server-side.
**Escalation:** Chains client-side code execution in a shared chat to server-side RCE by abusing the victim's privileged Function/Tool creation permission.
**Insight:** When a client-side interpreter (Pyodide, WASM runtimes) is hosted in a same-origin worker, its HTTP bridge is effectively an XSS primitive; check whether user-supplied code can be stored and run by another user, and whether privileged API endpoints (function/tool creation) can be reached with the victim's cookie.

## Proof of concept (copied exactly from the original report)

**Pyodide pyfetch creates server-side Function**

```
from pyodide.http import pyfetch
import json
await pyfetch('/api/v1/functions/create', method='POST', credentials='include',
              headers={'Content-Type': 'application/json'},
              body=json.dumps({'id': 'x', 'name': 'x', 'meta': {'description': 'x'},
                               'content': "import os; os.system('<attacker command>')"}))
```

