Open WebUI: Cross-user model-list exposure via static cache key in get_all_models (aiocache key= vs key_builder= misuse)GHSA-3wp3-xxj9-5jqq
LOWFix: open-webui/open-webui#25783GHSA-3wp3-xxj9-5jqq is a low-severity (CVSS 3.5) CWE-524 vulnerability in open-webui. A fix is available for open-webui — see the affected versions and patch details below.
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
Exploitation and automatability from CISA’s SSVC triage for GHSA-3wp3-xxj9-5jqq.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
GHSA-3wp3-xxj9-5jqq by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.
Where this sits among everything scored
Of 395,492 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.
Real-World Exposure
open-webuiReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.
Description
Summary
The get_all_models handlers in routers/openai.py and routers/ollama.py intended to cache their permission-filtered model lists per user, but the @cached decorator was misconfigured: it passed a key= lambda instead of key_builder=. In aiocache 0.12.3 (the pinned version), key= is a static cache key — a callable passed there is used as a constant object, not invoked per call. As a result the per-user key was never computed, and all callers collided onto a single shared cache entry within the TTL window. During that window, one user's permission-filtered model list could be served to a different authenticated user, crossing the per-user authorization boundary.
Impact
- Boundary crossed: Confidentiality (cross-user). A caller can receive the model list scoped to a different security principal than themselves.
- A user (or admin, or — depending on endpoint reachability — anonymous caller) who populates the cache causes the next caller within the TTL to receive that list rather than their own permission-filtered one.
- What's disclosed is the set of models another principal can access, including potentially the existence and naming of models restricted from the receiving user.
- Exposure is incidental and timing-dependent, not attacker-controlled: the leaked entry is whatever the most recent caller populated within
MODELS_CACHE_TTL(default 1 second), and the attacker cannot select the victim or force a target's list into the cache.
Affected component
backend/open_webui/routers/openai.py—get_all_models(~line 488)backend/open_webui/routers/ollama.py—get_all_models(~line 302)
Both decorated with @cached(ttl=MODELS_CACHE_TTL, key=lambda ...). No other @cached(... key=lambda ...) misuse was found elsewhere in the backend.
Root cause
aiocache 0.12's @cached treats key= as a static key; the per-call hook is key_builder= with signature key_builder(func, *args, **kwargs). Passing a callable to key= uses the callable object itself as a constant key, so every invocation resolved to the same entry and the intended per-user.id namespacing never occurred.
Reproduction (default config)
- On a default deployment, configure at least two users with different model-access permissions (e.g. one model restricted to user A).
- As user A, request the model list (populates the shared cache entry).
- Within
MODELS_CACHE_TTL(default 1s), as user B, request the model list. - User B receives user A's permission-filtered list, including models B is not permitted to see.
Remediation
Replace key= with key_builder= at both call sites and adjust the lambda to take the function as its first argument:
@cached(
ttl=MODELS_CACHE_TTL,
key_builder=lambda _func, request, user=None: (
f'openai_all_models_{user.id}' if user else 'openai_all_models'
),
)
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐍PyPI | open-webui | ≥ 0.6.27&&< 0.10.0 | 0.10.0pip install --upgrade 'open-webui==0.10.0' |
Affected Products
open webuiopenwebuiDetection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for open-webui, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update open-webui to 0.10.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-3wp3-xxj9-5jqq is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
Frequently Asked Questions
Is GHSA-3wp3-xxj9-5jqq in your dependencies?
Find it across PyPI, including transitive dependencies.