CVE-2026-96448: Keycloak FGAP Composite Privilege Escalation

Guilliano Molaire Guilliano Molaire 12 min read

Last updated: September 2026

CVE-2026-96448 is a privilege escalation in Keycloak’s Fine-Grained Admin Permissions (FGAP v2). When a delegated administrator assigns a role to a user or group, Keycloak checks whether that role is itself an administrative role, but it does not look inside composite roles. A limited admin can therefore assign a composite that contains realm-admin or another management role, and end up with full control of the realm. Red Hat published it on 25 September 2026 with a CVSS 3.1 base score of 6.6, and as of 27 September 2026 no community Keycloak release carries a fix. Until one does, the defence is an audit of which composite roles contain management roles, and who is allowed to assign them.

The attacker has to be an administrator already, which is why the score is moderate. The problem is that the administrator in question is exactly the one FGAP exists to contain: a helpdesk lead, a tenant admin in a multi-tenant setup, or a team that manages one application’s roles. This post explains the check that fails, which realms are exposed, how to find the risky composites, and what to change while you wait for a patched release.

What does CVE-2026-96448 allow?

It allows a delegated administrator to escalate to realm administrator by assigning a composite role. The CVE record Red Hat published on 25 September 2026 says the permission check for role assignment “does not look inside composite roles to see what other permissions they contain”, so “an administrator with limited rights can assign a role that secretly includes full administrative control” (CVE Program, CVE-2026-96448 record, 2026).

How FGAP v2 is supposed to bound role assignment

FGAP v2 is the second version of Keycloak’s fine-grained admin permissions. It became a supported feature in Keycloak 26.2, according to the 26.2.0 release notes in the Keycloak repository, and you switch it on per realm with the Admin Permissions setting under Realm settings. Once it is on, you can grant an administrator rights over some users, groups, clients or roles instead of handing out the broad realm-management roles.

For role assignment, the relevant rule is simple. Assigning ordinary roles can be delegated, for example through the map-role scope on the Roles resource type or through the manage-users role. Assigning an administrative role, meaning one of the realm-management client roles such as realm-admin, manage-users or manage-clients, is reserved for realm administrators. The code says so directly: in RolePermissionsV2.canMapRole, the branch for administrative roles carries the comment “only server or realm admins can map roles if FGAP is enabled”.

Why composite roles hide nested privileges

A composite role is a role that bundles other roles. Assign the composite, and the user effectively holds every role inside it, recursively. That is useful for building job-shaped roles such as “support-lead” out of smaller pieces, and it is the mechanism this CVE relies on.

In the current Keycloak source, the helper that decides whether a role counts as administrative, isRealmAdminRole, looks at only two things: the container the role belongs to (the realm’s realm-management client, the master realm’s per-realm admin client, or the master realm itself) and whether the role’s own name is in the list of admin role names. It never expands the role’s children. A custom realm role called support-lead that includes realm-admin as a child is, as far as that check is concerned, an ordinary role, so it falls through to the delegated rules:

if (root.hasOneAdminRole(AdminRoles.MANAGE_USERS)) {
    // user has manage-users, so they can map any non-admin role
    return true;
}

Because of that fall-through, anyone who can assign the composite also receives everything inside it, including the management roles that the admin-role branch was meant to reserve for realm administrators.

Who is exposed to CVE-2026-96448?

You are exposed when some composite role contains a management role and an administrator who is not a realm admin is allowed to assign that composite. The advisory describes the flaw in FGAP v2 terms, and realms with Admin Permissions enabled are the clear case. Red Hat’s vector is CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:H, classified CWE-285 Improper Authorization.

FGAP v2 is in use, and that covers more realms than the advisory suggests. The CVE record scopes the flaw to FGAP v2, lists the Red Hat build of Keycloak as affected and lists Red Hat Single Sign-On 7 as unaffected. Two details in the source matter here. First, AdminPermissions.evaluator picks the V2 permission classes, including RolePermissionsV2, whenever the server-level feature admin-fine-grained-authz:v2 is enabled, and it is enabled by default in current releases. The per-realm Admin Permissions switch changes how directly named admin roles are handled, but the manage-users branch quoted above runs either way.

