
Keycloak 26.8.1 中危 6.6 远程已认证认证残留。拥有 manage-users 权限的帮助台管理员可以将预先构建的 manage-realm 复合角色绑定到自身,然后读取完整的 realm 文档。需要该复合角色已存在。回环 PoC。
一个本应只负责重置密码和创建用户的服务台账户可以提升自己的权限。拥有 Keycloak manage-users 角色的人会在 realm 中发现一个遗留的职位名称(一个悄悄包含 manage-realm 的复合角色),将该职位名称绑定到自己的账户上,然后读取在上一请求中对他们隐藏的完整 realm 文档。他们无法凭空创建该复合角色。必须由真正的 realm 管理员事先构建它。这是 CVE-2026-94000 的角色映射同类问题,后者封堵了加入组上的相同手法。
类别: 认证遗留
可达性: 远程(已认证的 manage-users)
Keycloak 26.8.1 (24ed1dad) - Red Hat
| ID | 暂无 CVE |
| 类别 | 认证遗留 |
| 可达性 | 远程(已认证的 Admin REST;需要 manage-users 以及预先构建的复合角色) |
| CWE | CWE-269 / CWE-266 |
| CVSS | 中危:6.6 CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:H |
| 产品 | Keycloak |
| 受影响版本 | 26.8.1 (24ed1dadf3f246a555adb6d3d4a99e9b3003fb3b);镜像 quay.io/keycloak/keycloak:26.8.1 |
| 认证 | 拥有 realm-management / manage-users 的委派管理员。FGAP v2 是默认评估器。 |
| 许可证 | GNU Affero GPL v3.0 |
| 实验环境 | 仅 127.0.0.1:18082 |
需要一个已持有 manage-users 的现有 Keycloak 账户,以及一个 realm 或客户端角色,其自身名称不是管理员角色,且其复合角色包含 manage-realm 或 realm-admin。不需要令牌上具有 manage-realm。无法创建该复合角色。直接分配名为 manage-realm / realm-admin 的角色仍会被拒绝。
| 权限 | 方法 / 汇聚点 | 侦察 / 完整性 / 可用性 | 实验验证或推断 |
|---|---|---|---|
| 将非管理员复合角色映射到自身 | POST /admin/realms/{realm}/users/{id}/role-mappings/realm,使用复合角色 id 和名称 | 完整性(角色授予) | 实验验证。HTTP 204。见证 kcr02m7q4 |
| 对客户端角色执行相同授予 | POST /admin/realms/{realm}/users/{id}/role-mappings/clients/{client} | 完整性 | 源码。ClientRoleMappingsResource.java:184 处相同的 canMapRole |
| 在同一访问令牌上读取完整 realm 文档 | 映射后执行 GET /admin/realms/{realm} | 侦察 | 实验验证。之前:HTTP 200 部分内容(无 attributes,无 accessTokenLifespan)。之后:HTTP 200 完整内容,植入的 attribute 存在 |
在后续管理员调用中使用嵌套的 manage-realm | UserAdapter.hasRole 遍历复合角色;KeycloakIdentity.hasClientRole 信任用户模型 | 完整性 | 从源码推断。实验判定依据是完整 realm GET |
当复合角色为 realm-admin 时使用嵌套的 realm-admin | 相同遍历 | 完整性 | 推断。实验使用了 manage-realm |
| 阴性对照 | 第二个未映射该复合角色的 manage-users 用户 | 不适用 | 实验验证。对照 GET 保持部分内容 |
在此遗留问题上仍保持封堵
| 维度 | 方法 / 汇聚点 | 为何被封堵 |
|---|---|---|
| 完整性 | 直接映射 manage-realm / realm-admin / 其他 AdminRoles.ALL_ROLES | RolePermissionsV2.isRealmAdminRole 仍会拒绝 |
| 完整性 | 创建包含管理员角色的复合角色 | canMapComposite 仍会调用 checkAdminRoles |
| 完整性 | 加入其复合角色包含管理员角色的组 | CVE-2026-94000 在 GroupUtils.checkAdminGroupRoles 中遍历复合角色 |
| 完整性 | 创建已在该组中的用户 | UsersResource.createUser 上的相同遍历 |
| 侦察 | 匿名 Admin REST | 需要 manage-users 令牌 |
| 完整性 | 主机 RCE、realm-admin 铸造 realm-admin | 超出类别范围 |
GET /admin/realms/{realm} 对 manage-users 并不返回 HTTP 403。该角色本身就是一个管理员角色,因此服务器会返回 200 和一个部分文档。剩下的内容只有在复合角色被映射后才会出现的字段。
我固定使用 Keycloak 26.8.1。细粒度管理员权限 v2 默认开启。RolePermissionsV2.canMapRole 对所有未通过 isRealmAdminRole 的角色都返回 true,并且从不遍历复合角色。CVE-2026-94000 的修复只在组路径上遍历它们。
已记录的弯路:把 v1 的 isRealmAdminRole 当作实际生效的检查(v2 会覆盖它);在授权前期望 realm GET 返回 HTTP 403(实际结果是部分 200);重新提交 CVE-2026-94000(组加入,已关闭);一个反向 shell。纯属作秀。
python3 keycloak-manage-users-composite-Abraxas-Labs.py
或:
cd lab
./run.sh
仅以 http://127.0.0.1:18082 为目标。镜像为 quay.io/keycloak/keycloak:26.8.1。需要 Docker。启动器会 chdir 进入 lab/ 并执行 run.sh。
IOC image=quay.io/keycloak/keycloak:26.8.1 label-version=26.8.1
IOC serverinfo-version=26.8.1
IOC admin-composite=manage-realm
IOC limited-direct=manage-users
IOC control-direct=manage-users
IOC before-status=200 witness=0 full=0
IOC available=sneaky-composite
IOC post-status=204
IOC after-get attempt=1 status=200 witness=1 full=1
IOC control-status=200 witness=0 full=0 same-token=1
SUCCESS KC-H02-ROLEMAP kcr02m7q4
在 RolePermissionsV2.canMapRole 中遍历复合角色,方式与 GroupUtils.checkAdminGroupRoles 在群组加入时已有的做法一致。一个角色只要包含 manage-realm 或 realm-admin,即使其自身名称并非如此,也属于管理员角色。
24ed1dadf3f246a555adb6d3d4a99e9b3003fb3b)RolePermissionsV2.canMapRole(RolePermissionsV2.java:59-61)RoleMapperResource.addRealmRoleMappings(RoleMapperResource.java:289-290)GNU Affero GPL v3.0。参见 LICENSE。仅限回环实验环境。不提供任何担保。
Abraxas Labs [email protected]
请将 CVE 归功于 Abraxas Labs。