Active Directory LDAP Enumeration
LDAP is the protocol that sits under almost everything in Active Directory. When you query users, groups, computers, GPOs , you're talking LDAP. Understanding how to query it directly gives you a lot of flexibility, especially in environments where your usual tools get flagged.
This covers four ways to query AD via LDAP: the AD PowerShell module, raw LDAP filters with Get-ADObject, ADSI searcher, and WMI , plus ldapsearch from Linux.
Quick Reference
| Command | Description |
|---|---|
Get-ADGroup -Identity "<GROUP NAME>" -Properties * |
Get full info on an AD group |
whoami /priv |
Check current user privileges |
Get-WindowsCapability -Name RSAT* -Online | Select-Object -Property Name, State |
Check if RSAT tools are installed |
Get-WindowsCapability -Name RSAT* -Online | Add-WindowsCapability -Online |
Install all RSAT tools |
runas /netonly /user:corp.local\username powershell |
Run a process as another user (useful for remote AD queries from a non-domain machine) |
Get-ADObject -LDAPFilter '(objectClass=group)' | select cn |
Return all AD groups via LDAP filter |
Get-ADUser -LDAPFilter '(userAccountControl:1.2.840.113556.1.4.803:=2)' | select name |
List disabled users |
(Get-ADUser -SearchBase "OU=Employees,DC=corp,DC=local" -Filter *).count |
Count users in a specific OU |
get-ciminstance win32_product | fl |
Query installed software |
Get-ADComputer -Filter "DNSHostName -like 'SQL*'" |
Find computers with SQL in the hostname |
Get-ADGroup -Filter "adminCount -eq 1" | select Name |
Get all protected/admin groups |
Get-ADUser -Filter {adminCount -eq '1' -and DoesNotRequirePreAuth -eq 'True'} |
Find admin users without Kerberos pre-auth (ASREPRoastable) |
Get-ADUser -Filter {adminCount -gt 0} -Properties admincount,useraccountcontrol |
Enumerate UAC values for admin users |
Get-WmiObject -Class win32_group -Filter "Domain='corp'" |
Get AD groups via WMI |
([adsisearcher]"(&(objectClass=Computer))").FindAll() |
Find all computers using ADSI |
LDAP Filter Syntax
LDAP filters follow a prefix notation wrapped in parentheses. Once you know the pattern, you can build almost any query.
(attribute=value) # basic equality
(&(filter1)(filter2)) # AND
(|(filter1)(filter2)) # OR
(!(filter)) # NOT
(attribute=*) # attribute exists
(attribute>=value) # greater than or equal
The bitwise filter 1.2.840.113556.1.4.803 is used constantly for userAccountControl queries. It means "bitwise AND" , you're checking if a specific flag bit is set in the UAC value.
Common UAC flag values:
| Flag | Value | Meaning |
|---|---|---|
| ACCOUNTDISABLE | 2 | Account is disabled |
| PASSWD_NOTREQD | 32 | No password required |
| DONT_EXPIRE_PASSWORD | 65536 | Password never expires |
| DONT_REQ_PREAUTH | 4194304 | No Kerberos pre-auth required |
So (userAccountControl:1.2.840.113556.1.4.803:=4194304) means "find all accounts where the DONT_REQ_PREAUTH bit is set" , that's your ASREPRoast target list.
AD PowerShell Module
The cleanest way to query AD on Windows. Needs RSAT installed or to be run on a DC.
# Check / install RSAT
Get-WindowsCapability -Name RSAT* -Online | Select-Object -Property Name, State
Get-WindowsCapability -Name RSAT* -Online | Add-WindowsCapability -Online
Import-Module ActiveDirectory
# Domain info
Get-ADDomain
Get-ADForest
# Users
Get-ADUser -Filter * -Properties * | Select-Object SamAccountName, Description, PasswordLastSet, LastLogonDate
# Find disabled accounts
Get-ADUser -LDAPFilter '(userAccountControl:1.2.840.113556.1.4.803:=2)' | select Name
# Find accounts with no pre-auth (ASREPRoastable)
Get-ADUser -Filter {DoesNotRequirePreAuth -eq $true} | select SamAccountName
# Find admin users without pre-auth , high value targets
Get-ADUser -Filter {adminCount -eq '1' -and DoesNotRequirePreAuth -eq 'True'}
# Enumerate UAC values for all admin users
Get-ADUser -Filter {adminCount -gt 0} -Properties admincount,useraccountcontrol
# Accounts with passwords set to never expire
Get-ADUser -Filter {PasswordNeverExpires -eq $true} | select SamAccountName
# Accounts with SPNs set (Kerberoastable)
Get-ADUser -Filter {ServicePrincipalName -ne "$null"} -Properties ServicePrincipalName |
select SamAccountName, ServicePrincipalName
# Count users in a specific OU
(Get-ADUser -SearchBase "OU=Employees,DC=corp,DC=local" -Filter *).count
# Groups
Get-ADGroup -Filter * | select Name
Get-ADGroup -Filter "adminCount -eq 1" | select Name
Get-ADGroup -Identity "Domain Admins" -Properties *
Get-ADGroupMember "Domain Admins" -Recursive | select SamAccountName
# Computers
Get-ADComputer -Filter * -Properties * | select Name, OperatingSystem, LastLogonDate
Get-ADComputer -Filter "DNSHostName -like 'SQL*'"
# Trusts
Get-ADTrust -Filter *
Raw LDAP Queries with Get-ADObject
When you need more control over what you're querying, Get-ADObject with -LDAPFilter lets you write raw LDAP filters directly.
# All groups
Get-ADObject -LDAPFilter '(objectClass=group)' | select cn
# All users with a description set (check these for passwords)
Get-ADObject -LDAPFilter '(&(objectClass=user)(description=*))' -Properties description |
select cn, description
# Computers running a specific OS
Get-ADObject -LDAPFilter '(&(objectClass=computer)(operatingSystem=*Server 2016*))' |
select cn
# Find accounts locked out
Get-ADObject -LDAPFilter '(lockoutTime>=1)' | select cn
# Find objects with adminCount set
Get-ADObject -LDAPFilter '(adminCount=1)' | select cn, objectClass
# Custom search base
Get-ADObject -LDAPFilter '(objectClass=user)' \
-SearchBase "OU=IT,DC=corp,DC=local" | select cn
ADSI Searcher
[adsisearcher] is a .NET class built into Windows , no imports, no modules needed. Useful when PowerShell remoting is restricted or the AD module isn't available.
# All computers
([adsisearcher]"(&(objectClass=Computer))").FindAll()
# All users
([adsisearcher]"(&(objectClass=user))").FindAll()
# Specific user by samAccountName
([adsisearcher]"(&(objectClass=user)(samAccountName=targetuser))").FindOne()
# Users with SPNs
([adsisearcher]"(&(objectClass=user)(servicePrincipalName=*))").FindAll() |
foreach { $_.Properties['samaccountname'] }
# Disabled accounts
([adsisearcher]"(&(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=2))").FindAll() |
foreach { $_.Properties['samaccountname'] }
# All groups
([adsisearcher]"(&(objectClass=group))").FindAll() |
foreach { $_.Properties['cn'] }
# Search a specific OU
$searcher = [adsisearcher]""
$searcher.SearchRoot = [ADSI]"LDAP://OU=IT,DC=corp,DC=local"
$searcher.Filter = "(objectClass=user)"
$searcher.FindAll()
WMI
WMI can query AD objects through the root\directory\LDAP namespace. Slower than the AD module but built into every Windows machine.
# Get AD groups via WMI
Get-WmiObject -Class win32_group -Filter "Domain='corp'"
# Get group members
Get-WmiObject -Class win32_groupuser | Where-Object { $_.GroupComponent -match "Domain Admins" }
# Installed software , useful for identifying vulnerable packages
get-ciminstance win32_product | fl
# Running processes
Get-WmiObject win32_process | Select-Object Name, ProcessId, CommandLine
# All WMI AD classes
Get-WmiObject -Namespace "root\directory\LDAP" -List | Where-Object { $_.Name -like "*user*" }
ldapsearch from Linux
When you're on Linux with valid creds, ldapsearch gives you direct LDAP access without needing a Windows host.
# Authenticate and dump all users
ldapsearch -H ldap://<DC_IP> -D "corp\username" -w 'Password' \
-b "DC=corp,DC=local" "(objectClass=user)" sAMAccountName
# Disabled accounts
ldapsearch -H ldap://<DC_IP> -D "corp\username" -w 'Password' \
-b "DC=corp,DC=local" "(userAccountControl:1.2.840.113556.1.4.803:=2)" sAMAccountName
# ASREPRoastable accounts
ldapsearch -H ldap://<DC_IP> -D "corp\username" -w 'Password' \
-b "DC=corp,DC=local" "(userAccountControl:1.2.840.113556.1.4.803:=4194304)" sAMAccountName
# Kerberoastable accounts
ldapsearch -H ldap://<DC_IP> -D "corp\username" -w 'Password' \
-b "DC=corp,DC=local" "(&(objectClass=user)(servicePrincipalName=*))" \
sAMAccountName servicePrincipalName
# All groups
ldapsearch -H ldap://<DC_IP> -D "corp\username" -w 'Password' \
-b "DC=corp,DC=local" "(objectClass=group)" cn
# Anonymous bind check (no creds , sometimes works on misconfigured DCs)
ldapsearch -H ldap://<DC_IP> -x -b "DC=corp,DC=local" "(objectClass=user)" sAMAccountName
# Using LDAPS (port 636)
ldapsearch -H ldaps://<DC_IP>:636 -D "corp\username" -w 'Password' \
-b "DC=corp,DC=local" "(objectClass=user)"
Running as Another User
When you're on a non-domain machine but have domain credentials, runas /netonly lets you spawn a process that authenticates to the domain remotely while still running locally as your current user. Useful for running AD tools from a workstation that isn't joined to the target domain.
runas /netonly /user:corp.local\username powershell
# Enter the password when prompted
# The new PowerShell window will use those creds for any network/domain requests
What to Look For
A few queries worth running on every engagement:
# Accounts that are admin AND don't require Kerberos pre-auth , easy wins
Get-ADUser -Filter {adminCount -eq '1' -and DoesNotRequirePreAuth -eq 'True'}
# Accounts with descriptions , admins sometimes store passwords here
Get-ADUser -Filter * -Properties Description | Where-Object {$_.Description -ne $null} |
select SamAccountName, Description
# Service accounts with SPNs , Kerberoasting targets
Get-ADUser -Filter {ServicePrincipalName -ne "$null"} -Properties ServicePrincipalName |
select SamAccountName, ServicePrincipalName
# Computers you haven't seen before , find SQL, web, or dev servers
Get-ADComputer -Filter * -Properties * |
select Name, OperatingSystem, IPv4Address | Sort-Object OperatingSystem