# Open WebUI: Same-origin XSS to account takeover via terminal port-preview iframe hardcoding allow-same-origin

Open WebUI's terminal port-preview iframe hardcoded allow-same-origin with allow-scripts on the app origin, letting a malicious previewed port read localStorage token and take over accounts.

- Payout: Research
- Severity: high
- Program: Open WebUI
- Found by: manus-use
- Disclosed: 2026-09-10
- CVE: CVE-2026-87995
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-jmc6-2wr8-h3wj
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-jmc6-2wr8-h3wj
- Tags: area:web domain:web impact:ato impact:rce kind:research platform:ghsa sev:high tech:nodejs technique:incomplete-fix technique:source-review vuln:xss

**Target:** Port preview iframe in chat FileNav (PortPreview.svelte) proxied through terminal server
**Root cause:** The port preview iframe used a static sandbox string containing both allow-scripts and allow-same-origin, and the terminal proxy serves previewed content from the application's own origin, so the sandbox granted no isolation. The iframeSandboxAllowSameOrigin setting was ignored on this branch.
**Steps:**
1. Configure a terminal server reachable by attacker and victim (TERMINAL_SERVER_CONNECTIONS), leave TERMINAL_PROXY_HEADERS and CONTENT_SECURITY_POLICY unset.
2. As an authenticated non-admin user with terminal access, start a process on a port serving HTML with a script that reads `window.parent.localStorage.token` and sends it to an attacker host.
3. Get the victim to open the file navigator, switch to the port list, and click the attacker's port.
4. The preview iframe loads at the application origin with allow-same-origin + allow-scripts; the script executes, reads the token, and exfiltrates it.
**How found:** Noticed every other iframe had been migrated to an opt-in same-origin setting, but PortPreview.svelte still had a static sandbox string with allow-same-origin baked in; it was missed when the file preview got the equivalent fix for 0.11.0. Combined with the terminal proxy being mounted on the app origin and adding no CSP by default, the sandbox was the only boundary and it granted the permission that dissolved it.
**Exploitation:** Attacker with ordinary terminal access serves a page on a listening port; victim clicks that port in the port list; the previewed document runs same-origin, reaches window.parent, reads localStorage.token and exfiltrates it, yielding full account takeover. Admin or workspace.functions victims extend this to server-side code execution via Functions.
**Escalation:** Token theft of an admin or a user with workspace.functions escalates to server-side code execution through Functions.
**Insight:** When a proxy mounts upstream content under your own origin, an iframe sandbox containing both allow-scripts and allow-same-origin is equivalent to no sandbox; audit for leftover static sandbox strings after a project-wide migration to opt-in same-origin, especially on less-traveled components like port/file previews.

