VYPR
Moderate severityNVD Advisory· Published Feb 27, 2026· Updated Mar 6, 2026

Org.keycloak/keycloak-services: keycloak: unauthorized modification of unmanaged user attributes by administrators

CVE-2026-0871

Description

A flaw was found in Keycloak. An administrator with manage-users permission can bypass the "Only administrators can view" setting for unmanaged attributes, allowing them to modify these attributes. This improper access control can lead to unauthorized changes to user profiles, even when the system is configured to restrict such modifications.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Keycloak administrators with manage-users permission can bypass the 'Only administrators can view' setting for unmanaged attributes, leading to unauthorized modifications.

Vulnerability

Overview

CVE-2026-0871 is an improper access control vulnerability in Keycloak. A flaw exists where an administrator with the manage-users permission can bypass the 'Only administrators can view' setting for unmanaged attributes, allowing them to modify these attributes even when the system is configured to restrict such modifications [1][2]. This bypass occurs because the access control check does not properly enforce the configuration for unmanaged attributes, enabling unauthorized writes.

Exploitation

To exploit this vulnerability, an attacker must have an administrator account with the manage-users permission in Keycloak. No other special privileges are required. The attacker can then modify unmanaged attributes on user profiles, which are intended to be read-only for administrators under the 'Only administrators can view' setting. The vulnerability is triggered through the Admin API or admin console when updating user attributes [3].

Impact

Successful exploitation allows an administrator to make unauthorized changes to user profiles, potentially altering sensitive or security-relevant attributes. This could lead to privilege escalation, data integrity issues, or other security breaches depending on how the unmanaged attributes are used in the environment [1][2].

Mitigation

Red Hat has released a security advisory (RHSA-2026:2366) for Keycloak 26.4.9 that includes a fix for this vulnerability [4]. The fix ensures that the 'Only administrators can view' setting is properly enforced for unmanaged attributes, preventing unauthorized modifications [3]. Users should update to the latest version of Keycloak to remediate this issue.

AI Insight generated on May 18, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.keycloak:keycloak-server-spi-privateMaven
< 26.5.226.5.2

Affected products

5
  • Red Hat/Red Hat build of Keycloak 26.4.9v5
    cpe:/a:redhat:build_keycloak:26.4::el9
  • Red Hat/Red Hat JBoss Enterprise Application Platform Expansion Packv5
    cpe:/a:redhat:jbosseapxp
  • Red Hat/Red Hat JBoss Enterprise Application Platform 8v5
    cpe:/a:redhat:jboss_enterprise_application_platform:8
  • Red Hat/Red Hat Single Sign-On 7v5
    cpe:/a:redhat:red_hat_single_sign_on:7

Patches

1
9d0f679ecea4

45417 fixed unmanaged attributes to not allow writing when only admin can view policy is enabled

