CVE-2026-54009: open-webui: cross-user file read via auth bypass

GHSA-wch8-mhj5-9frg MEDIUM
Published June 17, 2026
CISO Take

Open WebUI's chat completions API resolves file IDs directly from disk without verifying that the requesting user owns the file: any authenticated user can supply another user's file UUID as an image URL value and prompt the LLM to transcribe its contents in plain text. While exploitation requires a valid account and prior knowledge of a victim's file UUID — identifiers that leak through shared chats, knowledge base memberships, folder indexes, and chat history exports — the attack itself is trivially simple once those prerequisites are met, and the EPSS ranking places this in the top 91st percentile of CVEs by relative exploitation likelihood. With 102 prior CVEs already recorded against the same package, shared Open WebUI deployments handling sensitive documents (legal, HR, financial) face meaningful data-exposure risk. Teams running multi-tenant Open WebUI instances should upgrade to version 0.9.6 immediately; where patching is not possible, restrict the `/api/v1/chat/completions` endpoint at the reverse proxy and audit request logs for `image_url` values matching UUID patterns rather than HTTP URLs or data URIs.

Sources: NVD GitHub Advisory EPSS ATLAS

What is the risk?

Risk is MEDIUM overall but climbs to HIGH in multi-tenant or enterprise deployments. The CVSS 6.5 score accurately captures the network-accessible, low-complexity, authenticated attack vector with high confidentiality impact and no integrity or availability harm. The authentication requirement bounds the blast radius to users with valid accounts, but on a shared team or SaaS Open WebUI instance this may include dozens to hundreds of principals. No public exploit code or Nuclei scanner template exists at time of writing, and the CVE is not on CISA KEV, suggesting no confirmed in-the-wild exploitation. The package's history of 102 CVEs and the vulnerability's trivial execution profile — a single crafted API request — represent residual risk that warrants treating the patch as P1 rather than routine maintenance.

How does the attack unfold?

Initial Access
Attacker authenticates to a shared Open WebUI instance using a valid account — own credentials, a compromised employee account, or a trial signup.
AML.T0012
File ID Discovery
Attacker obtains a target user's file UUID through shared chat history, knowledge base membership listings, folder index views, or chat export API endpoints that include file IDs in their responses.
AML.T0036
Authorization Bypass
Attacker sends POST /api/v1/chat/completions with the victim's file UUID as the image_url.url value; the middleware calls Files.get_file_by_id() with no ownership filter and base64-encodes the file for LLM injection.
AML.T0049
Data Exfiltration
The LLM transcribes or describes the unauthorized file content in its API response, returning the victim's sensitive document contents to the attacker in plaintext.
AML.T0024

What systems are affected?

Package Ecosystem Vulnerable Range Patched
Open WebUI pip <= 0.9.5 0.9.6
141.4K Pushed 4d ago 76% patched ~4d to patch Full package profile →

Do you use Open WebUI? You're affected.

How severe is it?

CVSS 3.1
6.5 / 10
EPSS
0.0%
chance of exploitation in 30 days
Higher than 9% of all CVEs
Exploitation Status
No known exploitation
Sophistication
Trivial

What is the attack surface?

AV AC PR UI S C I A
AV Network
AC Low
PR Low
UI None
S Unchanged
C High
I None
A None

What should I do?

5 steps
  1. Upgrade open-webui pip package to version 0.9.6 (patched).

  2. If immediate patching is blocked, restrict network access to POST /api/v1/chat/completions at the reverse proxy to trusted subnets or authenticated sessions with additional IP allowlisting.

  3. Audit application or web server logs for POST requests to the chat completions endpoint where the request body contains image_url.url values matching the UUID pattern [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} — these indicate potential exploitation attempts rather than legitimate HTTP or data-URI image references.

  4. On forked or custom builds, run git grep -n 'Files.get_file_by_id(' -- 'backend/open_webui/**' to identify any remaining call sites not guarded by get_file_by_id_and_user_id or has_access_to_file.

  5. Consider temporarily disabling file upload for non-admin users if patch deployment will be delayed beyond 48 hours.

How is it classified?

Which compliance frameworks are affected?

This CVE is relevant to:

EU AI Act
Article 9 - Risk Management System
ISO 42001
A.8 - Data Governance
NIST AI RMF
MANAGE 2.2 - Risk Tracking and Response
OWASP LLM Top 10
LLM02 - Sensitive Information Disclosure

Frequently Asked Questions

What is CVE-2026-54009?

