Skip to content
KitploitKITPLOIT
HerramientasExploitsBlog
Log in
Enviar
HerramientasExploitsBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

FeedsContactoPrivacidad© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
KeycloakESC — Laboratorio Docker reproducible + PoC: escalada de Keycloak manage-users -> realm-admin mediante roles compuestos (variante de CVE-2026-94000). Validado en 26.8.1. | Kitploit
Herramientas/GitHubGitHub/secfathy/keycloakesc
Autenticación y AutorizaciónEscalada de PrivilegiosAnálisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebPruebas de PenetraciónGestión de Identidad y Acceso (IAM)Labs y Práctica
GitHubsecfathy/keycloakesc

KeycloakESC

Laboratorio Docker reproducible + PoC: escalada de Keycloak manage-users -> realm-admin mediante roles compuestos (variante de CVE-2026-94000). Validado en 26.8.1.

3hace 13 díasAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir
Ver Repositorio

Escalada de privilegios mediante map-role compuesto en Keycloak

Un laboratorio Docker reproducible y PoC para una ruta de escalada de privilegios en la API REST de administración de Keycloak: un administrador delegado que posee solo manage-users puede escalar sus propios privilegios (o los de cualquier usuario o grupo) a realm-admin completo mediante la asignación de un rol compuesto cuyo nombre es ordinario pero cuyos hijos incluyen realm-management/realm-admin.

Confirmado de extremo a extremo contra la imagen oficial quay.io/keycloak/keycloak:26.8.1.

⚠️ Solo investigación autorizada. Todo se ejecuta contra un Keycloak desechable en localhost. No apuntes el PoC a sistemas que no estés explícitamente autorizado a probar.

  • El problema (causa raíz)
  • Prueba de concepto
  • Ejecución verificada
  • Remediación

El problema (causa raíz)

Keycloak 26.8.1 endureció la unión a grupos (CVE-2026-94000): un administrador con manage-users ya no puede añadir un usuario a un grupo que otorgue de forma transitiva roles de administración de realm-management. GroupUtils.checkAdminGroupRoles recorre los roles del grupo recursivamente y llama a requireMapRole en cada rol administrativo anidado:

private static void requireMapRoleRecursive(RoleModel role, AdminPermissionEvaluator auth, Set<String> visited) {
    if (!visited.add(role.getId())) return;
    if (AdminRoles.isAdminRole(role)) auth.roles().requireMapRole(role);   // checks nested admin roles
    if (role.isComposite()) role.getCompositesStream().forEach(child -> requireMapRoleRecursive(child, auth, visited));
}

El mismo recorrido recursivo nunca se añadió a los endpoints de mapeo de roles. Cuando un rol se mapea directamente a un usuario o grupo, el servidor solo verifica el rol enviado:

// RoleMapperResource.addRealmRoleMappings — mounted on BOTH users and groups
for (RoleRepresentation role : roles) {
    RoleModel roleModel = realm.getRole(role.getName());
    auth.roles().requireMapRole(roleModel);   // posted role only — children never inspected
    roleMapper.grantRole(roleModel);
}

Y la comprobación de privilegios en sí se basa en el nombre del rol, no en su contenido. En RolePermissionsV2.canMapRole, un rol se considera privilegiado solo cuando su contenedor es realm-management/master y su nombre está en AdminRoles.ALL_ROLES:

public boolean canMapRole(RoleModel role) {
    if (isRealmAdminRole(role)) { ... }                 // isRealmAdminRole = container + ALL_ROLES.contains(name)
    if (root.hasOneAdminRole(AdminRoles.MANAGE_USERS))  // manage-users may map ANY non-admin-named role
        return true;
    ...
}

Así que un compuesto llamado sneaky-composite que envuelve realm-management/realm-admin pasa la puerta — su nombre no está en ALL_ROLES, y sus hijos nunca son recorridos. La brecha abarca tres endpoints:

  • RoleMapperResource.addRealmRoleMappings — usuarios y grupos
  • ClientRoleMappingsResource.addClientRoleMapping — roles de cliente en cualquier cliente