https://github.com/keycloak/keycloakSebastian SchusterJan 9, 2026via ghsa
3 files changed · +81 12
  • server-spi-private/src/main/java/org/keycloak/userprofile/DefaultAttributes.java+1 11 modified
    @@ -261,20 +261,10 @@ public Map<String, List<String>> getWritable() {
             Map<String, List<String>> attributes = new HashMap<>(this);
     
             for (String name : nameSet()) {
    -            AttributeMetadata metadata = getMetadata(name);
    -            RealmModel realm = session.getContext().getRealm();
    -
    -            if ((UserModel.USERNAME.equals(name) && realm.isRegistrationEmailAsUsername())
    -                || isReadableOrWritableDuringRegistration(name)
    -                || !isManagedAttribute(name)) {
    -                continue;
    -            }
    -
    -            if (metadata == null || !metadata.canEdit(createAttributeContext(metadata))) {
    +            if (isReadOnly(name)) {
                     attributes.remove(name);
                 }
             }
    -
             return attributes;
         }
     
    
  • tests/base/src/test/java/org/keycloak/tests/admin/user/UserAttributesTest.java+71 0 modified
    @@ -9,14 +9,17 @@
     
     import jakarta.ws.rs.BadRequestException;
     
    +import org.keycloak.admin.client.resource.UserProfileResource;
     import org.keycloak.models.LDAPConstants;
     import org.keycloak.models.UserModel;
     import org.keycloak.representations.idm.ErrorRepresentation;
     import org.keycloak.representations.idm.RealmRepresentation;
     import org.keycloak.representations.idm.UserRepresentation;
    +import org.keycloak.representations.userprofile.config.UPConfig;
     import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
     import org.keycloak.testframework.server.KeycloakServerConfig;
     import org.keycloak.testframework.server.KeycloakServerConfigBuilder;
    +import org.keycloak.testsuite.util.userprofile.UserProfileUtil;
     
     import org.hamcrest.Matchers;
     import org.junit.jupiter.api.Assertions;
    @@ -144,6 +147,74 @@ public void attributes() {
             assertEquals(1, user1.getAttributes().size());
         }
     
    +    @Test
    +    public void unmanagedAttributes() {
    +        UserProfileResource userProfileRes = managedRealm.admin().users().userProfile();
    +        UserProfileUtil.enableUnmanagedAttributes(userProfileRes);
    +        adminEvents.poll();
    +
    +        UserRepresentation user1 = new UserRepresentation();
    +        user1.setUsername("user1");
    +        user1.singleAttribute("attr1", "value1");
    +        user1.singleAttribute("unmanaged1", "value2");
    +        String user1Id = createUser(user1);
    +
    +        //check visibility of attributes depending on unmanaged attribute policy
    +
    +        //with unmanaged attributes enabled, managed and unmanaged attributes are visible
    +        user1 = managedRealm.admin().users().get(user1Id).toRepresentation();
    +        assertEquals(2, user1.getAttributes().size());
    +        assertAttributeValue("value1", user1.getAttributes().get("attr1"));
    +        assertAttributeValue("value2", user1.getAttributes().get("unmanaged1"));
    +
    +        //with unmanaged attributes disabled, only managed attributes are visible
    +        UserProfileUtil.disableUnmanagedAttributes(userProfileRes);
    +        adminEvents.poll();
    +        user1 = managedRealm.admin().users().get(user1Id).toRepresentation();
    +        assertEquals(1, user1.getAttributes().size());
    +        assertAttributeValue("value1", user1.getAttributes().get("attr1"));
    +
    +        //with ADMIN_EDIT policy, managed and unmanaged attributes are visible
    +        UserProfileUtil.setUnmanagedAttributesPolicy(userProfileRes, UPConfig.UnmanagedAttributePolicy.ADMIN_EDIT);
    +        adminEvents.poll();
    +        user1 = managedRealm.admin().users().get(user1Id).toRepresentation();
    +        assertEquals(2, user1.getAttributes().size());
    +        assertAttributeValue("value1", user1.getAttributes().get("attr1"));
    +        assertAttributeValue("value2", user1.getAttributes().get("unmanaged1"));
    +
    +        //with ADMIN_VIEW policy, managed and unmanaged attributes are visible
    +        UserProfileUtil.setUnmanagedAttributesPolicy(userProfileRes, UPConfig.UnmanagedAttributePolicy.ADMIN_VIEW);
    +        adminEvents.poll();
    +        user1 = managedRealm.admin().users().get(user1Id).toRepresentation();
    +        assertEquals(2, user1.getAttributes().size());
    +        assertAttributeValue("value1", user1.getAttributes().get("attr1"));
    +        assertAttributeValue("value2", user1.getAttributes().get("unmanaged1"));
    +
    +        //check updating of attributes depending on unmanaged attribute policy
    +
    +        //with ADMIN_VIEW policy, only managed attributes can be updated
    +        user1.singleAttribute("attr1", "updated1");
    +        user1.singleAttribute("unmanaged1", "updated2");
    +        user1.singleAttribute("unmanaged2", "value3");
    +        updateUser(managedRealm.admin().users().get(user1Id), user1);
    +        user1 = managedRealm.admin().users().get(user1Id).toRepresentation();
    +        assertEquals(2, user1.getAttributes().size());
    +        assertAttributeValue("updated1", user1.getAttributes().get("attr1"));
    +        assertAttributeValue("value2", user1.getAttributes().get("unmanaged1"));
    +
    +        //with ADMIN_EDIT policy, managed and unmanaged attributes can be updated
    +        UserProfileUtil.setUnmanagedAttributesPolicy(userProfileRes, UPConfig.UnmanagedAttributePolicy.ADMIN_EDIT);
    +        adminEvents.poll();
    +        user1.singleAttribute("unmanaged1", "updated2");
    +        user1.singleAttribute("unmanaged2", "value3");
    +        updateUser(managedRealm.admin().users().get(user1Id), user1);
    +        user1 = managedRealm.admin().users().get(user1Id).toRepresentation();
    +        assertEquals(3, user1.getAttributes().size());
    +        assertAttributeValue("updated1", user1.getAttributes().get("attr1"));
    +        assertAttributeValue("updated2", user1.getAttributes().get("unmanaged1"));
    +        assertAttributeValue("value3", user1.getAttributes().get("unmanaged2"));
    +    }
    +
         @Test
         public void updateUserWithReadOnlyAttributes() {
             // Admin is able to update "usercertificate" attribute
    
  • tests/utils-shared/src/main/java/org/keycloak/testsuite/util/userprofile/UserProfileUtil.java+9 1 modified
    @@ -59,8 +59,16 @@ public static UPConfig setUserProfileConfiguration(RealmResource testRealm, Stri
         }
     
         public static UPConfig enableUnmanagedAttributes(UserProfileResource upResource) {
    +        return setUnmanagedAttributesPolicy(upResource, UPConfig.UnmanagedAttributePolicy.ENABLED);
    +    }
    +
    +    public static UPConfig disableUnmanagedAttributes(UserProfileResource upResource) {
    +        return setUnmanagedAttributesPolicy(upResource, null);
    +    }
    +
    +    public static UPConfig setUnmanagedAttributesPolicy(UserProfileResource upResource, UPConfig.UnmanagedAttributePolicy policy) {
             UPConfig cfg = upResource.getConfiguration();
    -        cfg.setUnmanagedAttributePolicy(UPConfig.UnmanagedAttributePolicy.ENABLED);
    +        cfg.setUnmanagedAttributePolicy(policy);
             upResource.update(cfg);
             return cfg;
         }
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.