Hi,
We are using the OWASP ZAP tool
(
https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project) to run basic security
testing against our web portal of which Keycloak is a key part. When ZAP runs the Keycloak
log fills up quickly with all kinds of stack traces. One of them is the one below.
In this case the ZAP tool attempts to login on the Keycloak login page securing our custom
realm with the following username:
ZAP') UNION ALL select NULL --
It seems from the stacktrace below that Keycloak uses this ‘username’ as is without any
escaping when querying LDAP(/AD) This results in an incorrect LDAP query because the
parenthesis are now ‘unbalanced’. I think all special characters in a username should be
escaped before the LDAP query is done? It seems that this is a case of LDAP Injection
(
https://www.owasp.org/index.php/LDAP_injection)
[0m[31m14:06:37,437 ERROR
[org.keycloak.federation.ldap.idm.store.ldap.LDAPOperationManager] (default task-21) Could
not query server using DN [ou=Users,ou=Customers,dc=graydon-test,dc=hf,dc=info,dc=nl] and
filter [(&(userPrincipalname=ZAP') UNION ALL select NULL --
)(objectclass=person)(objectclass=organizationalPerson)(objectclass=user)(objectclass=graydonCustomerPerson))]:
javax.naming.directory.InvalidSearchFilterException: Unbalanced parenthesis; remaining
name 'ou=Users,ou=Customers,dc=graydon-test,dc=hf,dc=info,dc=nl'
at com.sun.jndi.ldap.Filter.findRightParen(Filter.java:694)
at com.sun.jndi.ldap.Filter.encodeFilterList(Filter.java:733)
at com.sun.jndi.ldap.Filter.encodeComplexFilter(Filter.java:657)
at com.sun.jndi.ldap.Filter.encodeFilter(Filter.java:104)
at com.sun.jndi.ldap.Filter.encodeFilterString(Filter.java:74)
at com.sun.jndi.ldap.LdapClient.search(LdapClient.java:546)
at com.sun.jndi.ldap.LdapCtx.doSearch(LdapCtx.java:1985)
at com.sun.jndi.ldap.LdapCtx.searchAux(LdapCtx.java:1844)
at com.sun.jndi.ldap.LdapCtx.c_search(LdapCtx.java:1769)
at com.sun.jndi.toolkit.ctx.ComponentDirContext.p_search(ComponentDirContext.java:392)
at
com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.search(PartialCompositeDirContext.java:358)
at
com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.search(PartialCompositeDirContext.java:341)
at javax.naming.directory.InitialDirContext.search(InitialDirContext.java:267)
at javax.naming.directory.InitialDirContext.search(InitialDirContext.java:267)
at
org.keycloak.federation.ldap.idm.store.ldap.LDAPOperationManager$2.execute(LDAPOperationManager.java:169)
at
org.keycloak.federation.ldap.idm.store.ldap.LDAPOperationManager$2.execute(LDAPOperationManager.java:166)
at
org.keycloak.federation.ldap.idm.store.ldap.LDAPOperationManager.execute(LDAPOperationManager.java:536)
at
org.keycloak.federation.ldap.idm.store.ldap.LDAPOperationManager.search(LDAPOperationManager.java:166)
at
org.keycloak.federation.ldap.idm.store.ldap.LDAPIdentityStore.fetchQueryResults(LDAPIdentityStore.java:160)
at
org.keycloak.federation.ldap.idm.query.internal.LDAPQuery.getResultList(LDAPQuery.java:165)
at
org.keycloak.federation.ldap.idm.query.internal.LDAPQuery.getFirstResult(LDAPQuery.java:176)
at
org.keycloak.federation.ldap.LDAPFederationProvider.loadLDAPUserByUsername(LDAPFederationProvider.java:510)
at
org.keycloak.federation.ldap.LDAPFederationProvider.getUserByUsername(LDAPFederationProvider.java:305)
at
org.keycloak.models.UserFederationManager.getUserByUsername(UserFederationManager.java:237)
at
org.keycloak.models.utils.KeycloakModelUtils.findUserByNameOrEmail(KeycloakModelUtils.java:273)
at
org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator.validateUserAndPassword(AbstractUsernameFormAuthenticator.java:127)
at
org.keycloak.authentication.authenticators.browser.UsernamePasswordForm.validateForm(UsernamePasswordForm.java:56)
at
org.keycloak.authentication.authenticators.browser.UsernamePasswordForm.action(UsernamePasswordForm.java:49)
at
org.keycloak.authentication.DefaultAuthenticationFlow.processAction(DefaultAuthenticationFlow.java:84)
at
org.keycloak.authentication.DefaultAuthenticationFlow.processAction(DefaultAuthenticationFlow.java:75)
at
org.keycloak.authentication.AuthenticationProcessor.authenticationAction(AuthenticationProcessor.java:759)
at
org.keycloak.services.resources.LoginActionsService.processFlow(LoginActionsService.java:356)
at
org.keycloak.services.resources.LoginActionsService.processAuthentication(LoginActionsService.java:338)
at
org.keycloak.services.resources.LoginActionsService.authenticateForm(LoginActionsService.java:383)
at sun.reflect.GeneratedMethodAccessor480.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:139)
at
org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:295)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:249)
at
org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:138)
at
org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:101)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:395)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:202)
at
org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:221)
at
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56)
at
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85)
at
io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
at
org.keycloak.services.filters.KeycloakSessionServletFilter.doFilter(KeycloakSessionServletFilter.java:90)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:60)
at
io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
at
io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at
io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at
org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at
io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131)
at
io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at
io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at
io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at
io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
at
io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
at
io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50)
at
io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at
org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at
io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:284)
at
io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:263)
at
io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
at
io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:174)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:202)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:793)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
[0m[33m14:06:37,439 WARN [org.keycloak.events] (default task-21) type=LOGIN_ERROR,
realmId=graydon-customers, clientId=account, userId=null, ipAddress=172.23.6.21,
error=invalid_user_credentials, auth_method=openid-connect, auth_type=code,
redirect_uri=https://gry-test.hf.info.nl/auth/realms/graydon-customers/account/login-redirect,
code_id=7ee2f340-5bf8-42a2-b1ef-32890a78c305, username='ZAP') UNION ALL select
NULL -- '