NewApp Monitoring is liveSign up free
AgentbeamBeta
AI agent vulnerability · Open WebUI

CVE-2026-59216: Open WebUI: Cross-user code-interpreter and tool execution via unvalidated Socket.IO event-caller session_id

highCVSS 3.1 7.7GHSA-74h3-cxq7-vc5q

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-webui

For 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.0

get_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_id taken 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 sid

session_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:tool are emitted from the code-interpreter and tool-call paths (utils/middleware.py, tools/builtin.py), all routed through get_event_call.
  • The victim's live session_id is disclosed to any read-access participant of a shared note via ydoc:document:join.
  • POST /api/v1/chat/completions requires only get_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 is exec()'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_call now 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-derived user_id from the request metadata. The recommended ydoc:document:join sid-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 entries