CVE-2026-59216: Open WebUI: Cross-user code-interpreter and tool execution via unvalidated Socket.IO event-caller session_id
In Open WebUI, a low-privilege user can send code-interpreter Python or tool calls to another user's browser session by supplying that user's Socket.IO session_id. If the victim is an admin, this reaches the Functions API and gives server-side code execution. Versions before 0.10.0 are affected. Upgrade to 0.10.0.
Published Jul 24, 2026 · Analysis Oct 25, 2026
What an attacker can do
get_event_call() in backend/open_webui/socket/main.py sends execute:python and execute:tool events to request_info['session_id'] after checking only that the id is in SESSION_POOL. The id comes from the chat-completion request body in main.py (form_data.pop('session_id')) and is never matched to the authenticated user.
An attacker also needs the victim's live socket id. ydoc:document:join exposes the socket ids of everyone in a shared note's collaboration room to any participant with read access. So the attacker shares a note with the victim and waits until the victim opens it online.
The attacker then calls POST /api/v1/chat/completions, which needs only the default user role, with the victim's session_id and a model or Direct Connection they control to pick the payload. The code runs in the victim's browser context as the victim, which is a full session compromise. When the victim is an admin, that context reaches POST /api/v1/functions/create, whose source is exec()'d on the server, giving remote code execution as the server process (root in the default container). The reporter confirmed this on 0.9.6 with a callback showing uid=0. The code interpreter must be enabled.
Who is affected
open-webui before 0.10.0 (last affected release 0.9.6), with the code interpreter enabled and users who can share notes. Server-side RCE needs an admin to be the victim. Check:
pip show open-webuiFor Docker, check the image tag, for example ghcr.io/open-webui/open-webui:0.9.6 is affected.
open-webui
- Ecosystem
- PyPI
- Affected
- < 0.10.0
- Fixed in
- 0.10.0
- Check
pip show open-webui
How to fix it
Upgrade to open-webui 0.10.0:
pip install --upgrade open-webui==0.10.0get_event_call now checks that the target session belongs to the requesting user, using the server-derived user_id from request metadata, and returns Client session disconnected otherwise. Restricting what ydoc:document:join discloses is a separate defence-in-depth step, not part of this fix; the ownership check blocks cross-user delivery whether or not the attacker knows the id. The release notes also say real-time events now reach only the requesting user's own session. The advisory lists no workaround; consider turning off the code interpreter and note sharing until you upgrade.
How to tell if you were hit
Look for chat completion requests whose session_id belongs to a different user than the authenticated caller, if you log it. Review the admin Functions list for entries you did not create, and server logs for POST /api/v1/functions/create right after an admin opened a shared note. Check for unexpected child processes of the Open WebUI process and outbound callbacks from the container. In notes, look for shares from low-privilege accounts to admins.
Original advisory
From the GitHub Advisory Database (CC BY 4.0).
Summary
An authenticated low-privilege user can execute arbitrary code-interpreter Python and tools inside another user's authenticated session. The Socket.IO event-caller (get_event_call) delivers execute:python / execute:tool events to a client-supplied session_id after only checking that the session is connected, never that it belongs to the requester. Combined with ydoc:document:join, which exposes the live socket ids of everyone in a shared note's collaboration room to any read-access participant, an attacker can target a victim's session and run attacker-chosen code/tools in the victim's browser context. When the victim is an administrator, that hijacked context reaches the admin-only Functions API, whose source is executed server-side, yielding remote code execution as the server process (root in the default container).
Affected component
backend/open_webui/socket/main.py—get_event_call()/__event_caller__backend/open_webui/main.py— chat-completion metadata (session_idtaken from the request body)
Root cause
The event-caller routes to a caller-controlled session id with no ownership check:
# backend/open_webui/socket/main.py — get_event_call()
async def __event_caller__(event_data):
session_id = request_info['session_id']
if session_id not in SESSION_POOL: # only checks the session is connected
return {'error': 'Client session disconnected.'}
return await sio.call('events', {...}, to=session_id, ...) # delivered to that sidsession_id originates from the request body and is never validated against the authenticated user:
# backend/open_webui/main.py
metadata = {
'user_id': user.id, # server-derived (trustworthy)
'session_id': form_data.pop('session_id', None), # client-controlled
...
}SESSION_POOL[session_id] is the user record of whoever owns that socket. Because the caller checks only membership (in SESSION_POOL), a request carrying another user's session_id causes execute:python / execute:tool to be delivered to that other user's browser.
Reachability
execute:python/execute:toolare emitted from the code-interpreter and tool-call paths (utils/middleware.py,tools/builtin.py), all routed throughget_event_call.- The victim's live
session_idis disclosed to any read-access participant of a shared note viaydoc:document:join. POST /api/v1/chat/completionsrequires onlyget_verified_user(the default user role). The attacker uses their own account and a model / Direct Connection they control to choose the payload.
Impact
- Any victim: arbitrary code-interpreter Python and tool execution in the victim's authenticated session — the attacker acts with the victim's identity and origin (full session/account compromise).
- Admin victim: the hijacked admin context reaches
POST /api/v1/functions/create, whose source isexec()'d server-side → remote code execution as the server process (root in the default container).
The Functions API is intended administrator code-execution; the vulnerability here is the cross-user delivery that lets an attacker drive another user's session — including an admin's — into it. The primitive is a full session compromise even against non-admin victims.
Proof of Concept
The reporter's exploit.py reproduced on ghcr.io/open-webui/open-webui:0.9.6 and a build of the v0.9.6 tag, confirming blind server-side RCE out-of-band (callback returns uid=0(root)), using only a low-privilege user account that shared a note with an admin victim. Preconditions: code interpreter enabled; attacker shares a note with the victim; victim opens it while online; admin victim required for server RCE.
Fix
get_event_call must verify the target session belongs to the requesting user before delivering, not merely that it is connected:
session = SESSION_POOL.get(session_id)
if session is None or session.get('id') != request_info.get('user_id'):
return {'error': 'Client session disconnected.'}user_id in the request metadata is server-derived from the authenticated user, so it is trustworthy. Restricting ydoc:document:join so it does not disclose other participants' socket ids is recommended as defence-in-depth.
Affected / Patched
- Affected:
< 0.10.0(last affected release 0.9.6) - Patched: v0.10.0.
get_event_callnow verifies the target session belongs to the requesting user before delivering (session is None or session.get('id') != request_info.get('user_id')), using the server-deriveduser_idfrom the request metadata. The recommendedydoc:document:joinsid-disclosure restriction is defence-in-depth and independent of this fix; the ownership check closes the cross-user delivery regardless of whether the victim's sid is known.
Credited to waiveyk, Classic298.
Questions
- How does the attacker learn the victim's session id?
From ydoc:document:join. Any participant with read access to a shared note receives the live socket ids of everyone in its collaboration room, so the victim must open the note while online.
- Is server-side code execution possible for every victim?
No. Server-side RCE needs an admin victim, because the hijacked session then reaches the Functions API. For other users it is a full session compromise in the browser.
- What does 0.10.0 change?
get_event_call verifies that SESSION_POOL's entry for the session id belongs to the requesting user before delivering the event.
References
How this page is made: the data panel comes from the GitHub Advisory Database, CISA’s KEV catalog, the NVD and FIRST EPSS, and updates twice a day. The analysis is written by an AI model from the sources listed above and checked by automated rules before it is published. Check versions against the vendor's advisory before you act. Methodology
More Open WebUI vulnerabilities
All Open WebUI entriesCVE-2026-87011
Open WebUI: Unauthenticated requests can stall the server via uncached OIDC fetches in back-channel logout
high7.5Sep 10, 2026CVE-2026-87012
Open WebUI: Any authenticated user can suppress calendar alerts instance-wide via a non-numeric alert value
medium4.3Sep 10, 2026CVE-2026-87013
Open WebUI: Any authenticated user can start a non-terminating request via a folder parent cycle
medium4.3Sep 10, 2026CVE-2026-87014
Open WebUI: Admin demoted through SSO role sync keeps read and write access to all users' notes
medium6.5Sep 10, 2026CVE-2026-87015
Open WebUI: A user's session cookies are sent to tool servers configured for bearer authentication
medium6.8Sep 10, 2026CVE-2026-87016
Open WebUI: Sign-in as another user via wildcard characters in the OAuth subject claim on SQLite
high8.1Sep 10, 2026
