Active Directory Enumeration
Active Directory is the backbone of most enterprise networks, and enumerating its configuration, trusts and privileged access often reveals the most direct paths to full domain compromise. This section covers tools and techniques for identifying misconfigurations, excessive privileges and other weaknesses within an AD environment.
BloodHound
BloodHound is an Active Directory security tool that maps relationships between users, groups, computers, sessions, permissions and other objects within an Active Directory environment.
Rather than looking at individual configuration weaknesses in isolation, BloodHound allows us to look at Active Directory as a graph. Objects such as users and computers become nodes, while relationships such as group membership, administrative privileges and active sessions become edges connecting those nodes.
This becomes extremely useful during an internal penetration test because seemingly insignificant permissions can sometimes be chained together to create a path to a highly privileged account.
For example, a compromised user may not be a Domain Administrator, but they may control another account, which is a member of a group that has administrative access to a server where a Domain Administrator has an active session.
BloodHound helps identify these relationships and potential attack paths. You can find it here: https://github.com/SpecterOps/BloodHound
You can find a comprehensive BloodHound cheat sheet on this site covering collection methods and useful Cypher queries in more detail.
How BloodHound Works
BloodHound itself does not magically know what exists inside the target Active Directory environment. Data first needs to be collected from the domain and then imported into BloodHound.
Several collectors can be used depending on where we are operating from.
From Windows, SharpHound is commonly used.
From Linux, BloodHound.py can collect much of the same Active Directory information without requiring a Windows host.
For Azure / Entra ID environments, AzureHound can be used to collect cloud identity information.
SharpHound
SharpHound is the official Active Directory data collector used by BloodHound.
If using the PowerShell collector, first import SharpHound:
cd BloodHound-master\BloodHound-master\Ingestors\Import-Module .\SharpHound.ps1We can then perform a broad collection:
Invoke-BloodHound -CollectionMethod AllIf we only want to collect currently logged-on users:
Invoke-BloodHound -CollectionMethod LoggedOn -VerboseA specific domain can also be targeted:
Invoke-BloodHound -Domain example.local -CollectionMethod AllIf we know the Domain Controller:
Invoke-BloodHound -Domain example.local -DomainController 10.10.10.10 -CollectionMethod AllCredentials can also be explicitly supplied:
Invoke-BloodHound -CollectionMethods All `-Domain example.local `-LdapUsername 'username' `-LdapPassword 'password'Excluding Domain Controllers
Depending on the environment and the collection methods being used, we may want to exclude Domain Controllers:
Invoke-BloodHound -CollectionMethod All -ExcludeDCThis can reduce some potentially noisy collection activity against Domain Controllers and may be useful where monitoring products are present.
The appropriate collection method should always be selected according to what information is actually required rather than automatically performing the broadest possible collection.
Running SharpHound as Another Domain User
Sometimes we have valid domain credentials but our current Windows session is not running as that user.
runas with /netonly allows us to start a process where the supplied credentials are used for remote network authentication:
runas.exe /netonly /user:EXAMPLE\username "powershell.exe"Alternatively:
SharpHound can then be executed from the newly opened PowerShell session.
This is particularly useful when testing from a workstation that is not joined to the target domain.
Session Collection
Active sessions are particularly valuable in BloodHound because they can reveal where privileged users are currently logged in.
SharpHound can repeatedly collect session information:
.\SharpHound.exe --CollectionMethods Session --Loop --LoopDuration 00:45:00 --Domain example.localCredentials can also be supplied:
.\SharpHound.exe --CollectionMethods Session `--Loop `--LoopDuration 00:45:00 `--Domain example.local `--ldapusername 'username' `--ldappassword 'password'A shorter collection loop can also be performed:
.\SharpHound.exe -c All --Loop --LoopDuration 00:10:00 --Domain example.localOr:
.\SharpHound.exe --collectionmethods All `--Loop `--LoopDuration 00:05:00 `--domain example.local `--ldapusername 'username' `--ldappassword 'password'Looping is useful because sessions are temporary.
A privileged administrator may not be logged into an interesting server when the initial collection occurs, but may establish a session several minutes later.
Repeated session collection can therefore reveal attack paths that would not appear in a single snapshot.
BloodHound.py
If we are testing from Linux, BloodHound.py can collect Active Directory information without requiring SharpHound to execute on a Windows system.
A typical collection using NTLM authentication is:
bloodhound-python \-p 'password' \-c All \--zip \-d example.local \-dc dc01.example.local \-ns 10.10.10.10 \-gc dc01.example.local \--auth-method ntlmThe important values here are:
-u Username-p Password-c Collection methods--zip Create a ZIP ready for BloodHound-d Domain-dc Domain Controller-ns DNS server-gc Global CatalogAnother example:
bloodhound-python \-u username \-p password \-c All \--zip \-d example.local \-dc dc01.example.local \-ns 10.10.10.10 \-gc dc01.example.local \--auth-method ntlmKerberos authentication can also be used:
bloodhound-python \-u 'username' \-p 'password' \-d example.local \-c All \--zip \-dc dc01.example.local \-ns 10.10.10.10 \-gc dc01.example.local \--auth-method kerberosWhere we do not need to explicitly specify the Domain Controller or Global Catalog, a shorter command may be sufficient:
bloodhound-python \-u username \-p password \-c All \--zip \-d example.local \-ns 10.10.10.10 \--auth-method ntlmBloodHound.py is particularly useful during internal testing from Kali or another Linux attack host where we have valid domain credentials and network access to the required Active Directory services.
Importing the Data
Once SharpHound or BloodHound.py has completed, the resulting data can be imported into BloodHound.
Depending on the version being used, the exact interface differs, but the overall process remains the same.
After importing the data, BloodHound can begin displaying relationships and calculating attack paths.
Legacy BloodHound and Neo4j
Older versions of BloodHound use a locally installed Neo4j database.
Although newer BloodHound releases have changed the architecture, it can still be useful to maintain a legacy BloodHound environment when working with older tooling, custom queries or existing datasets.
Starting Neo4j from PowerShell
Navigate to the Neo4j installation:
cd D:\Pentest\CRTP\Tools\neo4j-community-4.1.1\binImport the Neo4j management module:
Import-Module .\neo4j-management.psd1Start Neo4j:
Invoke-Neo4j consoleRunning Neo4j as a Service
Alternatively, install Neo4j as a Windows service:
cd \neo4j-community-4.1.1-windows\neo4j-community-4.1.1\binneo4j.bat install-serviceneo4j.bat startThe Neo4j browser can then be accessed locally:
http://localhost:7474The historical default credentials are:
neo4j:neo4jOn first login, change the default password.
Once Neo4j is running, start BloodHound and upload the ZIP or JSON data generated by the collector.
Setting Up Legacy BloodHound
For legacy BloodHound:
- Download Neo4j Desktop for Windows or Linux.
- Download a legacy BloodHound release.
- Create a local Neo4j database.
- For the environment documented here, Neo4j
4.4.26was used. - Start the database and launch BloodHound.
- Import the collected SharpHound data.
Legacy BloodHound releases:
https://github.com/SpecterOps/BloodHound-Legacy/releasesWhen running the legacy BloodHound client on Kali, GPU-related issues can sometimes be avoided with:
./BloodHound --disable-gpu --use-gl=swiftshaderQuerying BloodHound
BloodHound provides built-in queries for common Active Directory attack paths, but we can also query the underlying graph directly.
Legacy BloodHound uses Neo4j and the Cypher query language.
This becomes useful when we want to answer very specific questions about the environment that are not covered by the default BloodHound queries.
Finding Domain Administrators
To retrieve members of the Domain Admins group:
RETURN u.nameThe results can then be exported from the Neo4j browser as CSV.
This provides a quick way of producing a list of highly privileged accounts for further investigation.
Enumerating Group Members
Return all users belonging to a specific group:
RETURN u.name AS UserNameThis is useful when an interesting security group has been identified and we want to determine which accounts inherit its privileges.
Find Users with Password Not Required
Search for enabled users where the passwordnotreqd property is set:
MATCH (n:User {enabled: True, passwordnotreqd: True})RETURN nThese accounts warrant further investigation as the PASSWD_NOTREQD account flag can indicate weak account configuration.
The presence of the flag does not by itself prove that the account currently has a blank password, so the result should be validated rather than automatically treated as an empty-password account.
Searching Object Descriptions
Active Directory descriptions are often overlooked during security assessments.
Administrators sometimes place operational information, account details or even credentials into user and computer descriptions.
Find Users with Descriptions
MATCH (c:User)WHERE c.description IS NOT NULLRETURN c.name,c.descriptionFind Computers with Descriptions
MATCH (c:Computer)WHERE c.description IS NOT NULLRETURN c.name,c.descriptionWe can also search user descriptions for specific keywords:
MATCH (u:User)WHERE u.description CONTAINS "pass"RETURN u.name, u.displayname, u.description, u.groupKeywords worth investigating may include:
passpasswordpwdadminservicetempVPNAny interesting values should be manually reviewed before determining whether sensitive information has actually been exposed.
Exporting Users and Email Addresses
Sometimes we simply want to use BloodHound’s collected data as a convenient source of Active Directory users.
Return all usernames:
MATCH (u:User)RETURN u.nameReturn email addresses:
MATCH (u:User)RETURN u.emailWhen using Neo4j Desktop Browser, the results can then be downloaded as CSV.
This can be useful for producing user lists for other authorised enumeration activities.
Finding Owned Users in a Group
BloodHound allows nodes to be marked as Owned when an account or system has been compromised during the assessment.
Once compromised users have been marked, we can query those relationships.
For example, find all owned users belonging to a particular VPN access group:
WHERE u.owned = trueRETURN u.name AS OwnedUser, u.objectid AS ObjectID, g.name AS GroupNameThis can quickly reveal whether a compromised account also has access to sensitive services or security groups.
Finding Systems a Group Administers
To identify computers where a particular group has local administrative privileges:
RETURN g.name AS GroupName, c.name AS ComputerNameThis becomes particularly interesting when broad groups such as Domain Users have administrative access to systems.
If a compromised account belongs to the group, those systems may represent immediate opportunities for lateral movement.
Custom BloodHound Queries
BloodHound can be extended with collections of custom Cypher queries.
Useful resources include:
https://github.com/hausec/Bloodhound-Custom-Queries
https://github.com/CompassSecurity/BloodHoundQueries/blob/master/BloodHound_Custom_Queries/customqueries.jsonFor older Windows BloodHound installations, custom queries can be downloaded into the user’s BloodHound configuration directory:
Invoke-WebRequest `-Uri "https://raw.githubusercontent.com/CompassSecurity/BloodHoundQueries/master/BloodHound_Custom_Queries/customqueries.json" `-OutFile "$env:USERPROFILE\AppData\Roaming\bloodhound\customqueries.json"On Linux:
curl -o ~/.config/bloodhound/customqueries.json \"https://raw.githubusercontent.com/CompassSecurity/BloodHoundQueries/master/BloodHound_Custom_Queries/customqueries.json"Custom queries can dramatically expand what can be investigated from an existing BloodHound dataset without requiring additional collection.
What Should We Look For?
Collecting BloodHound data is only the beginning.
During an Active Directory assessment we should use the resulting graph to investigate questions such as:
- Which accounts have paths to Domain Admin?
- Which computers have privileged users logged into them?
- Which users or groups have local administrator access?
- Are broad groups privileged over large numbers of computers?
- Which accounts can modify privileged groups?
- Which users can reset another user’s password?
- Are there exploitable delegation relationships?
- Are there dangerous ACL relationships?
- Are compromised accounts members of interesting groups?
- Can an owned principal reach a Tier 0 object?
- Are sensitive credentials present in object descriptions?
- Are there unexpected paths between different privilege tiers?
The objective is not simply to produce a colourful BloodHound graph. We want to determine whether the relationships represented by the graph can be turned into a practical attack path.
Using Owned Accounts
As accounts and systems are compromised during an assessment, mark them as owned in BloodHound.
This changes BloodHound from being purely an Active Directory visualisation tool into something that can help guide the next stage of the penetration test.
Rather than repeatedly asking:
“What can reach Domain Admin?”
we can ask the much more useful question:
“What can I currently control reach?”
This can dramatically reduce the amount of irrelevant information when working with a large Active Directory environment.
AzureHound
BloodHound can also be used to analyse relationships within Azure / Entra ID environments.
AzureHound performs the data collection role for Azure environments in much the same way that SharpHound does for Active Directory.
Preparing PowerShell
Install the Azure PowerShell modules:
Install-Module -Name Az -AllowClobberInstall-Module -Name AzureADPreview -AllowClobberAuthenticate:
Connect-AzureADConnect-AzAccountThe authentication window will prompt for the required credentials.
PowerShell AzureHound
Import AzureHound:
Import-Module .\AzureHound.ps1Collect the Azure data:
Invoke-AzureHoundThe resulting data can then be imported into a compatible BloodHound environment.
AzureHound Binary
AzureHound can also be run directly:
.\azurehound.exe list \-p "password" \-t "example.onmicrosoft.com" \-o example.jsonThe tenant name can be identified through other Entra ID enumeration techniques if it is not already known.
Querying Azure Data
Once AzureHound data has been imported, Cypher queries can be used to investigate Azure relationships.
For example:
MATCH p =(n)-[r:AZGlobalAdmin*1..]->(m)RETURN pThis can be used to investigate relationships involving the Global Administrator role within compatible BloodHound/AzureHound datasets.
Azure attack paths can be particularly valuable because privilege does not always come from direct role assignment. Group membership, application ownership, service principals and other relationships can potentially create indirect paths to privileged resources.
PingCastle
PingCastle is an Active Directory security assessment tool used to quickly identify common configuration weaknesses, privilege risks, outdated systems, trust relationships and other security issues within an AD environment. You can find it here: https://github.com/netwrix/pingcastle
You can find a comprehensive PingCastle cheat sheet on this site covering installation and additional commands in more detail.
Health Check
PingCastle generates an HTML report containing identified risks, affected objects and recommendations. Once I have credentials I always run this to get a great overview of the Active Directory health and what components to focus on for the penetration test.
.\PingCastle.exe --healthcheck # Run a health check against the current domain.\PingCastle.exe --healthcheck --server dc01.domain.local # Target a specific domain controller or domain.\PingCastle.exe --server domain.local --user username --password password --healthcheck # Specify credentials.\PingCastle.exe --healthcheck --server domain.local --level Full # Run a detailed, full assessment.\PingCastle.exe --healthcheck --server domain.local --privileged --level Full # Run with privileged checks for additional detail where suitable credentials are availableDomain and Forest Mapping
PingCastle can quickly map interconnected domains and trusts without performing a full health check, which is useful early in an engagement to understand the overall AD topology before testing in detail.
.\PingCastle.exe --carto # Map interconnected domains, trusts and forests.\PingCastle.exe --healthcheck --reachable # Assess all reachable domains.\PingCastle.exe --healthcheck --server * # Scan all reachable domains in the forest.\PingCastle.exe --healthcheck --server domain.local --explore-trust # Explore domain trusts.\PingCastle.exe --healthcheck --server domain.local --explore-forest-trust # Explore forest trustsTargeted Scanners
PingCastle also includes several scanners that can be run independently of the full health check. If I need to target a specific service like SMB I use the following.
.\PingCastle.exe --scanner <scanner> --server domain.local| Scanner | Description |
|---|---|
smb | Checks SMB versions and whether SMB signing is enforced (NTLM relay exposure) |
share | Enumerates network shares, including those accessible to broad groups |
localadmin | Enumerates local administrator group memberships |
laps_bitlocker | Checks whether LAPS and BitLocker are deployed |
computerversion | Identifies obsolete or unsupported operating systems |
antivirus | Identifies systems where an antivirus product can’t be detected |
spooler | Checks whether the Print Spooler service is remotely accessible |
nullsession / nullsession-trust | Checks for null-session access, including trust enumeration |
oxidbindings | Enumerates network interfaces via the DCOM OXID Resolver (no authentication required) |
smb3querynetwork | Enumerates network interfaces via SMB3 (authentication required) |
remote | Identifies remote access software installed on domain computers |
zerologon | Checks domain controllers for the ZeroLogon vulnerability |
smbhotfix | Checks for MS17-010/EternalBlue indicators |
kerberoschecksumhotfix | Checks for the MS14-068 Kerberos checksum vulnerability patch |
Entra ID Assessment
PingCastle can also assess Entra ID. Launch it interactively with .\PingCastle.exe, select entraid, then authenticate with useprt (using an available Primary Refresh Token) or askcredential.
Once complete, I use the following jq command to extract the MFA status from the generated JSON report.
jq -r '.Roles[].members[] | [.MFAStatus[], .EmailAddress, .DisplayName] | @csv' pingcastlecloud_domain.onmicrosoft.com.jsonConsolidating and Regenerating Reports
.\PingCastle.exe --hc-conso # Consolidate multiple health check reports, useful across multiple domains or forests.\PingCastle.exe --regen-report report.xml # Regenerate an HTML report from an existing XML report



