Anyone working in offensive security has heard of BloodHound. It uses graph theory to model Active Directory relationships and it remains one of my go-to tools on internal engagements.

I’m not claiming to be a BloodHound expert, but these are a few techniques that have repeatedly helped me in real assessments.

Loop collection method

Group memberships, GPOs and many other directory objects don’t change constantly during the working day. User sessions do. That makes session collection particularly valuable when you’re trying to understand where privileged users have logged on.

SharpHound can keep collecting session information over a period of time:

--CollectionMethod Session --Loop --Loopduration 02:00:00

You can then import the resulting data into BloodHound. A Neo4j query can also help estimate how much session coverage you have gathered:

MATCH (u1:User)
WITH COUNT(u1) AS totalusers
MATCH (c:Computer)-[r:HasSession]->(u2:User)
RETURN 100 * COUNT(DISTINCT(u2)) / totalusers AS userPercentage

Password Spray

Once you have valid credentials and permission to enumerate the directory, SharpHound data gives you a useful picture of the user population. On older BloodHound data sets I sometimes exported user names and prepared them for controlled password spraying.

cat users.json | jq -r '.data[].Properties | select(.enabled == true) | .name'

You can also review account descriptions alongside usernames:

cat users.json | jq -r '.data[].Properties | select(.enabled == true) | "\(.name) \(.description)"'

Descriptions are worth reviewing because they sometimes contain useful operational information and, in poorly managed environments, can occasionally expose credentials or password hints.

For simple username extraction, I also previously used:

cat users.json | cut -d ':' -f 2 | sed 's|[",\t]||g' | sort -u | tee users

The jq approach is preferable because it understands the JSON structure and lets you filter specifically for enabled users rather than relying on text parsing.

Spraying must of course follow the engagement’s lockout and safety rules.

AD CS & Custom Queries

Active Directory Certificate Services attack paths are another area where graphing becomes very powerful. Certipy can enumerate AD CS and produce BloodHound-compatible data, allowing certificate-service relationships to be analysed alongside the rest of the domain.

Custom Cypher queries are where BloodHound becomes even more useful. For example, the default query for old operating systems can include disabled computer accounts, so adding an enabled check can make the output more useful:

MATCH (H:Computer)
WHERE H.operatingsystem =~ '(?i).*(2000|2003|2008|xp|vista|7|me).*'
AND H.enabled = true
RETURN H

On one engagement there were 35 Domain Admins. Rather than right-clicking every user and marking each as high value, a query can do the work in one go:

MATCH p=(n:Group)<-[:MemberOf*1..]-(m)
WHERE n.objectid =~ "(?i)S-1-5-.*-512"
SET m.highvalue = true
RETURN m

The key point is to adapt queries to the engagement. Over time you build a small library of queries that answer the questions you repeatedly ask during testing.

Non Domain Joined

Your first foothold on an internal test is not always a domain-joined workstation. SharpHound can still be run from a non-domain-joined Windows system when you have valid domain credentials and the necessary network access. One approach is to launch a process using network-only credentials:

runas /netonly /user:DOMAIN\user.name powershell.exe

For PowerShell tooling you can similarly create a credential object:

$cred = Get-Credential

and supply it to commands that support -Credential.

Final thoughts

None of these techniques are revolutionary, and the BloodHound documentation is essential reading. The value comes from treating BloodHound as a flexible graph-analysis platform rather than just clicking the built-in queries. Small changes to collection and custom queries can save a surprising amount of time during an internal assessment.