Kestrel
대시보드로 돌아가기
CVE-2026-59213LOW· 3.5MITRENVDGHSA대응게시일: 2026. 07. 09.수정일: 2026. 07. 24.

Open WebUI: Cross-user model-list exposure via static cache key in get_all_models (aiocache key= vs key_builder= misuse)

위협 신호 · CVSS · EPSS · KEV

정기 패치· 높은 악용 신호 없음
CVSS
3.5low

이론적 심각도 점수

EPSS
0.3%상위 78.1%

30일 내 악용 확률 예측

KEV
미등재

실측 악용 기록 없음

권장 대응 기한60일 이내CISA SSVC 기준

계획된 패치 주기 내 조치(60일 이내)

외부 노출· KEV 미등재 · 자동화 어려움 · 부분 영향 · 외부 노출

CVSS 벡터 · 메트릭

악용 경로
공격 벡터네트워크
공격 복잡도높음
필요 권한낮음
사용자 상호작용불필요
범위변경
영향
기밀성 영향낮음
무결성 영향없음
가용성 영향없음
버전별 점수
CVSS 3.13.5LOW
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:L/I:N/A:N

상세 설명

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.pyget_all_models (~line 488)
  • backend/open_webui/routers/ollama.pyget_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)

  1. On a default deployment, configure at least two users with different model-access permissions (e.g. one model restricted to user A).
  2. As user A, request the model list (populates the shared cache entry).
  3. Within MODELS_CACHE_TTL (default 1s), as user B, request the model list.
  4. 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:

text
1@cached(
2 ttl=MODELS_CACHE_TTL,
3 key_builder=lambda _func, request, user=None: (
4 f'openai_all_models_{user.id}' if user else 'openai_all_models'
5 ),
6)

AI 심층 분석

공격 시나리오 · 재현 가능한 PoC 페이로드 · 즉시 적용 가능한 차단 패치를 한 번에 받아 보세요. 보안 운영팀이 그대로 점검·티켓팅에 쓸 수 있는 형태로 정리해 드립니다.