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.
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?
What systems are affected?
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| Open WebUI | pip | <= 0.9.5 | 0.9.6 |
Do you use Open WebUI? You're affected.
How severe is it?
What is the attack surface?
What should I do?
5 steps-
Upgrade open-webui pip package to version 0.9.6 (patched).
-
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.
-
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. -
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 byget_file_by_id_and_user_idorhas_access_to_file. -
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:
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
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
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 References
Timeline
Related Vulnerabilities
CVE-2026-44551 9.1 open-webui: LDAP auth bypass — full account takeover
Same package: open-webui CVE-2026-45672 8.8 open-webui: code exec gate bypass via API endpoint
Same package: open-webui CVE-2026-44552 8.7 open-webui: Redis cache poisoning enables cross-instance tool hijack
Same package: open-webui CVE-2025-64495 8.7 Open WebUI: XSS-to-RCE via malicious prompt injection
Same package: open-webui CVE-2026-45315 8.7 open-webui: stored XSS → JWT theft and admin takeover
Same package: open-webui