Skip to content

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.

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 DA

The 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.

  1. 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
  2. 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).
  3. RPC enumeration — MS-RPCE endpoints over 135/tcp and dynamic range. netlocalgroup, enum4linux, rpcclient extract local group membership (e.g., who is in Administrators on each box).
  4. DNS enumeration — _ldap._tcp.dc._msdcs.<domain> SRV records reveal all DCs. _kerberos._tcp, _kpasswd._tcp reveal KDC services. _gc._tcp reveals Global Catalogs.
  5. 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
  6. Trust enumeration — domains trust each other for auth. nltest /domain_trusts reveals all trusts. Cross-domain/cross-forest trusts extend attack surface; “external trusts” with SIDHistory filtering disabled are particularly dangerous.
TechniqueDescriptionDetection Difficulty
ldapsearch (raw)Direct LDAP queries from LinuxMedium — anonymous or low-priv bind
PowerView (PowerShell)Classic AD enumeration moduleMedium — EID 4104 with Get-Domain* patterns
SharpHound (C#)BloodHound collector, fastestMedium — high LDAP query rate
BloodHound.py (Python)Cross-platform collectorMedium — recognizable LDAP query pattern
ADRecon (PowerShell)Comprehensive HTML reportEasy — very high query volume
PingCastle (C#)AD security assessment toolMedium — well-known patterns
enum4linux / enum4linux-ngSMB/RPC enumerationEasy — old tool, high-noise traffic
rpcclient (manual)Raw RPC enumerationHard — looks like admin work
crackmapexec smb -MModules for users/groups/sharesMedium — recognizable UA
gpp-decrypt (legacy)Decrypt old GPP cpasswordsN/A — file-based
MS-RPRN PrintNightmare enumFind printable printers for CVE-2021-1675Hard — looks like admin
Terminal window
# === 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 relationships
ldapsearch -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 Admins
ldapsearch -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 users
Get-DomainUser -Properties samAccountName, memberOf, lastLogon
# Computers
Get-DomainComputer -Properties name, operatingSystem, lastLogonTimestamp
# Groups
Get-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 delegation
Get-DomainUser -TrustedToAuth
# Find all unconstrained delegation accounts (incl. computers)
Get-DomainUser -AllowDelegation -Unconstrained
Get-DomainComputer -Unconstrained
# === BLOODHOUND COLLECTION ===
# SharpHound (recommended, fast)
SharpHound.exe -c All -d corp.local --zipfilename corp.zip
# SharpHound via PowerShell
Invoke-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 enum4linux
enum4linux -u jsmith -p 'Pass@123' -a dc01.corp.local
# rpcclient manual enumeration
rpcclient -U 'jsmith%Pass@123' dc01.corp.local
> enumdomusers
> enumdomgroups
> querydominfo
> getusrdompwinfo 0x1f4 # RID of Administrator
# === CRACKMAPEXEC / NETEXEC ===
# Domain user enumeration
nxc smb 10.0.0.10 -u jsmith -p 'Pass@123' --users
# Group enumeration
nxc smb 10.0.0.10 -u jsmith -p 'Pass@123' --groups
# Shares
nxc smb 10.0.0.0/24 -u jsmith -p 'Pass@123' --shares
# Active sessions
nxc 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 ActiveDirectory
Get-ADUser -Filter * -Properties servicePrincipalName, memberOf, lastLogonDate
Get-ADComputer -Filter * -Properties operatingSystem, lastLogonDate
Get-ADGroup -Filter * -Properties member
Get-ADGroupMember -Identity "Domain Admins" -Recursive
# Trusts
Get-ADTrust -Filter *
Get-ADDomainController -Filter *

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.ps1 import, 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-Kerberoast are Sigma-flagged (Sigma win_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-NetSession pattern.
  • “Suspicious trust enumeration” — nltest /domain_trusts from 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).

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”.

EvasionWhat it bypassesDefense
Stagger collection methods over daysRate-based DfI heuristicLong-term trend analysis on per-user query volume
AD module (Get-AD*) instead of PowerViewPowerView-specific Sigma rulesBehavior baseline per admin user — anomaly detection
Custom LDAP queries (no named tool)Tool-signature rulesQuery content analysis (DLA — deep LDAP audit)
ADWS port 9389 over LDAP 389LDAP DfI sensorNetwork-level monitoring on all DC ports
LOLBins (wmic, net use)Tool-specific process-name rulesWMI/Net use behavioral analytics
Avoid NetSessionEnum RPCDfI session enumeration alertRe-baseline “session checks per hour”
Distributed collection across hostsSingle-source query volumePer-DC aggregation across all sources

⚠️ What caught me: I ran SharpHound’s -c All from 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.

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:

  1. Misconfigure the ACL: dsacls "CN=svc_sql,CN=Users,DC=corp,DC=local" /G corp\jsmith:GA (gives jsmith GenericAll on svc_sql).
  2. Add a Kerberoastable SPN to svc_sql: setspn -S MSSQLSvc/SRV01.corp.local:1433 corp\svc_sql.
  3. Set up a constrained-delegation path: svc_iis is allowed to delegate to HOST/SRV01.
  4. 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.exe process start, PowerView.ps1 import.
  • Performance counter — Netlogon queue length spike on DC during session collection.

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.

→ PE3 (Credential Dumping) · PE4 (Lateral Movement) · BT3 (Reverse Engineering)