Efecto neto: la carga útil privilegiada (un compuesto que envuelve realm-admin) solo necesita existir — creada por un administrador real, una importación de realm o un proceso anterior. manage-users puede entonces asignarla, lo que el modelo de amenazas de CVE-2026-94000 nunca contempló.

Variantes de ataque

#VariantePor qué funciona
AAuto-mapear sneaky-composite → realm-adminla puerta por nombre no detecta el compuesto
BMapear outer-sneaky anidado (→ sneaky-composite → realm-admin) sobre un usuariono se comprueba la profundidad
CMapear un rol de cliente en un cliente no administrativo (spa/hidden-ra → realm-admin)ClientRoleMappingsResource usa la misma puerta
DMapear el compuesto sobre un grupo que ya tiene miembrosAPI distinta a la de unión; los miembros existentes escalan sin ninguna unión

Prueba de concepto

Requisitos: Docker + Docker Compose v2, y curl + jq en el host.

cp .env.example .env     # optional: pin KC_VERSION (default 26.8.1)
make up                  # start Keycloak + auto-bootstrap the vulnerable realm `app`
make logs                # wait for "Lab realm 'app' is ready"

make check               # non-destructive detection (maps, then immediately unmaps)
make exploit             # Attack A: self-escalate limited-admin -> realm-admin
make all                 # full chain: check + A + B + C + D
make reset               # undo every escalation (keep the lab running)
make clean               # tear everything down

El contenedor bootstrap de una sola ejecución construye el escenario a través de la API REST de administración y luego finaliza:

ObjetoPropósito
limited-admin / limitedpassAtacante — solo posee manage-users
alice / bob / carol / daveUsuarios víctima
sneaky-composite (rol de realm)Compuesto → realm-management/realm-admin
outer-sneaky (rol de realm)Compuesto → sneaky-composite (un salto adicional)
Cliente spa + rol spa/hidden-raRol de cliente no administrador → realm-admin
Cliente confidential (confsecret)Objetivo de impacto de lectura de secretos
Grupo helpdesk (alice es miembro)Objetivo del ataque de mapeo de grupos

La PoC resuelve los IDs de rol de la forma en que un llamador con manage-users realmente puede hacerlo — a través de los endpoints .../role-mappings/.../available — porque a esa cuenta se le deniega GET /roles/{name} y GET /clients (403) pero no las listas de disponibles. Esto mantiene el script como una reproducción honesta desde la perspectiva de un atacante con bajos privilegios.

Validación de una corrección

Apunte el laboratorio a una compilación parcheada; un servidor corregido debe devolver 403 en cada paso de mapeo mientras sigue permitiendo el mapeo ordinario de roles no administradores:

KC_VERSION=<patched-tag> docker compose up -d --force-recreate
make check        # expect: NOT vulnerable

Ejecución verificada

Contra quay.io/keycloak/keycloak:26.8.1 (transcripción completa en docs/demo.txt, detalles en VALIDATION.md):

PasoAcciónResultado
Línea baselimited-admin POST /roles403 (genuinamente de bajo privilegio)
checkmapear sneaky-composite sobre sí mismo (luego desmapear)204 → VULNERABLE
Aautomapeo de sneaky-composite → realm-admin → crear rol / volcar correos de usuarios204 / 201 / 200
Bouter-sneaky anidado sobre bob → bob crea rol204 / 201
Cspa/hidden-ra sobre carol → carol crea rol204 / 201
Dmapear el wrapper sobre helpdesk → el miembro existente alice crea rol204 / 201
Control Dmanage-users une un nuevo miembro a helpdesk403 (corrección de CVE-2026-94000 intacta)

El control D demuestra la asimetría: la unión a un grupo está bloqueada, pero mapear el mismo compuesto sobre el grupo (o sobre un usuario/cliente) no se recorre y devuelve 204.


Remediación

Descargar herramienta