Second, the name-only check is older than V2: the legacy RolePermissions.checkAdminRoles also only inspects whether a role’s own name is an admin role name. So a manage-users holder could map a composite containing realm-admin with the realm switch off as well. What makes it a CVE under FGAP v2 is that V2 explicitly promises that only realm admins can grant admin roles when Admin Permissions is on, and composites break that promise. Whether Red Hat counts switch-off realms as in scope is not stated in the advisory, so the safe reading is to run the audit below in every realm where someone other than a realm admin holds manage-users.

A composite role holds a management role. The High attack complexity in Red Hat’s vector is consistent with this precondition, although the record gives no rationale for it. In a realm with Admin Permissions enabled, the delegated admin cannot build the composite themselves, because adding a child role goes through canMapComposite, which does treat a directly named admin role as realm-admin-only. The escalation needs a composite that someone with more privilege created earlier. In practice those exist more often than you would hope: an “ops” or “platform-admin” realm role created during setup, a client role that bundles view-users and manage-users for a support tool, or a role left over from a migration.

A delegated admin can assign that composite. In RolePermissionsV2.canMapRole, three kinds of administrator pass for a non-admin role:

Who Why they pass
Holders of the manage-users role Allowed to map “any non-admin role”
Admins who can map roles of the client that owns the role (client roles only) Pass the client-level canMapRoles check
Admins granted the map-role scope on that role Pass the FGAP permission evaluation

The same check guards both user role mappings and group role mappings, since RoleMapperResource serves both. Assigning the composite to a group the attacker belongs to works as well as assigning it to their own user.

If you run Organizations with per-tenant administrators, pay particular attention here, because tenant admins are the most common FGAP population. Our multitenancy guide for the Organizations feature covers that setup, and our write-up of CVE-2026-16072 and CVE-2026-18201 covers two earlier permission gaps in the same area.

How do I find the composites that make this exploitable?

Walk every composite role in the realm, expand its children recursively, and flag any that end in a realm-management client role. The admin console shows direct children under a role’s Associated roles tab, but it does not expand nested composites for you, so a script is more reliable.

Here is a small one against the Admin REST API, using curl and jq. It needs an access token for an account that can view roles and clients (a realm admin, or a service account with view-realm and view-clients):

KC=https://auth.example.com
REALM=myrealm
AUTH="Authorization: Bearer $TOKEN"

# id of the realm-management client in this realm
RM=$(curl -s -H "$AUTH" "$KC/admin/realms/$REALM/clients?clientId=realm-management" | jq -r '.[0].id')

# walk a role's children recursively, printing any realm-management role found.
# $2 carries the ids already on this branch, so a cyclic composite cannot loop forever.
walk() {
  case " $2 " in *" $1 "*) return ;; esac
  local seen="$2 $1"
  curl -s -H "$AUTH" "$KC/admin/realms/$REALM/roles-by-id/$1/composites" |
  jq -c '.[] | {id, name, composite, containerId}' |
  while read -r child; do
    id=$(jq -r .id <<<"$child"); name=$(jq -r .name <<<"$child")
    [ "$(jq -r .containerId <<<"$child")" = "$RM" ] && echo "  contains realm-management/$name"
    [ "$(jq -r .composite <<<"$child")" = "true" ] && walk "$id" "$seen"
  done
}

# realm roles that are composites
curl -s -H "$AUTH" "$KC/admin/realms/$REALM/roles" |
jq -r '.[] | select(.composite) | "(.id) (.name)"' |
while read -r id name; do echo "realm role: $name"; walk "$id"; done

Repeat the last block for each client’s roles (/clients/{id}/roles) if your applications define composite client roles. Keycloak does not stop you from building a cycle of composites, which is why the script tracks the roles it has already visited on each branch. If you repeat the block for the realm-management client, it will also list the built-in realm-admin composite, which you can ignore since it is already treated as an admin role. The realm’s default-roles-<realm> composite normally shows up with no hits, which is expected.