Open WebUI's chat completions API resolves file IDs directly from disk without verifying that the requesting user owns the file: any authenticated user can supply another user's file UUID as an image URL value and prompt the LLM to transcribe its contents in plain text. While exploitation requires a valid account and prior knowledge of a victim's file UUID — identifiers that leak through shared chats, knowledge base memberships, folder indexes, and chat history exports — the attack itself is trivially simple once those prerequisites are met, and the EPSS ranking places this in the top 91st percentile of CVEs by relative exploitation likelihood. With 102 prior CVEs already recorded against the same package, shared Open WebUI deployments handling sensitive documents (legal, HR, financial) face meaningful data-exposure risk. Teams running multi-tenant Open WebUI instances should upgrade to version 0.9.6 immediately; where patching is not possible, restrict the `/api/v1/chat/completions` endpoint at the reverse proxy and audit request logs for `image_url` values matching UUID patterns rather than HTTP URLs or data URIs.

Is CVE-2026-54009 actively exploited?

No confirmed active exploitation of CVE-2026-54009 has been reported, but organizations should still patch proactively.

How to fix CVE-2026-54009?

1. Upgrade open-webui pip package to version 0.9.6 (patched). 2. If immediate patching is blocked, restrict network access to POST /api/v1/chat/completions at the reverse proxy to trusted subnets or authenticated sessions with additional IP allowlisting. 3. Audit application or web server logs for POST requests to the chat completions endpoint where the request body contains image_url.url values matching the UUID pattern `[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}` — these indicate potential exploitation attempts rather than legitimate HTTP or data-URI image references. 4. On forked or custom builds, run `git grep -n 'Files.get_file_by_id(' -- 'backend/open_webui/**'` to identify any remaining call sites not guarded by `get_file_by_id_and_user_id` or `has_access_to_file`. 5. Consider temporarily disabling file upload for non-admin users if patch deployment will be delayed beyond 48 hours.

What systems are affected by CVE-2026-54009?

This vulnerability affects the following AI/ML architecture patterns: Multi-tenant AI chat platforms, RAG pipelines with document upload, Team AI assistants with shared knowledge bases, Enterprise document Q&A deployments.

What is the CVSS score for CVE-2026-54009?

CVE-2026-54009 has a CVSS v3.1 base score of 6.5 (MEDIUM). The EPSS exploitation probability is 0.03%.

What is the AI security impact?

Affected AI Architectures

Multi-tenant AI chat platformsRAG pipelines with document uploadTeam AI assistants with shared knowledge basesEnterprise document Q&A deployments

MITRE ATLAS Techniques

AML.T0024 Exfiltration via AI Inference API
AML.T0040 AI Model Inference API Access
AML.T0049 Exploit Public-Facing Application
AML.T0057 LLM Data Leakage

Compliance Controls Affected

EU AI Act: Article 9
ISO 42001: A.8
NIST AI RMF: MANAGE 2.2
OWASP LLM Top 10: LLM02

What are the technical details?

Original Advisory

