CVE-2022-24832 (Java, published 2022-04-11T21:15Z)
Description: value: GoCD is an open source a continuous delivery server. The bundled gocd-ldap-authentication-plugin included with the GoCD Server fails to correctly escape special characters when using the username to construct LDAP queries. While this does not directly allow arbitrary LDAP data exfiltration, it can allow an existing LDAP-authenticated GoCD user with malicious intent to construct and execute malicious queries, allowing them to deduce facts about other users or entries within the LDAP database (e.g alternate fields, usernames, hashed passwords etc) through brute force mechanisms. This only affects users who have a working LDAP authorization configuration enabled on their GoCD server, and only is exploitable by users authenticating using such an LDAP configuration. This issue has been fixed in GoCD 22.1.0, which is bundled with gocd-ldap-authentication-plugin v2.2.0-144.
Severity: MEDIUM (CVSS v3 base score: 6.8)
Weakness: CWE-74 \u2014 Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')
The product constructs all or part of a command, data structure, or record using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify how it is parsed or interpreted when it is sent to a downstream component.
Repository: https://github.com/gocd/gocd-ldap-authentication-plugin
Files changed:
src/main/java/cd/go/apacheds/ApacheDsLdapClient.java
src/main/java/cd/go/authentication/ldap/LdapClient.java
src/main/java/cd/go/framework/ldap/JNDILdapClient.java
src/test/java/cd/go/apacheds/ApacheDsLdapClientTest.java
Commit message: Escape/encode values when building search filters.


Vulnerable code:
    public <T> List<T> search(final String filter, final Object[] filterArgs, final Mapper<T> mapper, final int maxResultCount) {
                        .setFilter(format(filter, filterArgs))
        final String filter = format(ldapConfiguration.getUserSearchFilter(), "test");
    public List<Entry> search(final String filter, final Object[] filterArgs, final int maxResultCount) {
    <T> List<T> search(String userSearchFilter, Object[] filterArgs, Mapper<T> mapper, int maxResult);
    public <T> List<T> search(String filter, Object[] filterArgs, Mapper<T> mapper, int maxResult) {
class LdapTest {

Fixed code:
import org.apache.directory.api.ldap.model.filter.FilterEncoder;
    public <T> List<T> search(final String filter, final String[] filterArgs, final Mapper<T> mapper, final int maxResultCount) {
                        .setFilter(FilterEncoder.format(filter, filterArgs))
        final String filter = FilterEncoder.format(ldapConfiguration.getUserSearchFilter(), "test");
    public List<Entry> search(final String filter, final String[] filterArgs, final int maxResultCount) {
    <T> List<T> search(String userSearchFilter, String[] filterArgs, Mapper<T> mapper, int maxResult);
    public <T> List<T> search(String filter, String[] filterArgs, Mapper<T> mapper, int maxResult) {
class ApacheDsLdapClientTest {
    @Test
    void shouldEscapeSearchFilterValues() throws ParseException {
        final LdapConfiguration ldapConfiguration = new LdapConfigurationBuilder()
                .withSearchTimeout(10)
                .withSearchBases("ou=foo,dc=bar")
                .build();

        final LdapConnectionTemplate ldapConnectionTemplate = mock(LdapConnectionTemplate.class);
        final ApacheDsLdapClient ldap = new ApacheDsLdapClient(ldapConfiguration, ldapConnectionTemplate);
        final ArgumentCaptor<SearchRequest> argumentCaptor = ArgumentCaptor.forClass(SearchRequestImpl.class);

        when(ldapConnectionTemplate.search(argumentCaptor.capture(), ArgumentMatchers.<EntryMapper<Entry>>any())).thenReturn(Collections.singletonList(new DefaultEntry()));

        String injectionUserName = "*)(objectclass=*";
        ldap.search("(uid={0})", new String[]{injectionUserName}, 1);

        assertThat(argumentCaptor.getValue().getFilter())
                .isEqualTo(FilterParser.parse("(uid=\\2A\\29\\28objectclass=\\2A)"));
    }