For each composite that turns up, ask who can assign it. List the holders of manage-users in the realm, the administrators with client-level role mapping rights on the owning client, and any FGAP permission that grants map-role on that role or on all roles, and together they are the people who could use this CVE today.

Has anyone already used it?

Nothing public suggests so: the CISA enrichment added to the CVE record on 26 September 2026 lists exploitation as “none” and the flaw as not automatable. For your own realm, check the admin events. Role assignments are recorded with resource type REALM_ROLE_MAPPING or CLIENT_ROLE_MAPPING and operation CREATE, with a resource path such as users/{id}/role-mappings/realm or groups/{id}/role-mappings/realm. With Include representation enabled, the event body lists the roles that were assigned.

Filter those events for the risky composites you found, and look at who performed each assignment. An assignment made by an administrator who is not a realm admin, of a composite that contains a management role, is the pattern this CVE produces. If admin events were not being stored, this is a good moment to turn them on; our Keycloak auditing and event logging guide covers enabling and exporting them. The Keycloak Admin REST API guide shows how to query events and role mappings from a script.

What should I do before a fixed release ships?

Remove the precondition, because as of 27 September 2026 there is nothing to upgrade to. The CVE record lists no fixed version, Red Hat states that no mitigation meets its criteria, and the report reached Red Hat on 22 September 2026, after the newest community releases on each maintained line (26.7.4 on 16 September, 26.6.7 and 26.4.16 on 7 September) were tagged. We also checked the Keycloak main branch as of 25 September 2026, and isRealmAdminRole still inspects only the role itself.

Take management roles out of composites. This is the most direct fix. If a composite role needs to carry manage-users or realm-admin, question whether it should exist at all; assign those roles directly to the few people who need them, where the admin-role check applies. Ordinary composites of application roles are fine.

Restrict who can assign what remains. Where a privileged composite must stay, make sure no delegated administrator can map it. That means removing manage-users from people who are not meant to be realm administrators, and scoping any map-role permission to named roles rather than all roles.

Review the delegated admins you already have. An administrator who can map roles has always been one assignment away from anything those roles grant. The Keycloak security hardening checklist has a broader pass over admin access that is worth running alongside this one.

Plan the upgrade on your own line. Keycloak does ship fixes to older minor lines: 26.4.15 and 26.6.6 both landed on 11 August 2026, after 26.7.0. Watch the release notes for CVE-2026-96448 on the line you run. If you use the Red Hat build of Keycloak, follow Red Hat’s advisory for the product version that carries the fix.

How does this compare with other FGAP CVEs this year?

It is the latest in a run of delegated-admin flaws where a narrow FGAP permission was bypassed through a path that achieves the same effect. CVE-2026-97177 let an admin denied reset-password set passwords through the generic user update. CVE-2026-17526 let the impersonation role impersonate a realm administrator. And the 26.7.3 release fixed CVE-2026-16105 and CVE-2026-16106, both in how delegated admins read and edit role composites, which our 26.7.3 patch checklist covers.

The common thread is that FGAP’s restrictions are only as good as every endpoint that can reach the same outcome. For roles, “the outcome” is the user’s effective role set, which includes everything a composite expands to. When you design a delegated admin model, test it by the effective roles a delegated admin could end up granting, not by the names of the roles they can pick in the console. Our explainers on fine-grained authorization in Keycloak and fine-grained permissions with OPA cover the permission model in more depth.

What does managed Keycloak change here?

On Skycloak, identity management as a service built on upstream Keycloak, we roll security releases onto customer clusters, so when a fix for CVE-2026-96448 ships, it lands without you scheduling an upgrade. The realm design is still yours: which composites contain management roles and who can assign them are configuration decisions, and the audit above applies to a managed realm exactly as it does to a self-hosted one.

Frequently asked questions

Can an unauthenticated attacker exploit CVE-2026-96448?

No. The attacker must already be a Keycloak administrator with some role-mapping right, which is why Red Hat’s CVSS vector sets privileges required to High and the base score is 6.6. The risk is a delegated administrator, or a stolen delegated admin account, turning limited rights into full control of the realm.

Which Keycloak versions are affected?

