Low severity2.7NVD Advisory· Published Feb 2, 2026· Updated Apr 15, 2026
CVE-2025-13881
CVE-2025-13881
Description
A flaw was found in Keycloak Admin API. This vulnerability allows an administrator with limited privileges to retrieve sensitive custom attributes via the /unmanagedAttributes endpoint, bypassing User Profile visibility settings.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.keycloak:keycloak-servicesMaven | >= 26.5.0, < 26.5.2 | 26.5.2 |
org.keycloak:keycloak-servicesMaven | < 26.4.9 | 26.4.9 |
Affected products
1Patches
21d7ab8d5fb14fixed unmanaged attributes to not allow writing when only admin can view policy is enabled
3 files changed · +83 −7
server-spi-private/src/main/java/org/keycloak/userprofile/DefaultAttributes.java+2 −6 modified@@ -260,20 +260,16 @@ 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)) { + if ((UserModel.USERNAME.equals(name) && realm.isRegistrationEmailAsUsername())) { 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+72 −0 modified@@ -4,14 +4,18 @@ import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; + +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 java.util.ArrayList; import java.util.Collections; @@ -142,6 +146,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; }
c5c83d6604d4Fix test failures
1 file changed · +6 −0
server-spi-private/src/main/java/org/keycloak/userprofile/DefaultAttributes.java+6 −0 modified@@ -261,6 +261,12 @@ public Map<String, List<String>> getWritable() { Map<String, List<String>> attributes = new HashMap<>(this); for (String name : nameSet()) { + RealmModel realm = session.getContext().getRealm(); + + if ((UserModel.USERNAME.equals(name) && realm.isRegistrationEmailAsUsername())) { + continue; + } + if (isReadOnly(name)) { attributes.remove(name); }
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
10- github.com/advisories/GHSA-g78x-7vwx-9f58ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-13881ghsaADVISORY
- access.redhat.com/errata/RHSA-2026:2365nvdWEB
- access.redhat.com/errata/RHSA-2026:2366nvdWEB
- access.redhat.com/security/cve/CVE-2025-13881nvdWEB
- bugzilla.redhat.com/show_bug.cginvdWEB
- github.com/keycloak/keycloak/commit/1d7ab8d5fb1403902f5152820a8fc734d38b08d2ghsaWEB
- github.com/keycloak/keycloak/commit/c5c83d6604d4c73139f38fce3ed7b7c4c38c09f2ghsaWEB
- github.com/keycloak/keycloak/issues/45873ghsaWEB
- github.com/keycloak/keycloak/pull/45427ghsaWEB
News mentions
0No linked articles in our index yet.