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:
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).
Attacker sets their IdP picture claim to http://attacker.example/r.
Attacker signs in to open-webui via OAuth. open-webui invokes _process_picture_url("http://attacker.example/r", ...).
validate_url accepts the public URL. session.get("http://attacker.example/r") is invoked.
aiohttp follows the redirect server-side. No re-validation.
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.
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, GCP metadata.google.internal, Azure IMDS) → IAM credentials, managed-identity tokens
Localhost-bound services (Ollama at :11434, Redis, Elasticsearch, internal Postgres exporters)
RFC1918 internal infrastructure not exposed to the internet
Distinction from prior CVEs
Prior CVE
This finding
Distinguishing fact
CVE-2026-45338 (GHSA-24c9)
_process_picture_url had no validate_url() call at all
Fixed in v0.9.0 by adding the call. Ours is the call being insufficient because it doesn't loop over redirect targets. Different mechanism, different fix.
CVE-2026-45400 (GHSA-8w7q)
validate_url() had urlparse-vs-requests parser disagreement on \@ chars
Fixed in v0.9.5 by char-blocklist. Ours is post-validation redirect-following — orthogonal mechanism.
CVE-2026-45401 (GHSA-rh5x)
Five paths in retrieval, routers/images, utils/files, utils/middleware
Parent class. Same CWE-918 redirect-bypass mechanism. utils/oauth.py::_process_picture_url is not among the five paths in the parent advisory's "Affected code paths" section. Same class, missed sink. Direct sibling.
Suggested fix
async with session.get(
picture_url,
**get_kwargs,
ssl=AIOHTTP_CLIENT_SESSION_SSL,
allow_redirects=AIOHTTP_CLIENT_ALLOW_REDIRECTS, # add
) as resp:
Or, if redirects must remain enabled by default, wrap in a manual-follow loop that re-invokes validate_url() on each Location header. This mirrors the fix shape applied to the five paths in CVE-2026-45401.
Affected versions
Vulnerable: <= 0.9.5
Fix: 0.9.6
References
CVE-2026-45401 / GHSA-rh5x-h6pp-cjj6 (parent cluster, redirect-bypass on 5 paths)
open-webui issue #24560 (corroborates that the v0.9.5 redirect-fix was applied piecemeal across call sites)
Proof of Concept
End-to-end PoC executed against ghcr.io/open-webui/open-webui:v0.9.5 in Docker compose. Three services: attacker (OIDC IdP + 302-redirect endpoint on evil.example.com:9001/redirect), canary (internal target on internal-target.local:9002/sentinel), open-webui v0.9.5.