BloodHound is one of the most powerful tools in an attacker's arsenal — and one of the most underused tools in a defender's. Most security teams know it exists. They've seen it in reports. A few have run it once in a lab. Almost none are running it regularly as part of their defensive posture.

That's a mistake I see the consequences of in almost every IR engagement I work. The attacker ran BloodHound. The defender hadn't.

This post is about changing that — how to run BloodHound as a defender, what queries actually matter, and how to translate the graph data into things your team can prioritize and fix.

What BloodHound Is Actually Doing

BloodHound collects information about Active Directory objects (users, computers, groups, GPOs, OUs, domains) and the relationships between them, then models those relationships as a graph. The edges between nodes represent real AD permissions and trust relationships: group membership, local admin rights, session data, ACL permissions, delegation settings.

The attack path analysis runs shortest-path algorithms across that graph to find the shortest route from a starting node to a target (typically Domain Admin or a Domain Controller). What makes BloodHound powerful is that it surfaces non-obvious paths — the five-hop chain of permissions that no human would manually trace but that an attacker scripted in five minutes.

BloodHound CE (Community Edition) is the current open-source version. For most defensive use cases, it's free and fully capable of everything in this post.

Collecting Data: SharpHound and AzureHound

BloodHound needs data to analyze. Two primary collectors:

Before you run: SharpHound generates network traffic and AD queries that can look like enumeration to your SIEM or EDR. Run it during business hours, from an account that's logged in a change record, and make sure your SOC knows it's happening.

// SharpHound — full collection SharpHound.exe -c All --domain corp.local --outputdirectory C:\temp\bh-output\ // SharpHound — faster, less noisy SharpHound.exe -c DCOnly,Group,ObjectProps --domain corp.local // AzureHound — Entra ID collection .\AzureHound.exe list --tenant "tenant.onmicrosoft.com"

Frequency matters. Permissions change, new accounts get created, group memberships shift. Run SharpHound at least monthly in most environments, weekly in high-risk ones. Automate the collection and import — stale data gives you false confidence.

The Queries That Actually Matter

BloodHound's built-in queries are a reasonable starting point, but the real power is Cypher — the graph query language that lets you ask precise questions about your environment. Here are the queries I run first in every environment.

1. Shortest Paths to Domain Admin

// All shortest paths to DA — the baseline question MATCH p=shortestPath((u:User)-[*1..5]->(g:Group {name:"DOMAIN ADMINS@CORP.LOCAL"})) WHERE NOT u.name STARTS WITH "DOMAIN ADMINS" AND u.enabled = true RETURN p // Count unique starting nodes — how many accounts have a path? MATCH (u:User)-[*1..5]->(g:Group {name:"DOMAIN ADMINS@CORP.LOCAL"}) WHERE u.enabled = true RETURN count(DISTINCT u) AS accounts_with_path_to_DA

The count query gives you a number that's easy to track over time. Track it monthly — watch it go down as you remediate.

2. Kerberoastable Accounts

// Enabled users with SPNs (Kerberoastable) MATCH (u:User) WHERE u.hasspn = true AND u.enabled = true RETURN u.name, u.serviceprincipalnames, u.pwdlastset, u.admincount ORDER BY u.admincount DESC, u.pwdlastset ASC // Kerberoastable accounts WITH a path to DA (high priority) MATCH p=shortestPath((u:User {hasspn:true, enabled:true})-[*1..5]-> (g:Group {name:"DOMAIN ADMINS@CORP.LOCAL"})) RETURN u.name, u.serviceprincipalnames, length(p) AS hop_count ORDER BY hop_count ASC

The second query is your priority list — Kerberoastable accounts that also have a path to DA. Sort by hop count; fix the shortest paths first.

3. AS-REP Roastable Accounts

// Accounts with "Do not require Kerberos pre-authentication" enabled MATCH (u:User) WHERE u.dontreqpreauth = true AND u.enabled = true RETURN u.name, u.pwdlastset, u.admincount

There are very few legitimate reasons for this setting. Every account that shows up here is almost certainly a misconfiguration. Fix is simple — enable pre-authentication. Prioritize any account with AdminCount=1.

4. Unconstrained Delegation (Non-DCs)

// Computers with unconstrained delegation (excluding DCs) MATCH (c:Computer {unconstraineddelegation:true}) WHERE NOT c.name =~ ".*DC.*" RETURN c.name, c.operatingsystem, c.lastlogontimestamp // Users with unconstrained delegation MATCH (u:User {unconstraineddelegation:true, enabled:true}) RETURN u.name, u.admincount

Unconstrained delegation on a non-DC means if an attacker can coerce any privileged account into authenticating to that machine, they can capture a usable TGT and impersonate that account. Should be near-zero on non-DCs. Enumerate everything, confirm what's intentional (usually nothing), and remove the flag.

5. DCSync Rights

// Principals with DCSync rights MATCH p=(n)-[:DCSync|AllExtendedRights|GenericAll]->(d:Domain) WHERE NOT n.name STARTS WITH "DOMAIN ADMINS" AND NOT n.name STARTS WITH "ENTERPRISE ADMINS" AND NOT n.name = "DOMAIN CONTROLLERS@CORP.LOCAL" RETURN n.name, type(last(relationships(p))) AS permission_type, labels(n) AS node_type

DCSync rights let an account replicate the AD database including all password hashes. Should be held only by DCs and highest-privilege Tier 0 accounts. Anyone else on this list needs immediate investigation and remediation.

6. Stale AdminCount=1 Accounts

// Users with AdminCount=1 not currently in any privileged group // These are often historical — protected by ACLs but no longer in a privileged group MATCH (u:User {admincount:1, enabled:true}) WHERE NOT (u)-[:MemberOf*1..]->(:Group) RETURN u.name, u.pwdlastset

What AdminCount=1 means: When an account is added to a protected group (Domain Admins, Backup Operators, etc.), SDProp sets AdminCount=1 and locks down the ACL. If the account is later removed, AdminCount stays at 1 and the ACL remains locked — but the account no longer shows in group membership queries. These orphaned accounts are common and worth reviewing.

Turning Graph Data Into a Remediation Backlog

The data is only useful if it drives action. Here's how I structure BloodHound findings:

Track the P1/P2 counts as KPIs. "Accounts with path to DA in ≤5 hops" is a single number that captures your AD security posture in a way that resonates without needing to explain graph theory.

BloodHound CE vs. Enterprise

BloodHound CE is free and fully capable of everything in this post. The limitation is operational: manual collection, manual import, manual query execution. No scheduling, no alerting, no automated change detection.

BloodHound Enterprise adds automated collection, continuous monitoring, exposure score tracking over time, and attack path prioritization at scale. Worth evaluating for larger organizations trying to make this part of a continuous security program.

Bottom line: The goal is to be running BloodHound before an attacker does — not reacting to it in a post-incident review. Start with CE, run the collection, learn Cypher, and close the highest-priority paths. That single shift changes the conversation from "how did they get to DA" to "we already knew about that path and closed it."