## summary `POST /api/chat/completions` accepts an `image_url.url` value that, when it does NOT start with `http://`, `https://`, or `data:image/`, is interpreted as a file id and resolved against the global file table with no ownership check. An authenticated user can therefore set `image_url.url` to another user's file id, the server reads that file from disk, base64-encodes it, and injects the data URI into the LLM request. The user then prompts the LLM to describe / OCR the file and reads the content back. Same class as CVE-2026-44560 (RAG cross-user access) and the multiple `has_access_to_file` checks added in `routers/files.py` -- the auth boundary was tightened on the file router but not on this conversion path. ## affected code `backend/open_webui/utils/middleware.py:2113-2150` -- `convert_url_images_to_base64`: ```python async def convert_url_images_to_base64(form_data): messages = form_data.get('messages', []) for message in messages: content = message.get('content') if not isinstance(content, list): continue new_content = [] for item in content: if not isinstance(item, dict) or item.get('type') != 'image_url': new_content.append(item) continue image_url = item.get('image_url', {}).get('url', '') if image_url.startswith('data:image/'): new_content.append(item) continue try: base64_data = await get_image_base64_from_url(image_url) # <-- no `user` passed if base64_data: new_content.append({'type': 'image_url', 'image_url': {'url': base64_data}}) ``` called from the main chat completion middleware at `middleware.py:2357`: ```python form_data = await convert_url_images_to_base64(form_data) ``` `backend/open_webui/utils/files.py:57-95` -- `get_image_base64_from_url`: ```python async def get_image_base64_from_url(url: str) -> Optional[str]: try: if url.startswith('http'): validate_url(url) # ... SSRF-safe fetch with allow_redirects=AIOHTTP_CLIENT_ALLOW_REDIRECTS ... else: file = await Files.get_file_by_id(url) # <-- NO user_id filter if not file: return None file_path = await asyncio.to_thread(Storage.get_file, file.path) file_path = Path(file_path) if file_path.is_file(): with open(file_path, 'rb') as image_file: encoded_string = base64.b64encode(image_file.read()).decode('utf-8') content_type = mimetypes.guess_type(file_path.name)[0] or (file.meta or {}).get('content_type') ... return f'data:{content_type};base64,{encoded_string}' ``` `Files.get_file_by_id` in `models/files.py:161` does a bare `db.get(File, id)` -- no ownership filter. there is a separate `Files.get_file_by_id_and_user_id` at line 172 that does filter on `user_id`, and the file router uses `has_access_to_file(id, 'read', user, db)` at `routers/files.py:626` etc. neither check exists on this path. ## reproduction 1. As user A, upload any file (image works cleanly, pdf works if a vision-capable model is configured). Note the file id from the upload response, e.g. `c7f1d8e3-...`. 2. As user B, POST to `/api/v1/chat/completions` with body: ```json { "model": "<any vision model>", "messages": [ { "role": "user", "content": [ {"type": "text", "text": "transcribe everything you can see in this image"}, {"type": "image_url", "image_url": {"url": "c7f1d8e3-..."}} ] } ] } ``` Server reads user A's file from disk, base64-encodes it, and sends to the LLM as user B's image attachment. LLM response contains the file content. ## file id discovery File ids are UUIDs and not enumerable directly, but they leak via: - shared chats / channels containing the original upload - knowledge base members can see ids of files contributed by others - a user who can read a folder index sees the file ids of files inside - chat history exports (`/api/v1/chats/{id}`) include file ids - the user themselves can be tricked into pasting / sharing an id (less likely) ## impact Any authenticated user can read any other user's file content (image and any file with an image-guess mimetype path) via this channel. Severity is bounded by what the LLM will accept in `image_url` -- in practice, image files work cleanly with any vision model; pdf / docx work with multi-modal providers that accept them. ## suggested fix Thread the authenticated user through to `get_image_base64_from_url` and resolve the file via `Files.get_file_by_id_and_user_id(id, user.id)` (or `has_access_to_file(id, 'read', user, db)` if shared-via-knowledge-base access is intended). Same pattern that's already used in `routers/files.py:626` and elsewhere. minimal patch sketch: ```diff --- a/backend/open_webui/utils/files.py +++ b/backend/open_webui/utils/files.py @@ -57,7 +57,7 @@ -async def get_image_base64_from_url(url: str) -> Optional[str]: +async def get_image_base64_from_url(url: str, user=None) -> Optional[str]: try: if url.startswith('http'): ... else: - file = await Files.get_file_by_id(url) + file = (await Files.get_file_by_id_and_user_id(url, user.id) + if user is not None else None) + if file is None: + # fall back to access-grant check for shared files + file = await Files.get_file_by_id(url) + if file and not await has_access_to_file(url, 'read', user): + return None ``` and pipe `user` through `convert_url_images_to_base64(form_data, user)` from the middleware caller. happy to send a PR once you confirm the fix shape you want. ## variant note this was found via patch-diffing existing advisories. the same bug class likely exists in any other site that calls `Files.get_file_by_id` without an adjacent `has_access_to_file` / `get_file_by_id_and_user_id` check. quick grep: ``` git grep -n 'Files\.get_file_by_id(' -- 'backend/open_webui/**' ``` worth a sweep across utils/ and routers/ for missed sites. ## environment Open-webui main branch as of commit `3660bc0` (2026-05-10). python 3.x backend. confirmed by reading the source; no instance stood up.

Exploitation Scenario

An attacker with a standard employee account on a shared corporate Open WebUI deployment learns that a colleague in legal uploaded a confidential contract. The attacker discovers the file's UUID by viewing the shared knowledge base where the file was contributed — file IDs are exposed in the knowledge base member interface. The attacker then sends a single POST request to `/api/v1/chat/completions` with the colleague's UUID in the `image_url.url` field and the prompt 'transcribe all text visible in this document'. The Open WebUI middleware calls `Files.get_file_by_id(uuid)` with no ownership check, reads the file from disk, base64-encodes it, and forwards it as an image attachment to the vision LLM. The LLM's response — returned in the API JSON — contains the full text of the contract. No special tooling, no privilege escalation, no rate-limiting concerns: a single curl command achieves complete file exfiltration.

Weaknesses (CWE)

CWE-639 — Authorization Bypass Through User-Controlled Key: The system's authorization functionality does not prevent one user from gaining access to another user's data or record by modifying the key value identifying the data.

  • [Architecture and Design] For each and every data access, ensure that the user has sufficient privilege to access the record that is being requested.
  • [Architecture and Design, Implementation] Make sure that the key that is used in the lookup of a specific user's record is not controllable externally by the user or that any tampering can be detected.

Source: MITRE CWE corpus.

CVSS Vector

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N

Timeline

Published
June 17, 2026
Last Modified
June 17, 2026
First Seen
June 17, 2026

Related Vulnerabilities