VYPR
High severity8.1NVD Advisory· Published Oct 9, 2024· Updated Apr 15, 2026

CVE-2024-3656

CVE-2024-3656

Description

A flaw was found in Keycloak. Certain endpoints in Keycloak's admin REST API allow low-privilege users to access administrative functionalities. This flaw allows users to perform actions reserved for administrators, potentially leading to data breaches or system compromise.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.keycloak:keycloak-servicesMaven
< 24.0.524.0.5

Patches

1
d9f0c84b7975

Missing auth checks in some admin endpoints (#166)

https://github.com/keycloak/keycloakRicardo MartinMay 21, 2024via ghsa
5 files changed · +42 7
  • federation/ldap/src/main/java/org/keycloak/services/resources/admin/TestLdapConnectionResource.java+1 2 modified
    @@ -16,9 +16,7 @@
      */
     package org.keycloak.services.resources.admin;
     
    -import org.jboss.logging.Logger;
     import org.jboss.resteasy.reactive.NoCache;
    -import org.keycloak.common.ClientConnection;
     import org.keycloak.models.KeycloakSession;
     import org.keycloak.models.LDAPConstants;
     import org.keycloak.models.RealmModel;
    @@ -89,6 +87,7 @@ public Response testLDAPConnection(@FormParam("action") String action, @FormPara
         @NoCache
         @Consumes(MediaType.APPLICATION_JSON)
         public Response testLDAPConnection(TestLdapConnectionRepresentation config) {
    +        auth.realm().requireManageRealm();
             try {
                 LDAPServerCapabilitiesManager.testLDAP(config, session, realm);
                 return Response.noContent().build();
    
  • rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/UserResource.java+6 2 modified
    @@ -17,9 +17,9 @@
     import jakarta.ws.rs.core.MediaType;
     import org.jboss.resteasy.reactive.NoCache;
     import org.keycloak.models.KeycloakSession;
    -import org.keycloak.models.RealmModel;
     import org.keycloak.models.UserModel;
     import org.keycloak.representations.userprofile.config.UPConfig;
    +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
     import org.keycloak.userprofile.UserProfile;
     import org.keycloak.userprofile.UserProfileProvider;
     import org.keycloak.utils.StringUtil;
    @@ -30,10 +30,12 @@
     public class UserResource {
     
         private final KeycloakSession session;
    +    private final AdminPermissionEvaluator auth;
         private final UserModel user;
     
    -    public UserResource(KeycloakSession session, UserModel user) {
    +    public UserResource(KeycloakSession session, AdminPermissionEvaluator auth, UserModel user) {
             this.session = session;
    +        this.auth = auth;
             this.user = user;
         }
     
    @@ -42,6 +44,8 @@ public UserResource(KeycloakSession session, UserModel user) {
         @NoCache
         @Produces(MediaType.APPLICATION_JSON)
         public Map<String, List<String>> getUnmanagedAttributes() {
    +        auth.users().requireView(user);
    +
             UserProfileProvider provider = session.getProvider(UserProfileProvider.class);
     
             UserProfile profile = provider.create(USER_API, user);
    
  • rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/UsersResource.java+1 1 modified
    @@ -40,6 +40,6 @@ public UserResource getUser(@PathParam("id") String id) {
                 else throw new ForbiddenException();
             }
     
    -        return new UserResource(session, user);
    +        return new UserResource(session, auth, user);
         }
     }
    
  • services/src/main/java/org/keycloak/services/resources/admin/ClientRegistrationPolicyResource.java+1 0 modified
    @@ -74,6 +74,7 @@ public ClientRegistrationPolicyResource(KeycloakSession session, AdminPermission
         @Tag(name = KeycloakOpenAPI.Admin.Tags.CLIENT_REGISTRATION_POLICY)
         @Operation( summary="Base path for retrieve providers with the configProperties properly filled")
         public Stream<ComponentTypeRepresentation> getProviders() {
    +        auth.realm().requireViewRealm();
             return session.getKeycloakSessionFactory().getProviderFactoriesStream(ClientRegistrationPolicy.class)
                     .map((ProviderFactory factory) -> {
                         ClientRegistrationPolicyFactory clientRegFactory = (ClientRegistrationPolicyFactory) factory;
    
  • testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java+33 2 modified
    @@ -24,6 +24,7 @@
     import org.junit.Test;
     import org.keycloak.admin.client.Keycloak;
     import org.keycloak.admin.client.resource.AuthorizationResource;
    +import org.keycloak.admin.client.resource.BearerAuthFilter;
     import org.keycloak.admin.client.resource.RealmResource;
     import org.keycloak.common.Profile;
     import org.keycloak.models.AdminRoles;
    @@ -51,6 +52,7 @@
     import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
     import org.keycloak.representations.idm.RequiredActionProviderSimpleRepresentation;
     import org.keycloak.representations.idm.RoleRepresentation;
    +import org.keycloak.representations.idm.TestLdapConnectionRepresentation;
     import org.keycloak.representations.idm.UserRepresentation;
     import org.keycloak.representations.idm.authorization.PolicyRepresentation;
     import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
    @@ -73,6 +75,8 @@
     import org.keycloak.userprofile.DeclarativeUserProfileProvider;
     
     import jakarta.ws.rs.ClientErrorException;
    +import jakarta.ws.rs.client.Client;
    +import jakarta.ws.rs.core.MediaType;
     import jakarta.ws.rs.core.Response;
     import java.lang.reflect.Method;
     import java.util.Arrays;
    @@ -375,7 +379,11 @@ public void invoke(RealmResource realm) {
     
             invoke(new InvocationWithResponse() {
                 public void invoke(RealmResource realm, AtomicReference<Response> response) {
    -                response.set(realm.testLDAPConnection("nosuch", "nosuch", "nosuch", "nosuch", "nosuch", "nosuch"));
    +                TestLdapConnectionRepresentation config = new TestLdapConnectionRepresentation(
    +                        "nosuch", "nosuch", "nosuch", "nosuch", "nosuch", "nosuch");
    +                response.set(realm.testLDAPConnection(config.getAction(), config.getConnectionUrl(), config.getBindDn(),
    +                        config.getBindCredential(), config.getUseTruststoreSpi(), config.getConnectionTimeout()));
    +                response.set(realm.testLDAPConnection(config));
                 }
             }, Resource.REALM, true);
     
    @@ -1458,6 +1466,21 @@ public void invoke(RealmResource realm) {
                     realm.users().get(user.getId()).toRepresentation();
                 }
             }, Resource.USER, false);
    +        invoke(new InvocationWithResponse() {
    +            public void invoke(RealmResource realm, AtomicReference<Response> response) {
    +                // no-op
    +            }
    +            public void invoke(Keycloak keycloak, RealmResource realm, AtomicReference<Response> response) {
    +                try (Client client = Keycloak.getClientProvider().newRestEasyClient(null, null, true)) {
    +                    Response resp = client.target(suiteContext.getAuthServerInfo().getContextRoot().toString() + "/auth")
    +                            .path("/admin/realms/" + realm.toRepresentation().getRealm() + "/ui-ext/users/" + user.getId() + "/unmanagedAttributes")
    +                            .register(new BearerAuthFilter(keycloak.tokenManager()))
    +                            .request(MediaType.APPLICATION_JSON)
    +                            .get();
    +                    response.set(resp);
    +                }
    +            }
    +        }, Resource.USER, false);
             invoke(new Invocation() {
                 public void invoke(RealmResource realm) {
                     realm.users().get(user.getId()).update(user);
    @@ -1757,6 +1780,11 @@ public void invoke(RealmResource realm) {
                     realm.components().query("nosuch");
                 }
             }, Resource.REALM, false);
    +        invoke(new Invocation() {
    +            public void invoke(RealmResource realm) {
    +                realm.clientRegistrationPolicy().getProviders();
    +            }
    +        }, Resource.REALM, false);
             invoke(new InvocationWithResponse() {
                 public void invoke(RealmResource realm, AtomicReference<Response> response) {
                     response.set(realm.components().add(new ComponentRepresentation()));
    @@ -1945,7 +1973,7 @@ private void invoke(InvocationWithResponse invocation, Keycloak client, boolean
             int statusCode;
             try {
                 AtomicReference<Response> responseReference = new AtomicReference<>();
    -            invocation.invoke(client.realm(REALM_NAME), responseReference);
    +            invocation.invoke(client, client.realm(REALM_NAME), responseReference);
                 Response response = responseReference.get();
                 if (response != null) {
                     statusCode = response.getStatus();
    @@ -2054,6 +2082,9 @@ public interface InvocationWithResponse {
     
             void invoke(RealmResource realm, AtomicReference<Response> response);
     
    +        default void invoke(Keycloak keycloak, RealmResource realm, AtomicReference<Response> response) {
    +            invoke(realm, response);
    +        }
         }
     
         private void assertGettersEmpty(RealmRepresentation rep) {
    

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

12

News mentions

0

No linked articles in our index yet.