Open WebUI: Redirect-Bypass SSRF in OAuth `_process_picture_url` (incomplete-fix sibling of CVE-2026-45401)
High8.5CVE-2026-54008 · Published Jun 17, 2026 · updated Jul 13, 2026
## Summary `backend/open_webui/utils/oauth.py::_process_picture_url` (v0.9.5, lines 1435-1470) calls `validate_url(picture_url)` on the initial URL only, then invokes `aiohttp.ClientSession.get(picture_url, ...)` without `allow_redirects=False`. aiohttp's default is `allow_redirects=True, max_redirects=10`; the function does not pass the project's `AIOHTTP_CLIENT_ALLOW_REDIRECTS` env constant either. An attacker with a valid OAuth IdP identity can therefore submit a public URL that 302-redirects to an internal address and read the internal response body via the attacker's own `profile_image_url` field. This is the same redirect-bypass class as CVE-2026-45401 (GHSA-rh5x-h6pp-cjj6), on a 6th call site that the v0.9.5 patch missed. CVE-2026-45401's advisory body enumerates exactly five affected paths — `SafeWebBaseLoader._scrape`, `_fetch`, `get_content_from_url`, `load_url_image`, `get_image_base64_from_url` — none in `utils/oauth.py`. ## Vulnerable code (v0.9.5) `backend/open_webui/utils/oauth.py`, lines 1435-1470: ```python async def _process_picture_url(self, picture_url: str, access_token: str = None) -> str: if not picture_url: return '/user.png' try: ...
Affected versions
| Package | Affected | Fixed in |
|---|---|---|
| open-webui PyPI | < 0.9.6 | 0.9.6 |
Details and references
## Summary `backend/open_webui/utils/oauth.py::_process_picture_url` (v0.9.5, lines 1435-1470) calls `validate_url(picture_url)` on the initial URL only, then invokes `aiohttp.ClientSession.get(picture_url, ...)` without `allow_redirects=False`. aiohttp's default is `allow_redirects=True, max_redirects=10`; the function does not pass the project's `AIOHTTP_CLIENT_ALLOW_REDIRECTS` env constant either. An attacker with a valid OAuth IdP identity can therefore submit a public URL that 302-redirects to an internal address and read the internal response body via the attacker's own `profile_image_url` field. This is the same redirect-bypass class as CVE-2026-45401 (GHSA-rh5x-h6pp-cjj6), on a 6th call site that the v0.9.5 patch missed. CVE-2026-45401's advisory body enumerates exactly five affected paths — `SafeWebBaseLoader._scrape`, `_fetch`, `get_content_from_url`, `load_url_image`, `get_image_base64_from_url` — none in `utils/oauth.py`. ## Vulnerable code (v0.9.5) `backend/open_webui/utils/oauth.py`, lines 1435-1470: ```python 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) # initial URL only get_kwargs = {} if access_token: get_kwargs['headers'] = {'Authorization': f'Bearer {access_token}'} async with aiohttp.ClientSession(trust_env=True) as session: async with session.get(picture_url, **get_kwargs, ssl=AIOHTTP_CLIENT_SESSION_SSL) as resp: # ^^^^^^^^^^^ no allow_redirects=False if resp.ok: picture = await resp.read() 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}' ... ``` The function is invoked at `oauth.py:1556` (new-user OAuth signup) and `oauth.py:1536` (existing-user picture update on login). Neither call site re-validates after redirect-following. `backend/open_webui/retrieval/web/utils.py` (v0.9.5) imports the env constant `AIOHTTP_CLIENT_ALLOW_REDIRECTS` at line 51 and uses it on the five paths patched by CVE-2026-45401. `utils/oauth.py` does not import or reference it. ## Exploitation **Preconditions:** - `ENABLE_OAUTH_SIGNUP=true` or `OAUTH_UPDATE_PICTURE_ON_LOGIN=true` (common in production OAuth-IdP deployments) - Attacker has a valid identity on the configured OAuth IdP (Google, Microsoft, GitHub, or any generic OIDC provider) **Steps:** 1. Attacker hosts a redirect endpoint at `http://attacker.example/r` on a public IP. `validate_url("http://attacker.example/r")` returns True (`is_global=True` for public IPs). 2. Attacker sets their IdP `picture` claim to `http://attacker.example/r`. 3. Attacker signs in to open-webui via OAuth. open-webui invokes `_process_picture_url("http://attacker.example/r", ...)`. 4. `validate_url` accepts the public URL. `session.get("http://attacker.example/r")` is invoked. 5. attacker.example responds `HTTP/1.1 302 Found\r\nLocation: http://127.0.0.1:11434/api/tags`. (Or `http://169.254.169.254/latest/meta-data/iam/security-credentials/`, RFC1918 internal services, etc.) 6. aiohttp follows the redirect server-side. **No re-validation.** 7. The internal response body is read into `picture`, base64-encoded, and stored as `profile_image_url = "data:image/jpeg;base64,..."` on the attacker's account. 8. Attacker reads back via `GET /api/v1/auths/`. Decode the base64 payload to get the full internal response body. ## Impact Full-read SSRF, identical read-back primitive to CVE-2026-45338: - Cloud metadata services (AWS IMDSv1 at `169.254.169.254`
- CVSS 3.1
- CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N
- Severity from
- GitHub (reviewed advisory)
- Weakness
- CWE-918
- Also known as
- CVE-2026-54008, PYSEC-2026-2690
More Open WebUI advisories
All Open WebUI| Date | Advisory | Severity | Fixed in |
|---|---|---|---|
| Jun 17 | Open WebUI: Any authenticated user can read other users' private notes via Socket.IO | Medium5.3 | 0.8.11 |
| Jun 17 | Open WebUI: improper access control | Medium6.3 | 0.9.6 |
| Jun 17 | Open WebUI: RAG ACL Bypass in Milvus Multitenancy Mode | Medium6.5 | 0.9.6 |
| Jun 17 | Open WebUI: SSRF Protection Bypass in Playwright Web Loader via HTTP Redirects | High7.7 | 0.9.6 |
| Jun 17 | Open WebUI: Path traversal / SSRF in terminal server proxy via encoded path traversal | High7.7 | 0.9.6 |
| Jun 17 | Open WebUI BOLA: `search_knowledge_files` Allows Unauthorized Knowledge Base File Enumeration | Medium4.3 | 0.9.6 |