Open WebUI: SSRF into internal services via DNS rebinding in the Playwright web loader
High7.7CVE-2026-87996 · Published Sep 10, 2026
Affected versions
| Package | Affected | Fixed in |
|---|---|---|
| open-webui PyPI | >= 0.9.6, < 0.11.1 | 0.11.1 |
Details and references
## Summary With the Playwright web loader enabled, Open WebUI checks the address behind a user-submitted URL before allowing the request, then handed the request to the browser to perform. The browser resolved the hostname a second time, on its own, and that answer was never checked. An attacker who controls the authoritative DNS for a hostname they submit can answer the first lookup with a public address and the second with an internal one, so the browser connects to an address the check exists to block. The connection-layer pinning that protects the other fetch paths could not apply here, because the request ran inside the browser rather than through our own HTTP clients. ## Preconditions - `WEB_LOADER_ENGINE=playwright`. This is not the default, and deployments on the default web loader are unaffected. - A reachable Playwright browser, either local or via `PLAYWRIGHT_WS_URL`. - Any authenticated user who can submit a URL for ingestion or trigger a web search. No administrator role is required. - Control of the authoritative DNS for a hostname the attacker submits, serving a short TTL and alternating answers. No timing race is needed, because the two lookups are separate queries the attacker answers differently. - Internal services the browser process can actually reach. A deployment whose browser has no route to internal addresses loses nothing here. ## Impact An authenticated user can make the server read HTTP responses from addresses only it can reach: cloud instance metadata, loopback-bound admin APIs, and internal services on the same network. The response body is fulfilled back into the page, and the loader returns that page as the document, so the content lands in the web-search or ingestion result the user receives. On a cloud host with IMDSv1 reachable, that is enough to take instance IAM credentials. Because the intercepted request forwards the original method and headers, a page the attacker controls can also drive requests that need a header or a non-GET method, which covers the header-gated metadata endpoints. This is a read primitive; no modification of Open WebUI data and no availability impact was demonstrated. Deployments on the default web loader are not affected at all. ## Fix Fixed in 0.11.1 by commit `27402ff21` (#28634). The interceptor no longer asks the browser to perform the request. It issues the request from the SSRF-safe HTTP client instead, which resolves the hostname once and connects to that same validated address, re-validates every redirect hop, and then fulfils the browser with the response it received. Upgrading to 0.11.1 fully resolves this, and no configuration change is required. ## Root cause Affected component: `SafePlaywrightURLLoader` in `backend/open_webui/retrieval/web/utils.py`, in both the sync and async request interceptors. Affected setup: only builds running the Playwright loader engine. The address check and the connection were performed by two different resolvers in two different processes. Validation resolved the hostname in Python and inspected the result, then the request was handed to the browser, which resolved the name again and connected to whatever it got. Playwright's request-fetch API offers no way to pin a connection to an already-validated address, so the fix that had been applied to the other fetch paths, resolving once and connecting to that same address, could not reach this one. The check was therefore an opinion about an earlier lookup rather than a constraint on the connection that followed. ## Proof of concept Reproduced against the real implementation: the validation and interception code was taken unmodified, with only the module-level configuration constants stubbed at their documented defaults, and driven against a real Chromium instance inside an unprivileged network namespace with an authoritative DNS server answering the first lookup public and the second internal. Instance metadata and a loopback service were both reached, with the response
- CVSS 3.1
- CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N
- Severity from
- GitHub (reviewed advisory)
- Weakness
- CWE-367, CWE-918
- Also known as
- CVE-2026-87996
- github.com/open-webui/open-webui/security/advisories/GHSA-4v28-j6q3-5m4r
- nvd.nist.gov/vuln/detail/CVE-2026-87996
- github.com/open-webui/open-webui/pull/28634
- github.com/open-webui/open-webui/commit/27402ff210bfa253445720920dfb86b15a00327b
- github.com/open-webui/open-webui
- github.com/open-webui/open-webui/releases/tag/v0.11.1
More Open WebUI advisories
All Open WebUI| Date | Advisory | Severity | Fixed in |
|---|---|---|---|
| Sep 9 | Open WebUI: Any authenticated user can hang the server via message deletion in a cyclic chat tree CVE-2026-88000Medium6.5fixed in 0.11.1 | Medium6.5 | 0.11.1 |
| Sep 9 | Open WebUI: Server-side fetches reach blocked and internal hosts via unvalidated HTTP redirect targets CVE-2026-88001Medium5.0fixed in 0.11.1 | Medium5.0 | 0.11.1 |
| Sep 9 | Open WebUI: Any authenticated user can hang the server via a cyclic chat message history CVE-2026-88002Medium6.5fixed in 0.11.1 | Medium6.5 | 0.11.1 |
| Sep 10 | Open WebUI: Users denied by the OAuth domain allowlist or role policy can still sign in via token exchange CVE-2026-88005Medium6.5fixed in 0.9.0 | Medium6.5 | 0.9.0 |
| Sep 10 | Open WebUI: Any authenticated user can reach the Azure platform channel via server-side web fetch CVE-2026-87999High7.1fixed in 0.11.1 | High7.1 | 0.11.1 |
| Sep 10 | Open WebUI: Non-admin users can delete admin-owned external knowledge connections via knowledge base deletion CVE-2026-87998High7.1fixed in 0.11.1 | High7.1 | 0.11.1 |