Summary
Stale Admin Role in Socket.IO Session Pool Enables Post-Demotion Cross-User Note Access
Affected Component
Socket.IO session state and role-check callsites:
backend/open_webui/socket/main.py(lines 330-351,connecthandler, role snapshotted into SESSION_POOL)backend/open_webui/socket/main.py(lines 393-398,heartbeathandler, does not refresh role)backend/open_webui/socket/main.py(line 538,ydoc:document:join, uses cached role for admin check)backend/open_webui/socket/main.py(line 611,document_save_handler, uses cached role for admin check)backend/open_webui/routers/users.py(lines 557-633, role update, does not invalidate SESSION_POOL)backend/open_webui/routers/users.py(line 641, user delete, does not invalidate SESSION_POOL)
Affected Versions
Current main branch (commit 6fdd19bf1) and likely all versions with the collaborative document (Yjs) Socket.IO handlers.
Description
When a user connects via Socket.IO, the connect handler authenticates them via JWT and stores their user record (including role) in the in-memory SESSION_POOL dictionary keyed by session ID. The heartbeat handler keeps the session alive indefinitely but only refreshes the last_seen_at timestamp, never the role.
Role checks in the Yjs collaborative document handlers (ydoc:document:join, document_save_handler) consult the cached SESSION_POOL role rather than the database. Meanwhile, administrative role changes and user deletions do not iterate SESSION_POOL to disconnect affected sessions. As a result, a user whose admin role has been revoked retains admin privileges within their existing Socket.IO session for as long as they keep the connection alive (via automatic heartbeats).
HTTP endpoints are not affected, get_current_user at utils/auth.py refetches the user record from the database on every request. The gap is exclusive to the Socket.IO session cache.
# socket/main.py:330-351, role snapshotted at connect time
async def connect(sid, environ, auth):
user = None
if auth and 'token' in auth:
data = decode_token(auth['token'])
if data is not None and 'id' in data:
user = Users.get_user_by_id(data['id'])
if user:
SESSION_POOL[sid] = {
'id': user.id,
'role': user.role, # ← snapshotted, never refreshed
...
}
# socket/main.py:393-398, heartbeat refreshes last_seen_at only
async def heartbeat(sid, data):
user = SESSION_POOL.get(sid)
if user:
SESSION_POOL[sid] = {**user, 'last_seen_at': int(time.time())}
# role is carried forward unchanged
# socket/main.py:538, admin check against cached role
if user.get('role') != 'admin' and not has_access(user_id, 'note', note_id, 'read', db=db):
return
Attack Scenario
- User B is an admin and has an active browser session with a live Socket.IO connection.
SESSION_POOL[sid]recordsrole='admin'. - Admin A demotes User B to a regular user via
POST /api/v1/users/{B_id}/update. The DBuser.rolebecomes'user'. - No Socket.IO disconnect, no SESSION_POOL update, no token revocation event is triggered by the role change.
- User B's client continues sending
heartbeatevents every few seconds; these are accepted and only refreshlast_seen_at. - User B emits
ydoc:document:joinwithdocument_id = 'note:<victim_note_id>'for any note they do not own. - The handler at line 538 evaluates
user.get('role') != 'admin', returnsFalsebecauseSESSION_POOLstill holds the staleadminrole. Access check is bypassed, User B joins the document room, receives full document state and live updates. - User B emits
ydoc:document:updatefor the same note. The handler at line 611 performs the same cached-admin check, bypasses authorization, and persists attacker-controlled content to the victim's note viaNotes.update_note_by_id.
The same bypass occurs if the user is deleted entirely (delete_user_by_id), the deleted user retains admin privileges on their live socket until disconnection.
Preconditions
- Attacker must have an active Socket.IO connection established while they held admin role
- Attacker must retain the Socket.IO session after demotion/deletion (trivial, just don't close the browser)
Impact
- Read access to any user's notes after admin privileges have been revoked
- Write access (content injection, overwrite) to any user's notes under the same conditions
- The stale privilege is bounded only by the attacker's willingness to keep the Socket.IO connection alive; heartbeats extend the session indefinitely
- Official admin demotion or user deletion gives a false sense of security, HTTP access is correctly revoked, but real-time collaborative access silently continues
The application does not correctly enforce access controls, allowing a principal to access resources or operations beyond their granted permissions. Typical impact: unauthorized data access or execution of privileged operations.
CVE-2026-44553 has a CVSS score of 8.1 (High). The vector is network-reachable, low privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (0.9.0); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Remediation advice
Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.
Frequently Asked Questions
- What is CVE-2026-44553? CVE-2026-44553 is a high-severity incorrect authorization vulnerability in open-webui (pip), affecting versions <= 0.8.12. It is fixed in 0.9.0. The application does not correctly enforce access controls, allowing a principal to access resources or operations beyond their granted permissions.
- How severe is CVE-2026-44553? CVE-2026-44553 has a CVSS score of 8.1 (High). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of open-webui are affected by CVE-2026-44553? open-webui (pip) versions <= 0.8.12 is affected.
- Is there a fix for CVE-2026-44553? Yes. CVE-2026-44553 is fixed in 0.9.0. Upgrade to this version or later.
- Is CVE-2026-44553 exploitable, and should I be worried? Whether CVE-2026-44553 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-44553 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-44553? Upgrade
open-webuito 0.9.0 or later.