Active Directory Enumeration
Active Directory is a hierarchical database of every principal, computer, group, share, GPO, and ACL relationship in a Windows domain. As an authenticated domain user — even the lowest-privileged account — you can read almost all of it. The ACL on the domain head (DC=corp,DC=local) defaults to allowing “Authenticated Users” read on most attributes. This means a single valid domain credential lets you enumerate every path to Domain Admin: every group membership, every ACL granting a user the right to write to another user’s servicePrincipalName (Kerberoast setup), every unconstrained-delegation trust, every GPO that applies to a Domain Controller. BloodHound, introduced by SpecterOps in 2016, turned this enumeration into a graph problem that finds the shortest attack path automatically — and there is almost always a path.
The Core Concept
Section titled “The Core Concept”AD is structured as a tree of objects (users, computers, groups, GPOs, OUs) with relationships (MemberOf, ManagedBy, GPLink, WriteDacl, GenericAll). The attacker’s job is to find a chain of relationships from “current user” to “Domain Admins”.
┌──────────────────────────────┐ │ DC=corp,DC=local │ │ (Domain Head) │ └──────┬───────────────────────┘ │ ┌─────────────────┼────────────────────┐ │ │ │ CN=Users CN=Computers OU=Servers │ │ │ ┌───┴───┐ ┌───┴────┐ ┌─────┴─────┐ │ jsmith│ │ SRV01 │ │ DC01 │ │svc_sql│ │ SRV02 │ │ (DA) │ │admin │ │ Win11 │ │ │ └───┬───┘ └────────┘ └───────────┘ │ GenericAll on svc_sql → Kerberoast → crack → overpass │ │ MemberOf → Group "Help Desk" → GenericAll on "Server Operators" │ → MemberOf DAThe attacker asks the graph: “from my current node, what edges get me to DA?” BloodHound computes this with Neo4j’s shortest-path query. The answer is usually one of: (a) a user with GenericAll on another user → reset their password → log on as them → repeat; (b) a user with GenericWrite on a service account → add SPN → Kerberoast; (c) a user with AdminTier-0 group inheritance → direct DA; (d) a constrained-delegation chain; (e) an ACL on a GPO that applies to a DC.
How It Works
Section titled “How It Works”- LDAP enumeration — Active Directory exposes its database over LDAP (389/tcp) and LDAP/SSL (636/tcp). The default query base is
DC=<domain>,DC=<tld>. Any authenticated user can bind and query. Most useful filters:(&(objectClass=user)(objectCategory=person))— all user objects(objectClass=computer)— all computer objects(objectClass=group)— all groups(objectCategory=groupPolicyContainer)— all GPOs(userAccountControl:1.2.840.113556.1.4.803:=524288)— accounts trusted for unconstrained delegation (UAC bit 0x80000)(userAccountControl:1.2.840.113556.1.4.803:=4194304)— DONT_REQ_PREAUTH (AS-REP roastable)(servicePrincipalName=*)— Kerberoastable accounts
- SMB enumeration — NetBIOS/SMB (445/tcp) reveals shares, sessions, logged-on users.
net view \\srv01,net session,Get-NetSession. Crucial for finding which users are logged on where (which means: where can you find their LSASS to dump). - RPC enumeration — MS-RPCE endpoints over 135/tcp and dynamic range.
netlocalgroup,enum4linux,rpcclientextract local group membership (e.g., who is in Administrators on each box). - DNS enumeration —
_ldap._tcp.dc._msdcs.<domain>SRV records reveal all DCs._kerberos._tcp,_kpasswd._tcpreveal KDC services._gc._tcpreveals Global Catalogs. - Graph analysis (BloodHound) — SharpHound collector enumerates all of the above plus ACLs, GPOs, sessions, and writes to JSON. The BloodHound UI ingests this and renders the graph; Cypher queries find attack paths:
MATCH (n:User), (m:Group) WHERE m.name = "DOMAIN ADMINS@CORP.LOCAL"MATCH p = shortestPath((n)-[*1..10]->(m))RETURN p
- Trust enumeration — domains trust each other for auth.
nltest /domain_trustsreveals all trusts. Cross-domain/cross-forest trusts extend attack surface; “external trusts” with SIDHistory filtering disabled are particularly dangerous.
Common Techniques
Section titled “Common Techniques”| Technique | Description | Detection Difficulty |
|---|---|---|
ldapsearch (raw) | Direct LDAP queries from Linux | Medium — anonymous or low-priv bind |
PowerView (PowerShell) | Classic AD enumeration module | Medium — EID 4104 with Get-Domain* patterns |
SharpHound (C#) | BloodHound collector, fastest | Medium — high LDAP query rate |
BloodHound.py (Python) | Cross-platform collector | Medium — recognizable LDAP query pattern |
ADRecon (PowerShell) | Comprehensive HTML report | Easy — very high query volume |
PingCastle (C#) | AD security assessment tool | Medium — well-known patterns |
enum4linux / enum4linux-ng | SMB/RPC enumeration | Easy — old tool, high-noise traffic |
rpcclient (manual) | Raw RPC enumeration | Hard — looks like admin work |
crackmapexec smb -M | Modules for users/groups/shares | Medium — recognizable UA |
gpp-decrypt (legacy) | Decrypt old GPP cpasswords | N/A — file-based |
MS-RPRN PrintNightmare enum | Find printable printers for CVE-2021-1675 | Hard — looks like admin |
Hands-On Commands
Section titled “Hands-On Commands”# === RAW LDAP ENUMERATION ===# All users with SPN (Kerberoast targets)ldapsearch -x -H ldap://dc01.corp.local -D 'jsmith@corp.local' -w 'Pass@123' \ -b 'DC=corp,DC=local' \ '(&(objectClass=user)(servicePrincipalName=*))' \ sAMAccountName servicePrincipalName memberOf
# All users with DONT_REQ_PREAUTH (AS-REP roast targets)ldapsearch -x -H ldap://dc01.corp.local -D 'jsmith@corp.local' -w 'Pass@123' \ -b 'DC=corp,DC=local' \ '(userAccountControl:1.2.840.113556.1.4.803:=4194304)' \ sAMAccountName
# Unconstrained delegation accounts (危险!)ldapsearch -x -H ldap://dc01.corp.local -D 'jsmith@corp.local' -w 'Pass@123' \ -b 'DC=corp,DC=local' \ '(userAccountControl:1.2.840.113556.1.4.803:=524288)' \ sAMAccountName
# Domain trust relationshipsldapsearch -x -H ldap://dc01.corp.local -D 'jsmith@corp.local' -w 'Pass@123' \ -b 'CN=System,DC=corp,DC=local' '(objectClass=trustedDomain)' \ cn trustDirection trustPartner
# All Domain Adminsldapsearch -x -H ldap://dc01.corp.local -D 'jsmith@corp.local' -w 'Pass@123' \ -b 'CN=Domain Admins,CN=Users,DC=corp,DC=local' '(objectClass=*)' member
# === POWERSHELL: PowerView ===Import-Module .\PowerView.ps1# Domain usersGet-DomainUser -Properties samAccountName, memberOf, lastLogon# ComputersGet-DomainComputer -Properties name, operatingSystem, lastLogonTimestamp# GroupsGet-DomainGroup -MemberIdentity "Domain Admins"# Who is logged on where?Get-NetSession -ComputerName SRV01# Find users with GenericAll on other users (password-reset path)Get-DomainUser | Get-DomainObjectAcl -ResolveGUIDs | ? { $_.ActiveDirectoryRights -match "GenericAll" -and $_.ObjectType -eq "User" -and $_.SecurityIdentifier -ne "S-1-5-18"}# Find users with constrained delegationGet-DomainUser -TrustedToAuth# Find all unconstrained delegation accounts (incl. computers)Get-DomainUser -AllowDelegation -UnconstrainedGet-DomainComputer -Unconstrained
# === BLOODHOUND COLLECTION ===# SharpHound (recommended, fast)SharpHound.exe -c All -d corp.local --zipfilename corp.zip# SharpHound via PowerShellInvoke-BloodHound -CollectionMethod All -OutputDirectory C:\windows\temp\# BloodHound.py (Linux, Python — cross-platform)python3 bloodhound.py -d corp.local -u jsmith -p 'Pass@123' -ns 10.0.0.10 -c All# Collection methods (pick subsets for stealth):# -c Default : users, groups, computers, ACLs, sessions# -c Session : logged-on sessions (most noisy — uses RPC)# -c ACL : just ACLs# -c ComputerOnly : per-computer local groups# -c GPOLocalGroup: GPO -> local group mapping# -c RDP : RDP-permissions on computers (very noisy)
# After collection, ingest into BloodHound UI:# 1. Run Neo4j (docker-compose up)# 2. Open BloodHound UI, drag-and-drop the JSON files# 3. Pre-built queries: "Find All Domain Admins", "Shortest Path to Domain Admins"# Custom Cypher:# MATCH (u:User {name:'JSMITH@CORP.LOCAL'}), (g:Group {name:'DOMAIN ADMINS@CORP.LOCAL'}),# p = shortestPath((u)-[:MemberOf|HasSession|AdminTo|GenericAll|WriteDacl*1..10]->(g))# RETURN p
# === ENUM4LINUX / RPCENUM ===enum4linux-ng -Aa dc01.corp.local -u jsmith -p 'Pass@123'# Or classic enum4linuxenum4linux -u jsmith -p 'Pass@123' -a dc01.corp.local# rpcclient manual enumerationrpcclient -U 'jsmith%Pass@123' dc01.corp.local > enumdomusers > enumdomgroups > querydominfo > getusrdompwinfo 0x1f4 # RID of Administrator
# === CRACKMAPEXEC / NETEXEC ===# Domain user enumerationnxc smb 10.0.0.10 -u jsmith -p 'Pass@123' --users# Group enumerationnxc smb 10.0.0.10 -u jsmith -p 'Pass@123' --groups# Sharesnxc smb 10.0.0.0/24 -u jsmith -p 'Pass@123' --shares# Active sessionsnxc smb 10.0.0.10 -u jsmith -p 'Pass@123' --sessions# Logged-on users (high signal!)nxc smb 10.0.0.10 -u jsmith -p 'Pass@123' --loggedon-users
# === AD MODULE (Microsoft official, may be stealthier) ===Import-Module ActiveDirectoryGet-ADUser -Filter * -Properties servicePrincipalName, memberOf, lastLogonDateGet-ADComputer -Filter * -Properties operatingSystem, lastLogonDateGet-ADGroup -Filter * -Properties memberGet-ADGroupMember -Identity "Domain Admins" -Recursive# TrustsGet-ADTrust -Filter *Get-ADDomainController -Filter *Detection — Defender View
Section titled “Detection — Defender View”Windows Security Log:
- EID 4662 — An operation was performed on an object. The most important AD enumeration signal. SACL on the domain head and key containers must be configured for “Read All Properties” success auditing. With SACL in place, every LDAP query against the user container generates 4662 events with
Properties: {1131f6aa-9c07-11d1-f79f-00c04fc2dcd2}(Read All Properties) and the querying user. - EID 5136 — Directory service object was modified. Less relevant for read-only enumeration.
- EID 5145 — Network share object was checked (SMB share enumeration).
Sysmon:
- EID 1 — ProcessCreate: process names —
SharpHound.exe,BloodHound.exe,PowerView.ps1import,ldapsearch,enum4linux— are all flaggable. - EID 22 — DNSEvent: queries for
*_ldap._tcp.dc._msdcs*SRV records reveal domain-controller discovery. - EID 3 — NetworkConnect: process connecting to port 389/636 (LDAP) and 445 (SMB) on the DC.
PowerShell Script Block Logging:
- EID 4104 — Script block logged. PowerView commands like
Get-DomainUser,Get-DomainGroup,Get-NetSession,Invoke-BloodHound,Invoke-Kerberoastare Sigma-flagged (Sigmawin_powershell_bloodhound,win_powershell_powerview). - EID 4103 — Module logging for parsed pipeline commands.
Network detection (Suricata):
- SharpHound’s LDAP query pattern is recognizable by the volume and order of LDAP operations (searchRequest → bind → searchRequest for user, group, computer objects in rapid succession).
- Suricata SID for BloodHound / SharpHound network pattern —
[verify]no public SID; usually custom rule. - ET OPEN SID 2221019-class covers SMB/RPC enumeration patterns [verify].
MITRE ATT&CK:
- T1087.002 — Account Discovery: Domain Account
- T1018 — Remote System Discovery
- T1069.002 — Groups Discovery: Domain Groups
- T1087 — Account Discovery
- T1482 — Domain Trust Discovery
- T1087.001 — Account Discovery: Local Account
- T1069 — Groups Discovery
Defender for Identity:
- “Network reconnaissance (LDAP)” — flags users querying AD via LDAP from unusual hosts.
- “Reconnaissance using SMB session enumeration” —
Get-NetSessionpattern. - “Suspicious trust enumeration” —
nltest /domain_trustsfrom a non-DC.
Wazuh rule 87106 [verify] — LDAP enumeration pattern (high-volume LDAP queries from a single user). Wazuh rule 87107 [verify] — BloodHound / SharpHound pattern (process name + LDAP traffic).
Evasion Techniques
Section titled “Evasion Techniques”Distribute queries across time. SharpHound’s “All” collection makes thousands of LDAP queries in minutes. A defender with SACL auditing sees the burst. Use collection methods separately (-c ACL first, days later -c Default, days later -c Session) — each individual method looks like admin activity, the combined picture is what BloodHound gives.
Use AD module not PowerView. Microsoft’s ActiveDirectory PowerShell module is installed on every DC and many admin workstations. Get-ADUser commands are normal admin activity; Get-DomainUser (PowerView) is flagged by every modern Sigma rule. Same data, very different detection profile.
Custom LDAP queries. SharpHound’s collector has a recognizable LDAP query sequence (queries in a specific order with specific filter strings). A custom Python script using ldap3 library with hand-written queries blends with legitimate admin tools. Cost: more development effort. Benefit: no signature.
Use ADWS over LDAP. Microsoft’s AD Web Service (9389/tcp) is the SOAP/XML endpoint used by the ActiveDirectory PowerShell module. It looks like admin activity; the queries are server-side, so the LDAP traffic seen at the wire level is from the DC to itself. Detect by network connection to port 9389 from non-admin hosts.
Stagger across hosts. SharpHound from a single workstation for an entire domain is anomalous. Spread across multiple hosts (if you have multiple footholds) and combine the JSON output offline.
Avoid session enumeration. Get-NetSession uses RPC NetSessionEnum which is one of the loudest enumeration calls — every DC logs it, and DfI specifically alerts on it. Sessions are critical data (they tell you where the DA’s LSASS is) but they are the noisiest. Get sessions selectively: target one DC at a time, late at night, with a single query each.
Use built-in tools instead of named tools. Replace SharpHound.exe with Get-ADUser -Filter * | Export-Csv. Same data, different telemetry. Replace Get-NetSession with wmic /node:DC01 path win32_loggedonuser. Each LOLBin substitution changes one Sigma rule from “match” to “no match”.
| Evasion | What it bypasses | Defense |
|---|---|---|
| Stagger collection methods over days | Rate-based DfI heuristic | Long-term trend analysis on per-user query volume |
| AD module (Get-AD*) instead of PowerView | PowerView-specific Sigma rules | Behavior baseline per admin user — anomaly detection |
| Custom LDAP queries (no named tool) | Tool-signature rules | Query content analysis (DLA — deep LDAP audit) |
| ADWS port 9389 over LDAP 389 | LDAP DfI sensor | Network-level monitoring on all DC ports |
| LOLBins (wmic, net use) | Tool-specific process-name rules | WMI/Net use behavioral analytics |
| Avoid NetSessionEnum RPC | DfI session enumeration alert | Re-baseline “session checks per hour” |
| Distributed collection across hosts | Single-source query volume | Per-DC aggregation across all sources |
⚠️ What caught me: I ran SharpHound’s
-c Allfrom a workstation and was caught by an unexpected source: the DC’s NTDS performance counter. SharpHound’s session collection spawns thousands of RPC calls per second against the DC’s Netlogon service. The DC’s admin noticed the Netlogon queue length had spiked to 30k+ pending requests and investigated. The log evidence was SharpHound’s process name on the workstation, but the trigger was a performance metric — not a security alert at all. Lesson: the side effects of enumeration (DC load, network throughput, response time) are themselves a detection channel. Defenders monitor performance, not just security events.
Lab Practice
Section titled “Lab Practice”VMs: DC01 (Server 2022, single-domain forest), SRV01 (member server), Win11 (domain-joined client). One domain admin (corpadmin), two service accounts (svc_sql, svc_iis), one low-priv user (jsmith) with accidental GenericAll on svc_sql (your attack path).
Setup:
- Misconfigure the ACL:
dsacls "CN=svc_sql,CN=Users,DC=corp,DC=local" /G corp\jsmith:GA(gives jsmith GenericAll on svc_sql). - Add a Kerberoastable SPN to svc_sql:
setspn -S MSSQLSvc/SRV01.corp.local:1433 corp\svc_sql. - Set up a constrained-delegation path:
svc_iisis allowed to delegate toHOST/SRV01. - Configure SACL on the domain head: enable “Read All Properties” success auditing for “Everyone” (this is the defensive configuration).
Objective:
- (a) Enumerate all users, groups, computers via raw LDAP (ldapsearch).
- (b) Run SharpHound, ingest into BloodHound, find the shortest path from jsmith to DA.
- (c) Demonstrate the OPSEC-safe collection: collect ACLs only, then a week later collect sessions, combine offline.
- (d) Find the AS-REP roastable and Kerberoastable accounts via LDAP filter.
Expected flag format: flag{ad_enum_acl_kerb_asrep_delegation_paths_mapped}.
Detection that should fire:
- Windows Security EID 4662 — high volume of LDAP reads from jsmith in short window.
- Defender for Identity — “Network reconnaissance using LDAP” if installed.
- Sysmon EID 1 —
SharpHound.exeprocess start,PowerView.ps1import. - Performance counter — Netlogon queue length spike on DC during session collection.
Interview Questions
Section titled “Interview Questions”Technical: walk through the exact LDAP filter to find all accounts trusted for unconstrained delegation. What UAC bit is set, and what is the LDAP extensible match OID for bitwise AND on a numeric attribute?
Scenario: you have a single low-priv domain user credential. Walk through your enumeration strategy from first LDAP query to identifying a path to Domain Admin. What order do you query in, and why?
Conceptual: explain why “Authenticated Users” can read almost all of AD by default. What is the historical operational reason, and what is the security implication for defenders?
Technical: BloodHound’s shortest-path query uses Neo4j. Walk through what edges (relationships) BloodHound supports and what each represents in AD terms (e.g., GenericAll, MemberOf, HasSession, AdminTo, WriteDacl).
Scenario: an organization has deployed Defender for Identity on all DCs. Your SharpHound collection was caught. Walk through your re-collection strategy using only Microsoft-builtin tools to achieve the same result.
Related Modules
Section titled “Related Modules”→ PE3 (Credential Dumping) · PE4 (Lateral Movement) · BT3 (Reverse Engineering)