The CVE record, published on 25 September 2026, lists the Red Hat build of Keycloak as affected without version ranges. FGAP v2 has been a supported feature since Keycloak 26.2, and the composite-blind check is present in 26.7.4, the newest community release. Treat every realm on 26.2 or later with delegated administrators as affected until a fixed release names this CVE.

Is a realm without Admin Permissions enabled affected?

The advisory names FGAP v2 and does not say. In current Keycloak source, a manage-users holder can assign a composite containing realm-admin whether or not the realm switch is on, because both the V2 code and the legacy V1 check look only at the role’s own name. Run the composite audit in every realm to be safe.

Does removing realm-admin from composites fully mitigate it?

Only if you remove every management role, not just realm-admin. A composite that contains manage-users, manage-clients or manage-realm still hands out administrative power the admin-role check was meant to reserve. Red Hat lists no official mitigation, so removing the precondition is the practical control until a fixed release ships.

How do I know if a delegated admin already escalated?

Search admin events for CREATE operations on REALM_ROLE_MAPPING or CLIENT_ROLE_MAPPING that assigned one of your privileged composites, and check who performed them. Assignments by administrators who are not realm admins are the signature. This only works if admin events were being stored at the time.

Sources

  • CVE Program, CVE-2026-96448, “Keycloak-services: keycloak-services: fgap v2 composite-blind role mapping allows privilege escalation”, CNA record from Red Hat, published 25 September 2026, retrieved 2026-09-27, https://www.cve.org/CVERecord?id=CVE-2026-96448 (record JSON: https://github.com/CVEProject/cvelistV5/blob/main/cves/2026/96xxx/CVE-2026-96448.json)
  • Red Hat Customer Portal, “CVE-2026-96448”, https://access.redhat.com/security/cve/CVE-2026-96448 (linked from the CVE record; not reachable from our research environment)
  • Red Hat Bugzilla, bug 2539291, https://bugzilla.redhat.com/show_bug.cgi?id=2539291
  • Keycloak, RolePermissionsV2.java, keycloak/keycloak on GitHub, main branch as of 2026-09-25, retrieved 2026-09-27, https://github.com/keycloak/keycloak/blob/main/services/src/main/java/org/keycloak/services/resources/admin/fgap/RolePermissionsV2.java
  • Keycloak, AdminPermissions.java, keycloak/keycloak on GitHub, retrieved 2026-09-27, https://github.com/keycloak/keycloak/blob/main/services/src/main/java/org/keycloak/services/resources/admin/fgap/AdminPermissions.java
  • Keycloak, RolePermissions.java (legacy checkAdminRoles), keycloak/keycloak on GitHub, retrieved 2026-09-27, https://github.com/keycloak/keycloak/blob/main/services/src/main/java/org/keycloak/services/resources/admin/fgap/RolePermissions.java
  • Keycloak, RoleMapperResource.java, keycloak/keycloak on GitHub, retrieved 2026-09-27, https://github.com/keycloak/keycloak/blob/main/services/src/main/java/org/keycloak/services/resources/admin/RoleMapperResource.java
  • Keycloak, release notes for 26.2.0, “Fine-grained admin permissions supported”, keycloak/keycloak on GitHub, retrieved 2026-09-27, https://github.com/keycloak/keycloak/blob/main/docs/documentation/release_notes/topics/26_2_0.adoc
  • Keycloak releases, tags 26.7.4, 26.6.7 and 26.4.16, retrieved 2026-09-27, https://github.com/keycloak/keycloak/releases

Patched on the day, not on your next maintenance window

Keycloak 26.7.2 fixed CVE-2026-18963, an account takeover through password reset. Skycloak had it available for auto-upgrade and told customers the same day upstream shipped it. Self-hosted teams schedule that work themselves.

Guilliano Molaire
Written by
Founder

Guilliano is the founder of Skycloak and a cloud infrastructure specialist with deep expertise in product development and scaling SaaS products. He discovered Keycloak while consulting on enterprise IAM and built Skycloak to make managed Keycloak accessible to teams of every size.

Start Free Trial Talk to Sales
© 2026 Skycloak. All Rights Reserved. Design by Yasser Soliman