{"id":"CVE-2026-54021","aliases":["EUVD-2026-38522","GHSA-9rpj-v7hf-vv2w","PYSEC-2026-2718"],"status":"published","title":"Open WebUI: Authenticated users can target arbitrary configured Ollama backends via unguarded url_idx path parameter","description":"Open WebUI is a self-hosted artificial intelligence platform designed to operate entirely offline. Prior to 0.9.6, several direct, index-addressed Ollama proxy routes accept a caller-supplied url_idx path parameter and use it as a raw index into the admin-configured OLLAMA_BASE_URLS list. Access control on these routes validates only whether the user may use the requested model, never which backend the request is routed to. Any authenticated user can append an arbitrary url_idx to force their request onto an Ollama backend they were never authorized to reach, including internal, higher-privilege, or explicitly admin-disabled backends. This vulnerability is fixed in 0.9.6.","severity":{"version":"3.1","score":6.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-10","score":0.00283,"percentile":0.20999,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-863"],"published":"2026-06-17T18:01:50Z","modified":"2026-10-28T02:30:48Z","fixes":[{"ecosystem":"PyPI","package":"open-webui","fixed_in":["0.9.6"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"open-webui","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"0.9.5"},{"fixed":"0.9.6"}]}]},{"source":"osv","ecosystem":"PyPI","package":"open-webui","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.9.6"}]}],"versions":["0.1.124","0.1.125","0.2.0","0.2.1","0.2.2","0.2.3","0.2.4","0.2.5","0.3.0","0.3.1","0.3.10","0.3.12","0.3.13","0.3.14","0.3.15","0.3.16","0.3.17","0.3.17.dev2","0.3.17.dev3","0.3.17.dev4","0.3.17.dev5","0.3.18","0.3.19","0.3.2","0.3.20","0.3.21","0.3.22","0.3.23","0.3.24","0.3.25","0.3.26","0.3.27","0.3.27.dev1","0.3.27.dev2","0.3.27.dev3","0.3.28","0.3.29","0.3.3","0.3.30","0.3.30.dev1","0.3.30.dev2","0.3.31","0.3.31.dev1","0.3.32","0.3.33","0.3.33.dev1","0.3.34","0.3.35","0.3.4","0.3.5","0.3.6","0.3.7","0.3.8","0.3.9","0.4.0","0.4.0.dev1","0.4.0.dev2","0.4.1","0.4.2","0.4.3","0.4.4","0.4.5","0.4.6","0.4.6.dev1","0.4.7","0.4.8","0.5.0","0.5.0.dev1","0.5.0.dev2","0.5.1","0.5.10","0.5.11","0.5.12","0.5.13","0.5.14","0.5.15","0.5.16","0.5.17","0.5.18","0.5.19","0.5.2","0.5.20","0.5.3","0.5.3.dev1","0.5.4","0.5.5","0.5.6","0.5.7","0.5.8","0.5.9","0.6.0","0.6.1","0.6.10","0.6.11","0.6.12","0.6.13","0.6.14","0.6.15","0.6.16","0.6.18","0.6.19","0.6.2","0.6.20","0.6.21","0.6.22","0.6.23","0.6.24","0.6.25","0.6.26","0.6.26.dev1","0.6.27","0.6.28","0.6.29","0.6.3","0.6.30","0.6.31","0.6.32","0.6.33","0.6.34","0.6.35","0.6.36","0.6.37","0.6.38","0.6.39","0.6.4","0.6.40","0.6.41","0.6.42","0.6.43","0.6.5","0.6.6","0.6.6.dev1","0.6.7","0.6.8","0.6.9","0.7.0","0.7.1","0.7.2","0.8.0","0.8.1","0.8.10","0.8.11","0.8.12","0.8.2","0.8.3","0.8.4","0.8.5","0.8.6","0.8.7","0.8.8","0.8.9","0.9.0","0.9.1","0.9.2","0.9.3","0.9.4","0.9.5"]},{"source":"osv","ecosystem":"PyPI","package":"open-webui","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.9.6"}]}],"versions":["0.1.124","0.1.125","0.2.0","0.2.1","0.2.2","0.2.3","0.2.4","0.2.5","0.3.0","0.3.1","0.3.10","0.3.12","0.3.13","0.3.14","0.3.15","0.3.16","0.3.17","0.3.17.dev2","0.3.17.dev3","0.3.17.dev4","0.3.17.dev5","0.3.18","0.3.19","0.3.2","0.3.20","0.3.21","0.3.22","0.3.23","0.3.24","0.3.25","0.3.26","0.3.27","0.3.27.dev1","0.3.27.dev2","0.3.27.dev3","0.3.28","0.3.29","0.3.3","0.3.30","0.3.30.dev1","0.3.30.dev2","0.3.31","0.3.31.dev1","0.3.32","0.3.33","0.3.33.dev1","0.3.34","0.3.35","0.3.4","0.3.5","0.3.6","0.3.7","0.3.8","0.3.9","0.4.0","0.4.0.dev1","0.4.0.dev2","0.4.1","0.4.2","0.4.3","0.4.4","0.4.5","0.4.6","0.4.6.dev1","0.4.7","0.4.8","0.5.0","0.5.0.dev1","0.5.0.dev2","0.5.1","0.5.10","0.5.11","0.5.12","0.5.13","0.5.14","0.5.15","0.5.16","0.5.17","0.5.18","0.5.19","0.5.2","0.5.20","0.5.3","0.5.3.dev1","0.5.4","0.5.5","0.5.6","0.5.7","0.5.8","0.5.9","0.6.0","0.6.1","0.6.10","0.6.11","0.6.12","0.6.13","0.6.14","0.6.15","0.6.16","0.6.18","0.6.19","0.6.2","0.6.20","0.6.21","0.6.22","0.6.23","0.6.24","0.6.25","0.6.26","0.6.26.dev1","0.6.27","0.6.28","0.6.29","0.6.3","0.6.30","0.6.31","0.6.32","0.6.33","0.6.34","0.6.35","0.6.36","0.6.37","0.6.38","0.6.39","0.6.4","0.6.40","0.6.41","0.6.42","0.6.43","0.6.5","0.6.6","0.6.6.dev1","0.6.7","0.6.8","0.6.9","0.7.0","0.7.1","0.7.2","0.8.0","0.8.1","0.8.10","0.8.11","0.8.12","0.8.2","0.8.3","0.8.4","0.8.5","0.8.6","0.8.7","0.8.8","0.8.9","0.9.0","0.9.1","0.9.2","0.9.3","0.9.4","0.9.5"]}],"products":[{"vendor":"open-webui","product":"open-webui"},{"vendor":"openwebui","product":"open_webui"}],"references":[{"url":"https://github.com/open-webui/open-webui/security/advisories/GHSA-9rpj-v7hf-vv2w","tags":["x_refsource_CONFIRM","Vendor Advisory","advisory","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-54021","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-9rpj-v7hf-vv2w","tags":["advisory"]},{"url":"https://github.com/open-webui/open-webui","tags":["package"]},{"url":"https://github.com/pypa/advisory-database/tree/main/vulns/open-webui/PYSEC-2026-2718.yaml","tags":["web"]},{"url":"https://pypi.org/project/open-webui","tags":["web","package"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/54xxx/CVE-2026-54021.json","tags":["advisory"]}],"sources":{"cve":[{"id":"CVE-2026-54021","title":"Open WebUI: Authenticated users can target arbitrary configured Ollama backends via unguarded url_idx path parameter","description":"Open WebUI is a self-hosted artificial intelligence platform designed to operate entirely offline. Prior to 0.9.6, several direct, index-addressed Ollama proxy routes accept a caller-supplied url_idx path parameter and use it as a raw index into the admin-configured OLLAMA_BASE_URLS list. Access control on these routes validates only whether the user may use the requested model, never which backend the request is routed to. Any authenticated user can append an arbitrary url_idx to force their request onto an Ollama backend they were never authorized to reach, including internal, higher-privilege, or explicitly admin-disabled backends. This vulnerability is fixed in 0.9.6.","status":"published","published":"2026-06-23T16:39:58.041Z","modified":"2026-06-26T19:21:06.832Z","cvss":[{"version":"3.1","score":6.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L","level":"medium","assessor":"cna"}],"cwes":["CWE-863"],"updated_at":"2026-11-02T13:37:58.613221Z"}],"euvd":[{"id":"EUVD-2026-38522","description":"Open WebUI is a self-hosted artificial intelligence platform designed to operate entirely offline. Prior to 0.9.6, several direct, index-addressed Ollama proxy routes accept a caller-supplied url_idx path parameter and use it as a raw index into the admin-configured OLLAMA_BASE_URLS list. Access control on these routes validates only whether the user may use the requested model, never which backend the request is routed to. Any authenticated user can append an arbitrary url_idx to force their request onto an Ollama backend they were never authorized to reach, including internal, higher-privilege, or explicitly admin-disabled backends. This vulnerability is fixed in 0.9.6.","status":"published","published":"2026-06-23T16:39:58Z","modified":"2026-06-26T19:21:06Z","cvss":[{"version":"3.1","score":6.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L","level":"medium"}],"updated_at":"2026-11-02T18:20:23.728044Z"}],"ghsa":[{"id":"GHSA-9rpj-v7hf-vv2w","title":"Open WebUI: Authenticated users can target arbitrary configured Ollama backends via unguarded url_idx path parameter","description":"## Summary\n\nSeveral direct, index-addressed Ollama proxy routes accept a caller-supplied `url_idx`\npath parameter and use it as a raw index into the admin-configured `OLLAMA_BASE_URLS`\nlist. Access control on these routes validates only whether the user may use the\nrequested *model*, never which *backend* the request is routed to. Any authenticated\nuser can append an arbitrary `url_idx` to force their request onto an Ollama backend\nthey were never authorized to reach, including internal, higher-privilege, or\nexplicitly admin-disabled backends.\n\n## Affected endpoints\n\nAll indexed Ollama routes that resolve the backend through `get_ollama_url()`:\n\n```\nPOST /ollama/api/chat/{url_idx}\nPOST /ollama/api/generate/{url_idx}\nPOST /ollama/api/embed/{url_idx}\nPOST /ollama/api/embeddings/{url_idx}\nPOST /ollama/v1/chat/completions/{url_idx}\nPOST /ollama/v1/completions/{url_idx}\nPOST /ollama/v1/messages/{url_idx}\nPOST /ollama/v1/responses/{url_idx}\n```\n\n## Root cause\n\n`backend/open_webui/routers/ollama.py` — `get_ollama_url()` consults the\nmodel-to-backend allow-list (`OLLAMA_MODELS[model][\"urls\"]`) only when `url_idx` is\nomitted. When the caller supplies `url_idx`, that mapping is skipped and the value is\nused directly as an index:\n\n```python\nasync def get_ollama_url(request: Request, model: str, url_idx: Optional[int] = None):\n    if url_idx is None:\n        models = request.app.state.OLLAMA_MODELS\n        if model not in models:\n            raise HTTPException(...)\n        url_idx = random.choice(models[model].get(\"urls\", []))\n    url = request.app.state.config.OLLAMA_BASE_URLS[url_idx]   # caller-controlled, no authz\n    return url, url_idx\n```\n\nThe outbound request is then sent to that backend using the backend's own configured\nAPI key. Backends an admin has disabled (`OLLAMA_API_CONFIGS[\"\u003cidx\u003e\"].enable = false`)\nare hidden from model discovery but remain reachable through the indexed route, because\nthe disabled state is never re-checked at request time.\n\n## Impact\n\nA verified, non-admin user with read access to any single model can:\n- route requests to internal / higher-capability / restricted Ollama backends in\n  multi-backend deployments, bypassing backend-level isolation;\n- reach backends the admin has explicitly disabled;\n- have those requests authenticated with the target backend's configured API key\n  (the key is used server-side; it is not returned to the attacker);\n- consume the restricted backend's compute.\n\nThere is no cross-user data disclosure and no exfiltration of the backend credential\nitself; the impact is unauthorized access to, and use of, restricted backend resources.\n\n## Affected / Patched\n\n- Affected: `\u003c= 0.9.5`\n- Patched: `\u003e= 0.9.6`\n\n## Fix\n\n0.9.6 adds `validate_ollama_backend_idx()`, invoked on every indexed route (directly and\nvia `get_ollama_url()`), which returns 403 for any non-admin caller-supplied `url_idx`\nthat is not in the requested model's allowed `urls`. Because disabled backends are absent\nfrom every model's `urls`, the same check also blocks routing to disabled backends.","status":"published","published":"2026-06-17T18:01:50Z","modified":"2026-07-20T21:11:11Z","cvss":[{"version":"3.1","score":6.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L","level":"medium"}],"cwes":["CWE-863"],"updated_at":"2026-11-02T14:40:47.245721Z"}],"nvd":[{"id":"CVE-2026-54021","description":"Open WebUI is a self-hosted artificial intelligence platform designed to operate entirely offline. Prior to 0.9.6, several direct, index-addressed Ollama proxy routes accept a caller-supplied url_idx path parameter and use it as a raw index into the admin-configured OLLAMA_BASE_URLS list. Access control on these routes validates only whether the user may use the requested model, never which backend the request is routed to. Any authenticated user can append an arbitrary url_idx to force their request onto an Ollama backend they were never authorized to reach, including internal, higher-privilege, or explicitly admin-disabled backends. This vulnerability is fixed in 0.9.6.","status":"published","published":"2026-06-23T18:18:07.37Z","modified":"2026-06-26T20:17:26.07Z","cvss":[{"version":"3.1","score":6.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L","level":"medium","assessor":"secondary"}],"cwes":["CWE-863"],"updated_at":"2026-11-02T14:12:25.288279Z"}],"osv":[{"id":"CVE-2026-54021","title":"Open WebUI: Authenticated users can target arbitrary configured Ollama backends via unguarded url_idx path parameter","description":"Open WebUI is a self-hosted artificial intelligence platform designed to operate entirely offline. Prior to 0.9.6, several direct, index-addressed Ollama proxy routes accept a caller-supplied url_idx path parameter and use it as a raw index into the admin-configured OLLAMA_BASE_URLS list. Access control on these routes validates only whether the user may use the requested model, never which backend the request is routed to. Any authenticated user can append an arbitrary url_idx to force their request onto an Ollama backend they were never authorized to reach, including internal, higher-privilege, or explicitly admin-disabled backends. This vulnerability is fixed in 0.9.6.","status":"published","published":"2026-06-23T16:39:58.041Z","modified":"2026-10-28T02:30:48.308429491Z","cvss":[{"version":"3.1","score":6.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L","level":"medium"}],"cwes":["CWE-863"],"updated_at":"2026-11-02T15:45:08.132336Z"},{"id":"GHSA-9rpj-v7hf-vv2w","title":"Open WebUI: Authenticated users can target arbitrary configured Ollama backends via unguarded url_idx path parameter","description":"## Summary\n\nSeveral direct, index-addressed Ollama proxy routes accept a caller-supplied `url_idx`\npath parameter and use it as a raw index into the admin-configured `OLLAMA_BASE_URLS`\nlist. Access control on these routes validates only whether the user may use the\nrequested *model*, never which *backend* the request is routed to. Any authenticated\nuser can append an arbitrary `url_idx` to force their request onto an Ollama backend\nthey were never authorized to reach, including internal, higher-privilege, or\nexplicitly admin-disabled backends.\n\n## Affected endpoints\n\nAll indexed Ollama routes that resolve the backend through `get_ollama_url()`:\n\n```\nPOST /ollama/api/chat/{url_idx}\nPOST /ollama/api/generate/{url_idx}\nPOST /ollama/api/embed/{url_idx}\nPOST /ollama/api/embeddings/{url_idx}\nPOST /ollama/v1/chat/completions/{url_idx}\nPOST /ollama/v1/completions/{url_idx}\nPOST /ollama/v1/messages/{url_idx}\nPOST /ollama/v1/responses/{url_idx}\n```\n\n## Root cause\n\n`backend/open_webui/routers/ollama.py` — `get_ollama_url()` consults the\nmodel-to-backend allow-list (`OLLAMA_MODELS[model][\"urls\"]`) only when `url_idx` is\nomitted. When the caller supplies `url_idx`, that mapping is skipped and the value is\nused directly as an index:\n\n```python\nasync def get_ollama_url(request: Request, model: str, url_idx: Optional[int] = None):\n    if url_idx is None:\n        models = request.app.state.OLLAMA_MODELS\n        if model not in models:\n            raise HTTPException(...)\n        url_idx = random.choice(models[model].get(\"urls\", []))\n    url = request.app.state.config.OLLAMA_BASE_URLS[url_idx]   # caller-controlled, no authz\n    return url, url_idx\n```\n\nThe outbound request is then sent to that backend using the backend's own configured\nAPI key. Backends an admin has disabled (`OLLAMA_API_CONFIGS[\"\u003cidx\u003e\"].enable = false`)\nare hidden from model discovery but remain reachable through the indexed route, because\nthe disabled state is never re-checked at request time.\n\n## Impact\n\nA verified, non-admin user with read access to any single model can:\n- route requests to internal / higher-capability / restricted Ollama backends in\n  multi-backend deployments, bypassing backend-level isolation;\n- reach backends the admin has explicitly disabled;\n- have those requests authenticated with the target backend's configured API key\n  (the key is used server-side; it is not returned to the attacker);\n- consume the restricted backend's compute.\n\nThere is no cross-user data disclosure and no exfiltration of the backend credential\nitself; the impact is unauthorized access to, and use of, restricted backend resources.\n\n## Affected / Patched\n\n- Affected: `\u003c= 0.9.5`\n- Patched: `\u003e= 0.9.6`\n\n## Fix\n\n0.9.6 adds `validate_ollama_backend_idx()`, invoked on every indexed route (directly and\nvia `get_ollama_url()`), which returns 403 for any non-admin caller-supplied `url_idx`\nthat is not in the requested model's allowed `urls`. Because disabled backends are absent\nfrom every model's `urls`, the same check also blocks routing to disabled backends.","status":"published","published":"2026-06-17T18:01:50Z","modified":"2026-07-20T21:15:26.037031096Z","cvss":[{"version":"3.1","score":6.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L","level":"medium"}],"cwes":["CWE-863"],"updated_at":"2026-11-02T15:46:44.225359Z"},{"id":"PYSEC-2026-2718","title":"Open WebUI: Authenticated users can target arbitrary configured Ollama backends via unguarded url_idx path parameter","description":"## Summary\n\nSeveral direct, index-addressed Ollama proxy routes accept a caller-supplied `url_idx`\npath parameter and use it as a raw index into the admin-configured `OLLAMA_BASE_URLS`\nlist. Access control on these routes validates only whether the user may use the\nrequested *model*, never which *backend* the request is routed to. Any authenticated\nuser can append an arbitrary `url_idx` to force their request onto an Ollama backend\nthey were never authorized to reach, including internal, higher-privilege, or\nexplicitly admin-disabled backends.\n\n## Affected endpoints\n\nAll indexed Ollama routes that resolve the backend through `get_ollama_url()`:\n\n```\nPOST /ollama/api/chat/{url_idx}\nPOST /ollama/api/generate/{url_idx}\nPOST /ollama/api/embed/{url_idx}\nPOST /ollama/api/embeddings/{url_idx}\nPOST /ollama/v1/chat/completions/{url_idx}\nPOST /ollama/v1/completions/{url_idx}\nPOST /ollama/v1/messages/{url_idx}\nPOST /ollama/v1/responses/{url_idx}\n```\n\n## Root cause\n\n`backend/open_webui/routers/ollama.py` — `get_ollama_url()` consults the\nmodel-to-backend allow-list (`OLLAMA_MODELS[model][\"urls\"]`) only when `url_idx` is\nomitted. When the caller supplies `url_idx`, that mapping is skipped and the value is\nused directly as an index:\n\n```python\nasync def get_ollama_url(request: Request, model: str, url_idx: Optional[int] = None):\n    if url_idx is None:\n        models = request.app.state.OLLAMA_MODELS\n        if model not in models:\n            raise HTTPException(...)\n        url_idx = random.choice(models[model].get(\"urls\", []))\n    url = request.app.state.config.OLLAMA_BASE_URLS[url_idx]   # caller-controlled, no authz\n    return url, url_idx\n```\n\nThe outbound request is then sent to that backend using the backend's own configured\nAPI key. Backends an admin has disabled (`OLLAMA_API_CONFIGS[\"\u003cidx\u003e\"].enable = false`)\nare hidden from model discovery but remain reachable through the indexed route, because\nthe disabled state is never re-checked at request time.\n\n## Impact\n\nA verified, non-admin user with read access to any single model can:\n- route requests to internal / higher-capability / restricted Ollama backends in\n  multi-backend deployments, bypassing backend-level isolation;\n- reach backends the admin has explicitly disabled;\n- have those requests authenticated with the target backend's configured API key\n  (the key is used server-side; it is not returned to the attacker);\n- consume the restricted backend's compute.\n\nThere is no cross-user data disclosure and no exfiltration of the backend credential\nitself; the impact is unauthorized access to, and use of, restricted backend resources.\n\n## Affected / Patched\n\n- Affected: `\u003c= 0.9.5`\n- Patched: `\u003e= 0.9.6`\n\n## Fix\n\n0.9.6 adds `validate_ollama_backend_idx()`, invoked on every indexed route (directly and\nvia `get_ollama_url()`), which returns 403 for any non-admin caller-supplied `url_idx`\nthat is not in the requested model's allowed `urls`. Because disabled backends are absent\nfrom every model's `urls`, the same check also blocks routing to disabled backends.","status":"published","published":"2026-07-13T15:46:19.930457Z","modified":"2026-07-13T16:32:18.829322998Z","cvss":[{"version":"3.1","score":6.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L","level":"medium"}],"updated_at":"2026-11-02T15:52:55.961245Z"}]}}
