Before BloodHound, finding a path to Domain Admin in a mid-size domain was mostly guesswork: you enumerated ACLs by hand, eyeballed group memberships, and built the graph in your head. BloodHound moved that graph into Neo4j, where you can actually query it.
The workflow is three steps: collect, ingest, query. Most people get the first one wrong.
Collection
SharpHound is the collector. Run it from a domain-joined Windows host with the credentials of a normal user:
.\SharpHound.exe -c All --outputdirectory C:\temp\loot
-c All collects users, groups, computers, sessions, ACLs, trusts, GPOs, and OUs. It writes a zip. That collection is enough for most engagements.
Things that matter at this stage:
- Sessions are time-sensitive.
AdminCountand session data go stale in hours. Collect, then analyse immediately — don’t collect on Monday and query on Thursday. - Stealth matters on real engagements. Session collection touches every machine.
--stealthand--excludedcsreduce the footprint. For a first pass,-c DCOnlycollects everything from the domain controllers alone: no host is touched except the DC, and you still get the full ACL picture. - Output format.
--outputdirectorykeeps the zip somewhere predictable; the default drops it in the current working directory and people lose track of it.
Ingestion
Start Neo4j, then import the zip via the BloodHound UI or the CLI:
neo4j console
Once imported, two built-in queries answer most of the questions:
- Shortest Paths from Owned Principals — set your controlled user as owned, then look at what is reachable.
- Shortest Paths to Domain Admins — the inverse view.
Reading the graph
The useful primitive is not “who is a Domain Admin”. It is “which edge can I traverse”. The edges worth memorising:
| Edge | What it means |
|---|---|
MemberOf | Direct group nesting |
AdminTo | Local admin on that host |
CanRDP / CanPSRemote | Remote access rights |
GenericAll / WriteDacl | Full or ACL-writing control over the object |
ForceChangePassword | Exactly what it says |
AllowedToDelegate | Constrained delegation target |
An AdminTo edge to a workstation that a Domain Admin logs into is a path. So is GenericAll on a group that is nested into Domain Admins.
The pattern that keeps working
The most common real-world path I find is mundane:
- A helpdesk group has
ForceChangePasswordon a broad OU of users. - One of those users has an active session on a server where a tier-2 admin also has a session.
- That admin has
AdminToon the domain controller.
Nothing exotic, three edges, and it’s reachable from a starter account. BloodHound makes it visible in a way that scrolling through ADUC never did.
Remediation is the hard part
Remediation is where the graph stops being a red-team toy and becomes useful to the defenders running it. Cutting one edge usually breaks the path:
- Remove
AdminTowhere it isn’t operationally required. - Break the tier model so tier-2 admins never have sessions on tier-0 assets.
- Rotate credentials for any account that appeared in a discovered path — assume compromise.
Re-run collection after remediation and confirm the path is actually gone. A surprising number of “fixed” paths survive because the change was made to a group the tool reads through a different route.