# REPRO-2026-00087: Apache Druid basic security LDAP authenticator can be bypassed when the LDAP server allows anonymous binds, permitting login with any existing username and an empty password. ## Summary Status: published Severity: critical Type: security Confidence: Unknown ## Identifiers REPRO ID: REPRO-2026-00087 GHSA: GHSA-Q672-HFC7-G833 CVE: CVE-2026-23906 ## Package Name: org.apache.druid.extensions:druid-basic-security Ecosystem: Maven Affected: 0.17.0 through 35.x (<36.0.0) Fixed: 36.0.0 ## Root Cause ## Summary Apache Druid’s druid-basic-security LDAP authenticator accepts a successful LDAP bind as proof of authentication without verifying that a password was actually supplied. If the backing LDAP server allows simple binds with an empty password (anonymous bind with DN), any existing username can be authenticated by providing an empty password. ## Impact - **Package/component affected:** org.apache.druid.extensions:druid-basic-security (LDAPCredentialsValidator) - **Affected versions:** >= 0.17.0, < 36.0.0 (verified on 35.0.0) - **Risk level:** High – attackers can authenticate as any existing LDAP user with an empty password when the LDAP server allows anonymous bind, leading to unauthorized access to Druid APIs and data. ## Root Cause LDAPCredentialsValidator#validateCredentials looks up the user DN using the configured bind user, then calls validatePassword() which performs a simple LDAP bind with the supplied password. The code treats any successful bind as valid credentials, but does not explicitly reject empty passwords. On LDAP servers configured to accept simple binds with an empty password (anonymous bind with DN), the bind succeeds and authentication is granted. The patched release (36.0.0) adds explicit checks to reject empty passwords before attempting LDAP bind. ## Reproduction Steps 1. Run `repro/reproduction_steps.sh`. 2. The script builds Druid 35.0.0, launches an in-memory LDAP server that accepts empty-password binds, then runs a Java PoC calling LDAPCredentialsValidator with username `alice` and an empty password. 3. Expected evidence: the script prints `AUTH_SUCCEEDED: alice` and `Vulnerability reproduced: LDAP anonymous bind accepted empty password`. ## Evidence - Log file: `logs/poc_output.txt` - Key excerpt: - `AUTH_SUCCEEDED: alice` - `Vulnerability reproduced: LDAP anonymous bind accepted empty password` - Environment: OpenJDK 17, Maven build of Druid 35.0.0 with UnboundID in-memory LDAP server. ## Recommendations / Next Steps - Add explicit validation to reject empty passwords before attempting LDAP bind (as done in 36.0.0). - Upgrade to Apache Druid 36.0.0 or later. - Add regression tests to ensure empty passwords are rejected even if LDAP accepts anonymous binds. ## Additional Notes - The reproduction script is idempotent and verified to pass twice consecutively. - The PoC uses a real LDAP server (UnboundID in-memory) configured to allow empty-password binds, matching the vulnerable behavior scenario. ## Reproduction Details Reproduced: 2026-02-13T15:27:38.483Z Duration: 2935 seconds Tool calls: 340 Turns: Unknown Handoffs: 2 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00087 pruva-verify GHSA-Q672-HFC7-G833 pruva-verify CVE-2026-23906 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00087&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00087/artifacts/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - GitHub Advisory: https://github.com/advisories/GHSA-Q672-HFC7-G833 - NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-23906 - Source: https://github.com/advisories/GHSA-Q672-HFC7-G833 ## Artifacts - repro/rca_report.md (analysis, 2491 bytes) - repro/reproduction_steps.sh (reproduction_script, 6708 bytes) - vuln_variant/rca_report.md (analysis, 4350 bytes) - vuln_variant/reproduction_steps.sh (reproduction_script, 6894 bytes) - bundle/ticket.md (ticket, 2161 bytes) - logs/ldap_server.log (log, 416 bytes) - logs/JndiTest.class (other, 2137 bytes) - logs/LDAPAnonBindPoC.java (other, 1571 bytes) - logs/classpath_raw.txt (other, 1014 bytes) - logs/ldif/60-user.ldif (other, 111 bytes) - logs/ldif/50-allow-anon.ldif (other, 98 bytes) - logs/LdapAnonTest.class (other, 754 bytes) - logs/result.txt (other, 70 bytes) - logs/poc_output.txt (other, 208 bytes) - logs/LDAPAnonBindPoC.class (other, 2702 bytes) - logs/classpath.txt (other, 0 bytes) - logs/LdapAnonTest.java (other, 309 bytes) - logs/maven_build.log (log, 52634 bytes) - logs/AnonLdapServer$1.class (other, 1545 bytes) - logs/alice.ldif (other, 111 bytes) - logs/tmp_cp.txt (other, 0 bytes) - logs/AnonLdapServer.java (other, 2412 bytes) - logs/AnonLdapServer.class (other, 2454 bytes) - logs/JndiTest.java (other, 1111 bytes) - vuln_variant/patch_analysis.md (documentation, 1529 bytes) - logs/variant_attempts.sh (other, 4018 bytes) - logs/variant_classpath_fixed.txt (other, 7252 bytes) - logs/LdapVariantPoC.java (other, 1119 bytes) - logs/memory.txt (other, 11 bytes) - logs/variant_classpath.txt (other, 17097 bytes) - logs/variant_attempts.log (log, 2940 bytes) - logs/LdapVariantPoC.class (other, 2384 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00087 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00087/artifacts/repro/reproduction_steps.sh - Web: https://pruva.dev/r/REPRO-2026-00087 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://pruva.dev