CVE-2026-59216: Open WebUI Cross-User Code Execution via Unvalidated Socket.IO session_id
A logged-in user can hijack another user's browser session and run arbitrary code inside it, including as an administrator, by substituting the victim's Socket.IO session ID into a normal chat…

The problem
In Open WebUI versions before 0.10.0, the get_event_call function in backend/open_webui/socket/main.py routes execute:python and execute:tool Socket.IO events to whichever session_id the client sends in the request body. The only check is whether that session ID exists in SESSION_POOL (i.e., is connected), not whether it belongs to the requesting user.
A separate weakness in ydoc:document:join leaks the live socket IDs of every participant in a shared collaborative note to any read-access member of that note. Combined, these two issues let a low-privilege attacker learn a victim's socket ID and then fire code-interpreter or tool-execution events directly into that victim's browser session.
When the victim is an administrator, the hijacked session reaches the Functions API whose source is exec()'d server-side, giving the attacker remote code execution as root in the default container.
Proof of concept
A working proof-of-concept for CVE-2026-59216 in open-webui, with the exact payload below.
# Step 1: attacker shares a note with the victim and captures the victim's session_id
# from the ydoc:document:join room event (Socket.IO, read-access participants).
# Step 2: attacker sends a crafted chat completion carrying the victim's session_id.
# The code-interpreter payload runs in the victim's browser (or, for an admin victim,
# escalates to server-side RCE via the Functions API).
POST /api/v1/chat/completions HTTP/1.1
Host: <target>
Authorization: Bearer <attacker_token>
Content-Type: application/json
{
"model": "<attacker_controlled_model_or_direct_connection>",
"session_id": "<victim_socket_id_from_ydoc_room>",
"messages": [
{"role": "user", "content": "run my payload"}
],
"tool_ids": ["<attacker_tool_id>"]
}
# The backend passes session_id straight into get_event_call, which checks only:
# if session_id not in SESSION_POOL: return error # victim IS connected
# and then emits execute:tool / execute:python to the victim's socket ID.
# No ownership check is performed before 0.10.0.The root cause is CWE-639: Authorization Bypass Through User-Controlled Key. The session_id field is taken directly from the attacker's request body (form_data.pop('session_id', None)) and placed into metadata alongside the server-derived user_id. Inside get_event_call, the code checked only session_id not in SESSION_POOL, which is satisfied whenever the victim is online, never confirming that SESSION_POOL[session_id]['id'] == request_info['user_id'].
The patch (commit 386ac958, PR #25763) adds exactly that comparison: session is None or session.get('id') != request_info.get('user_id'), using the server-derived user_id as the authority. Because user_id comes from the authenticated JWT and not from the request body, an attacker cannot forge it.
The advisory also recommends restricting ydoc:document:join to stop leaking socket IDs as defence-in-depth, but the ownership check alone closes the cross-user delivery primitive.
The fix
Upgrade to Open WebUI 0.10.0. The fix in commit 386ac958 (PR #25763) adds an ownership check to get_event_call: the target session must belong to the requesting user (session.get('id') == request_info.get('user_id')) before any execute:python or execute:tool event is dispatched.
As additional hardening, restrict or audit ydoc:document:join to prevent socket ID disclosure to room participants.
Reported by doge-woof.
Related research
- high · 7.1CVE-2026-59714CVE-2026-59714: open-webui Cross-Channel Message Overwrite via Chat Completion API
- high · 7.3CVE-2026-59214CVE-2026-59214: Open WebUI Stored Web-Worker XSS via Pyodide Same-Origin Code Execution
- high · 7.1CVE-2026-59219CVE-2026-59219: Open WebUI Insufficient Session Expiration on Realtime Endpoints
- high · 8.8CVE-2026-57516CVE-2026-57516: Ray read_webdataset Arbitrary Code Execution via Default Pickle Decoder