CVE-2026-54008: Open WebUI: Redirect-Bypass SSRF in OAuth `_process_picture_url`

Published Jun 17, 2026
·
Updated

Summary

backend/openwebui/utils/oauth.py::processpictureurl (v0.9.5, lines 1435-1470) calls validateurl(pictureurl) on the initial URL only, then invokes aiohttp.ClientSession.get(pictureurl, ...) without allowredirects=False. aiohttp's default is allowredirects=True, maxredirects=10; the function does not pass the project's AIOHTTPCLIENTALLOWREDIRECTS 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 profileimageurl 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, getcontentfromurl, loadurlimage, getimagebase64fromurl — none in utils/oauth.py.

Vulnerable code (v0.9.5)

backend/openwebui/utils/oauth.py, lines 1435-1470:

python async def processpictureurl(self, pictureurl: str, accesstoken: str = None) -> str: if not pictureurl: return '/user.png' try: validateurl(pictureurl) # initial URL only

getkwargs = {} if accesstoken: getkwargs['headers'] = {'Authorization': f'Bearer {accesstoken}'} async with aiohttp.ClientSession(trustenv=True) as session: async with session.get(pictureurl, getkwargs, ssl=AIOHTTPCLIENTSESSIONSSL) as resp: # ^^^^^^^^^^^ no allowredirects=False if resp.ok: picture = await resp.read() base64encodedpicture = base64.b64encode(picture).decode('utf-8') guessedmimetype = mimetypes.guesstype(pictureurl)[0] if guessedmimetype is None: guessedmimetype = 'image/jpeg' return f'data:{guessedmimetype};base64,{base64encodedpicture}' ...

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/openwebui/retrieval/web/utils.py (v0.9.5) imports the env constant AIOHTTPCLIENTALLOWREDIRECTS 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: - ENABLEOAUTHSIGNUP=true or OAUTHUPDATEPICTUREONLOGIN=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. validateurl("http://attacker.example/r") returns True (isglobal=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 processpictureurl("http://attacker.example/r", ...). 4. validateurl 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 profileimageurl = "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, 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) | processpictureurl had no validateurl() 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) | validateurl() 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::processpictureurl is not among the five paths in the parent advisory's "Affected code paths" section. Same class, missed sink. Direct sibling. |

Suggested fix

python async with session.get( pictureurl, getkwargs, ssl=AIOHTTPCLIENTSESSIONSSL, allowredirects=AIOHTTPCLIENTALLOWREDIRECTS, # add ) as resp:

Or, if redirects must remain enabled by default, wrap in a manual-follow loop that re-invokes validateurl() 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) - CVE-2026-45338 / GHSA-24c9-2m8q-qhmh (original processpictureurl SSRF, patched v0.9.0) - CVE-2026-45400 / GHSA-8w7q-q5jp-jvgx (validateurl parser-disagreement bypass, patched v0.9.5) - 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.

Fresh-CSPRNG sentinel generated after OAuth state-establishing call (per Gate 5.5 oracle protocol): SSRF-POC-5580111b2a0d7d0c8324bfa92a0d9d09.

Result: - profileimageurl field after OAuth login: data:image/jpeg;base64,U1NSRi1QT0MtNTU4MDExMWIyYTBkN2QwYzgzMjRiZmE5MmEwZDlkMDk= - Base64 decode: SSRF-POC-5580111b2a0d7d0c8324bfa92a0d9d09 (byte-for-byte sentinel match) - Canary log: !!! SSRF HIT - sentinel served

Chain confirmed: OAuth login → IdP returns picture claim evil.example.com:9001/redirect → validateurl() accepts FQDN → aiohttp.ClientSession.get(...) follows 302 to internal-target.local:9002/sentinel server-side without re-validation → response body base64-encoded into attacker's profileimageurl → readable via GET /api/v1/auths/.

PoC artifacts (compose, attacker server, canary, run/verify scripts, full transcript) available on request.

Reporter

Matteo Panzeri — GitHub: matte1782, contact: matteo1782@gmail.com. Requesting CVE credit as Matteo Panzeri.

Other sources

Open WebUI is a self-hosted artificial intelligence platform designed to operate entirely offline. Prior to 0.9.6, backend/openwebui/utils/oauth.py::processpictureurl calls validateurl(pictureurl) on the initial URL only, then invokes aiohttp.ClientSession.get(pictureurl, ...) without allowredirects=False. aiohttp's default is allowredirects=True, maxredirects=10; the function does not pass the project's AIOHTTPCLIENTALLOWREDIRECTS 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 profileimageurl field. This vulnerability is fixed in 0.9.6.

MITRE

Affected Software

2 affected componentsFixes available
pip/open-webui<=0.9.5
0.9.6
openwebui Open WebUI<0.9.6

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade pip/open-webui to a version that resolves this vulnerability.

    Fixed in 0.9.6
  2. Upgrade

    Upgrade to a fixed release to a version that resolves this vulnerability.

    Fixed in 0.9.6
  3. Configuration

    In backend/open_webui/utils/oauth.py::_process_picture_url, ensure aiohttp is called with allow_redirects=False (or otherwise prevent automatic 302/Location redirect-following) instead of aiohttp default allow_redirects=True, so the internal redirect target is not fetched server-side.

    open-webui backend (oauth picture fetch) aiohttp.ClientSession.get allow_redirects = false
  4. Configuration

    If redirects must remain enabled, implement a manual-follow loop in backend/open_webui/utils/oauth.py::_process_picture_url that extracts each Location header and re-invokes validate_url() on every redirect target before performing the next aiohttp request.

    open-webui backend (oauth picture fetch) redirect handling (validate_url re-invocation on Location) = re-validate each redirect target

Event History

Jun 17, 2026
Advisory Published
via GitHub·02:10 PM
Data Sourced
via GitHub·02:10 PM
DescriptionSeverityWeaknessAffected Software
Jun 23, 2026
CVE Published
via MITRE·04:50 PM
Data Sourced
via MITRE·04:50 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·06:18 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

What is the severity of CVE-2026-54008?

CVE-2026-54008 has a high severity score of 8.5.

2

What type of vulnerability is CVE-2026-54008?

CVE-2026-54008 is classified as a Server-Side Request Forgery (SSRF) vulnerability.

3

How do I fix CVE-2026-54008?

To fix CVE-2026-54008, ensure that the `aiohttp.ClientSession.get` call has `allow_redirects=False`.

4

What component is affected by CVE-2026-54008?

CVE-2026-54008 affects the `backend/open_webui/utils/oauth.py` module in version 0.9.5 of the open-webui software.

5

When was CVE-2026-54008 published?

CVE-2026-54008 was published on June 17, 2026.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203