BloodHound for Beginners: Mapping the Attack Paths Hiding in Active Directory
Most Active Directory environments are not compromised because of a missing patch. They are compromised because of a chain of small, individually reasonable decisions — a helpdesk group that can reset passwords, a service account that ended up in Backup Operators, a domain admin who once logged into a file server and left a session behind.
Individually, none of those is a vulnerability. Strung together, they are a five-hop road to Domain Admin.
BloodHound is the tool that finds those chains. It takes everything in your directory, turns it into a graph, and then asks the question no spreadsheet-based ACL audit can answer: given this user, what is the shortest path to owning the domain?
It was built by SpecterOps and released free and open source. Red teams love it. Blue teams should love it more — because the same graph that shows an attacker their route shows you exactly which edge to delete.
By the end of this guide you will have built a lab domain, populated it with realistic mess, collected the graph, found attack paths, and then learned how to detect the collection and dismantle the paths.
Before we start: the ground rules
Same rules as always on this blog, and they matter here more than most.
- Build the lab. Do not point this at production without written authorization. BloodHound's collector is read-only — it does not change anything — but running it against a directory you do not own is unauthorized enumeration of that directory. It is also extremely visible if anyone is watching, which is the whole point of Part 7.
- If you own the AD and want to run it for real: do it. Genuinely. Get sign-off, run it, and be prepared for the results to be unflattering. Every environment's first BloodHound run is humbling.
- Do not connect this lab to a production network. You are about to build a deliberately weak domain and spray thousands of random ACLs across it. Keep it on an isolated virtual network.
- Snapshot everything before you start. You will break the domain at some point. That is fine, if you can roll back.
Part 1 — Active Directory in five minutes
If you already administer AD, skip ahead. If not, here is the minimum.
Active Directory is the identity system that runs the overwhelming majority of corporate Windows networks. It stores users, groups, computers, and policies in a central database, and a Domain Controller (DC) serves that database out. When you log into a work laptop, that laptop asks a DC to verify you.
The pieces that matter for this article:
- Users — people and service accounts.
- Groups — collections of users. Crucially, groups can contain other groups, and rights are inherited down the chain. This nesting is where blast radius hides.
- Computers — every domain-joined machine is an object too.
- Domain Admins — the group that effectively owns everything. There are a few equivalents (Enterprise Admins, Administrators, and anything that can DCSync); reaching any of them is game over.
- ACLs — every object has an access control list saying who can do what to it. Who can reset this user's password? Who can add members to this group? Who can rewrite this object's permissions? Those answers are ACL entries, and there are usually hundreds of thousands of them in a real domain.
That last point is the crux. No human can audit hundreds of thousands of ACLs by reading them. But a computer can walk them as a graph.
Part 2 — What BloodHound actually does
BloodHound applies graph theory to Active Directory. Everything becomes a node; every relationship becomes a directed edge.
Once your directory is a graph, finding attack paths stops being detective work and becomes a pathfinding query — the same computer-science problem as routing a GPS. "Shortest path from this node to that node" is a solved problem, and BloodHound just runs it against your permissions.
Here is what that looks like in practice, and it is worth studying for a minute:
Read it left to right:
- jdoe is a normal employee. He is in the HELPDESK group — completely legitimate.
- HELPDESK has ForceChangePassword over svc_backup. Someone granted that so helpdesk could unstick service accounts. Also legitimate-ish.
- svc_backup is in BACKUP OPERATORS, because backups need privileges.
- Backup Operators are AdminTo FILE01 — local administrator on the file server.
- adm_bob, a Domain Admin, has a live session on FILE01 because he was troubleshooting something last Tuesday.
- Domain Admins can DCSync — replicate the directory, i.e. dump every password hash in the company.
An attacker who phishes jdoe resets a service account password, logs into the file server as a local admin, steals the Domain Admin's credential material from memory, and then dumps the entire domain. No exploit. No malware signature. No CVE. Just permissions doing exactly what they were configured to do.
This is why BloodHound changed the industry. It made this class of problem visible.
The edges are the vocabulary
BloodHound understands dozens of relationship types. These are the ones you will see most:
Part 3 — Who uses this, and how
The user brief for this article asked me to cover all three perspectives, and they genuinely differ.
Red team / penetration testers
For an offensive operator, BloodHound answers "where do I go next?" It collapses what used to be days of manual enumeration into a single query.
Typical workflow: land initial access on one workstation → run the collector as that low-privileged user → mark the compromised account as Owned in BloodHound → ask for the shortest path from Owned to Domain Admins → work the path hop by hop, abusing each edge with a matching tool.
The efficiency gain is enormous, and it changed how engagements are scoped. It also changed reporting: instead of "we found a misconfiguration," a red team can hand over a picture of the exact chain, which is far more persuasive to management.
Blue team / defenders — the underrated half
This is the part that gets skipped, and it is the higher-value use.
BloodHound is a defensive auditing tool that happens to also be an offensive one. The graph is neutral; only the intent differs. As a defender you can ask questions that are otherwise unanswerable:
- How many enabled users can reach Domain Admins at all? (Nearly always a shockingly large number the first time.)
- Who can DCSync the domain? It should be a very short list, and every entry should be deliberate.
- Which accounts are Kerberoastable and privileged? Those are your emergencies.
- Where is unconstrained delegation still enabled?
- Which service accounts have local admin on more machines than anyone remembers?
Then you delete edges and re-measure. That is the loop. BloodHound turns "improve AD security" from a vague aspiration into a number that goes down. I will come back to this in Part 8 because it is the single most useful thing in this article.
How real attackers use it
Not theoretical. BloodHound and its collectors show up constantly in incident response, and in ransomware crews' own internal documentation — the leaked Conti playbooks included BloodHound instructions for affiliates, essentially as standard operating procedure.
The pattern in real intrusions is consistent:
- Initial access via phishing, a stolen VPN credential, or an exposed service.
- Run BloodHound early — often within hours — to understand the environment faster than the defenders do.
- Follow the shortest path to Tier 0.
- Use Domain Admin to push ransomware everywhere at once, or to quietly exfiltrate for months.
The uncomfortable takeaway: attackers frequently understand your AD attack surface better than you do, because they bothered to run the graph and you did not. That asymmetry is entirely fixable, and fixing it costs nothing but time.
Part 4 — Build the lab
What you need
- A hypervisor: VirtualBox (free), VMware, Proxmox, or UTM on Apple Silicon.
- DC01 — Windows Server 2022 or 2025. Microsoft publishes free time-limited evaluation ISOs; that is all a lab needs.
- WKSTN-07 — Windows 10 or 11 Enterprise evaluation, domain-joined. Optional — see the note below.
- ANALYST — Kali or Ubuntu with Docker, to run BloodHound.
- ~12 GB RAM and 120 GB disk across all three, and a couple of hours.
Tight on resources? Drop WKSTN-07. You can collect the graph from Linux with the Python collector instead of running SharpHound on Windows. You lose some session data (which is collected from Windows hosts), but every other edge type still works and the exercise holds up fine.
Put all VMs on an isolated / internal / host-only network. You will need internet briefly to download ISOs and tools; disconnect afterwards.
Step 1 — Promote the domain controller
Install Windows Server, then:
lab.local.Rename-Computer -NewName DC01 -Restart
# after reboot — static IP, and point DNS at itself
New-NetIPAddress -InterfaceAlias 'Ethernet' -IPAddress 192.168.56.10 -PrefixLength 24
Set-DnsClientServerAddress -InterfaceAlias 'Ethernet' -ServerAddresses 127.0.0.1
Install-WindowsFeature AD-Domain-Services -IncludeManagementTools
Install-ADDSForest -DomainName lab.local -DomainNetbiosName LAB -InstallDns -Force
It will prompt for a Safe Mode (DSRM) password and reboot. When it comes back you have a domain.
Step 2 — Make the domain realistically messy
A clean, freshly-built domain has almost no attack paths, which makes for a boring lab. Real domains are messy because they are old. BadBlood simulates that age: it creates thousands of users, groups, and OUs, and — critically — sprays random ACLs across them.
git clone https://github.com/davidprowe/BadBlood.git
cd BadBlood
.\Invoke-BadBlood.ps1
It will make you type badblood to confirm, because it is genuinely destructive to a real directory. It needs Domain Admin and Schema Admin. Only ever run this in a lab.
Two things the README gets wrong or omits:
-
It claims there are no parameters. There are — and smaller numbers are much kinder to a laptop:
.\Invoke-BadBlood.ps1 -UserCount 500 -GroupCount 100 -ComputerCount 50 -NonInteractive -
The project is effectively unmaintained (last commit 2023). It still works and it is still the best tool for generating volume, but do not expect updates. If you want a purpose-built vulnerable lab with curated attack paths instead of random ones, look at GOAD, which is actively maintained and ships a 2-VM "MINILAB" profile.
Step 3 — Add a deliberate attack path
BadBlood's randomness usually produces interesting paths on its own, but let us guarantee the one from Figure 2 exists so you have something known-good to find:
# a normal user, a service account, and a helpdesk group
New-ADUser -Name jdoe -SamAccountName jdoe -AccountPassword (Read-Host -AsSecureString) -Enabled $true
New-ADUser -Name svc_backup -SamAccountName svc_backup -AccountPassword (Read-Host -AsSecureString) -Enabled $true
New-ADGroup -Name HELPDESK -GroupScope Global
Add-ADGroupMember -Identity HELPDESK -Members jdoe
# svc_backup lands in a privileged builtin group
Add-ADGroupMember -Identity "Backup Operators" -Members svc_backup
# and HELPDESK can reset svc_backup's password (this is the dangerous edge)
$svc = Get-ADUser svc_backup
$hd = Get-ADGroup HELPDESK
$acl = Get-Acl "AD:$($svc.DistinguishedName)"
$rule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
$hd.SID, 'ExtendedRight', 'Allow',
[GUID]'00299570-246d-11d0-a768-00aa006e0529') # User-Force-Change-Password
$acl.AddAccessRule($rule)
Set-Acl -Path "AD:$($svc.DistinguishedName)" -AclObject $acl
That last block is the ForceChangePassword edge, created exactly the way a well-meaning admin would create it.
Part 5 — Install BloodHound CE
BloodHound Community Edition (CE) is the current version. It replaced the older "BloodHound Legacy," which needed a hand-installed Neo4j database — CE ships everything in Docker Compose, which is a large quality-of-life improvement.
The current documented install path is bloodhound-cli, a small Go binary that wraps Docker Compose for you. (If you find an older tutorial telling you to pipe ghst.ly/getbhce into docker compose — that URL still works, but it is no longer the recommended route.)
On ANALYST, with Docker installed:
bloodhound-cli install brings up the whole stack and prints the initial password.wget https://github.com/SpecterOps/bloodhound-cli/releases/latest/download/bloodhound-cli-linux-amd64.tar.gz
tar -xvzf bloodhound-cli-linux-amd64.tar.gz
./bloodhound-cli install
The installer prints the generated admin password when it finishes:
[+] BloodHound is ready to go!
[+] You can log in as `admin` with this password: <password>
Browse to http://localhost:8080/ui/login, log in as admin, and change the password when prompted.
Handy subcommands: ./bloodhound-cli resetpwd if you lose the password, ./bloodhound-cli update to upgrade, ./bloodhound-cli containers stop to shut it down.
On macOS you will need
xattr -d com.apple.quarantine ./bloodhound-clibefore the binary will run.Resources: the docs ask for 8 GB RAM / 4 cores / 10 GB disk as a minimum for the BloodHound host itself. On a low-RAM machine the container can OOM (exit 137) if you hammer the API immediately after startup — give it a moment to settle.
Part 6 — Collect the graph
BloodHound does not touch your directory itself. A separate collector reads AD and produces JSON, which you upload. The collector is the noisy part — remember that for Part 7.
SharpHound is the Windows collector. Download the version that matches your BloodHound CE release (CE and Legacy collectors are not interchangeable — grab it from the BloodHound CE releases page or the download link in the CE UI).
On WKSTN-07, as any domain user — no admin rights needed:
.\SharpHound.exe -c All --zipfilename lab-collection
-c is short for --collectionmethods. All runs everything; the narrower options matter both for speed and for noise:
| Option | What it collects |
|---|---|
-c Group | Group memberships only — fast and quiet |
-c ACL | Object ACLs — where the interesting edges live |
-c Session | Who is logged in where (touches every host over SMB) |
-c LocalAdmin | Local administrator rights on machines |
-c LocalGroups | Composite: LocalAdmin + RDP + DCOM + PSRemote |
-c DCOnly | LDAP-only — never touches workstations, much stealthier |
-c All | Everything |
-c DCOnly is the one to remember: it is a composite of the LDAP-sourced methods (ACL, Container, Group, ObjectProps, Trusts, GPOLocalGroup, CertServices) and skips per-host SMB enumeration entirely. Dramatically quieter, at the cost of session data. Also useful: --stealth, --throttle / --jitter to slow collection down, and -l to loop session collection over time.
Do not pin a SharpHound version from a tutorial — including this one. Collector and CE releases move together, so download the collector from your own running instance (left menu → Download Collectors). A mismatched collector produces data your instance will not ingest cleanly.
Note also that the zip ships
SharpHound.ps1alongside the exe, which exportsInvoke-BloodHoundif you prefer PowerShell:Invoke-BloodHound -CollectionMethods All.
No Windows host? Use the Python collector from ANALYST. Be careful here: the original bloodhound-python targets Legacy, and CE has a separate build. Note the package name and the command name differ, which catches people out:
pipx install bloodhound-ce # package is bloodhound-ce
bloodhound-ce-python -d lab.local -u jdoe -p 'Password123!' \
-ns 192.168.56.10 -c All --zip # command is bloodhound-ce-python
Use pipx (or a venv) — you cannot have the legacy and CE versions installed globally at the same time. The Python collector's -c values are not identical to SharpHound's, and it cannot collect GPO local groups.
Then upload the zip: in the CE UI use Quick Upload in the left menu, or Administration → Data Collection → File Ingest. Zip files are accepted directly (some older write-ups claim otherwise — they are stale). Ingestion takes a minute or two for a lab-sized domain.
Part 7 — Ask the graph questions
BloodHound CE ships a library of pre-built queries — start there, they are genuinely good. "Find all Domain Admins," "Shortest paths to Domain Admins," "Find Computers with Unconstrained Delegation" and friends are all one click.
The real power, though, is Cypher, the graph query language. A few that earn their keep:
Shortest paths to Domain Admins — the classic:
MATCH p=shortestPath((t:Group)<-[*1..]-(s:Base))
WHERE t.objectid ENDS WITH '-512' AND s <> t
RETURN p LIMIT 1000
(-512 is the well-known RID for Domain Admins, which is more reliable than matching on a name that might be localized or renamed. :Base is CE's generic node label.)
Who can DCSync? — i.e. who can dump every hash:
MATCH p=(:Base)-[:DCSync|AllExtendedRights|GenericAll]->(:Domain)
RETURN p LIMIT 1000
Kerberoastable users — excluding krbtgt and managed accounts, which are noise:
MATCH (u:User)
WHERE u.hasspn = true AND u.enabled = true
AND NOT u.objectid ENDS WITH '-502'
AND NOT COALESCE(u.gmsa, false) = true
AND NOT COALESCE(u.msa, false) = true
RETURN u LIMIT 100
Unconstrained delegation — old, dangerous, and still everywhere:
MATCH p=shortestPath((s)-[*1..]->(t:Computer))
WHERE t.unconstraineddelegation = true AND s <> t
RETURN p LIMIT 1000
Two CE gotchas that will bite you. First, BloodHound CE does not hand your query to Neo4j — it has its own Cypher parser, and a few things Neo4j supports are unimplemented (
CASE, list/pattern comprehensions,existssubqueries,MERGE). Second, "high value" has moved: older tutorials testg.highvalue = true, but current CE marks Tier Zero with a label (Tag_Tier_Zero). If a tiering query returns nothing, that is usually why — and on some builds you may need to fall back toCOALESCE(n.system_tags,'') CONTAINS 'admin_tier_0'.
In the lab, try marking jdoe as Owned (right-click the node) and then running the shortest-path query from Owned users. You should find the chain from Figure 2. That moment — watching the tool draw a line from a nobody account to the domain — is the one that makes this click.
Part 8 — Detect the collection
Now the defender's half. SharpHound is loud. It is not stealthy by nature, because its job is to ask the directory about everything.
What to look for:
1. LDAP volume from a single account. Collection reads essentially every object in the directory in under a minute. Event 4662 (operation on an AD object) grouped by account makes this obvious. Be warned about volume, though: SACL-based 4662 logging can produce on the order of hundreds of events per user per day — hundreds of thousands per day in a mid-sized environment. It needs baselining before it is useful, which is why decoy objects (below) are the higher-yield play.
2. Session enumeration across many hosts. With -c All, SharpHound touches each computer over SMB. It checks TCP 445, then uses NetSessionEnum, NetWkstaUserEnum, and remote registry — which means named pipes over IPC$. Key on these three, from one source to many destinations, in 5145:
| Pipe | API | What it is asking |
|---|---|---|
\PIPE\srvsvc | NetSessionEnum | who is logged on here? |
\PIPE\wkssvc | NetWkstaUserEnum | same question, different API |
\PIPE\winreg | remote registry | session enumeration via registry |
Very few legitimate tools fan out across the estate like that.
3. Decoy objects — the cheapest control you will ever deploy. Create a user that no legitimate process should ever query. Give it a tempting name (svc_sql_admin), an SPN so it looks Kerberoastable, and no actual privileges. Then alert on any 4662 or 4769 referencing it. Legitimate activity never touches it, so the false-positive rate is essentially zero. Defender for Identity ships this as Honeytoken was queried via LDAP (2429) and Honeytoken authentication activity (2014).
4. Client- and DC-side LDAP telemetry. Beyond event logs, the Microsoft-Windows-LDAP-Client ETW provider exposes the queries themselves. In Defender for Endpoint that surfaces as DeviceEvents with an LdapSearch action, where you can hunt on the search filter — SharpHound's filters are distinctive. Caveats: user-space ETW can be tampered with, MDE excludes some processes, and these are all source-modifiable indicators.
5. Microsoft Defender for Identity. If you have it, several detectors map onto collection directly. Note MDI is mid-migration between two alert-naming schemes, so you may see either:
xdr_SuspiciousLdapQuery— "suspicious LDAP query associated with a known attack tool." The closest thing to a named BloodHound detection.xdr_SmbSessionEnumeration— suspicious SMB enumeration from an untrusted host.xdr_SuspiciousSensitiveAttributeLdapQuery,xdr_PossibleSpnEnumerationLdap,xdr_DcSyncAttackDetected.- Classic equivalents: Security principal reconnaissance (LDAP) — 2038, User and Group membership reconnaissance (SAMR) — 2021, Account Enumeration reconnaissance (LDAP) — 2437, Suspected DCSync attack — 2006.
6. Enable the logging first. None of the above works if you are not collecting it: turn on Directory Service Access auditing for 4662 (with SACLs, and tune for volume), Detailed File Share auditing for 5145 at least on DCs and servers, and forward DC security logs somewhere you can actually query.
7. Antivirus catches the easy case. Microsoft signatures the collector binary (HackTool:MSIL/SharpHound), and SpecterOps' own docs acknowledge AV/EDR flags it. Plenty of real intrusions get caught right here, before any behavioral detection matters — which is also why sophisticated operators do not run it off disk unmodified.
8. Accept that motivated attackers will evade this. -c DCOnly skips the SMB fan-out entirely. --stealth, --throttle and --jitter slow collection to blend into noise. Detection here is about raising cost and catching the unsophisticated majority — not building an impenetrable wall.
Worth knowing: SpecterOps does not publish detection guidance for SharpHound collection. Their defender-facing material is about using BloodHound defensively and about hardening BloodHound deployments. The detection content above comes from Microsoft's documentation and third-party detection engineering, not from the tool's authors.
Part 9 — Actually fix it
Detection tells you someone is looking. Fixing the paths means there is less to find.
In rough order of impact:
- Tier your administration. Tier 0 accounts (Domain Admins and equivalents) must never log into Tier 1/2 machines. This single rule kills the
HasSessionedges that make most paths work. Microsoft's current guidance lives under the Enterprise Access Model / privileged access strategy — the old "ESAE / Red Forest" pattern has been retired as a general recommendation, so read the current material rather than a 2016 blog post. - Remove standing local-admin rights, especially where they arrive via nested groups. Just-in-time elevation beats permanent membership.
- Windows LAPS — unique, rotated local administrator passwords per machine. Stops one dumped local hash turning into access everywhere.
- gMSA for service accounts. Group Managed Service Accounts have long random passwords the system rotates automatically, which makes Kerberoasting them pointless. Where gMSA is not feasible, use 25+ character passwords.
- Kill unconstrained delegation. It is legacy, it is dangerous, and its remaining legitimate uses are rare.
- Prune nested groups and audit ACLs. Most of the ugly edges are
GenericAll/WriteDaclgrants nobody remembers making. - Add privileged accounts to Protected Users and require smartcard/MFA for admin where you can.
The metric that matters
If you take one thing from this article, take this: run collection quarterly and track how many enabled users can reach Domain Admins at all. That number is your domain's blast radius.
The honest caveat is that many-to-many shortest-path queries are expensive — BloodHound's own docs warn that they often will not complete on a large graph, and the shipped "Owned → Tier Zero" query is commented out for exactly that reason. So in a real environment, sample rather than boil the ocean: pick your most-exposed populations (a department's users, everyone in a VPN group, all service accounts) and measure paths from those.
A lab-scale version, which is fine on a BadBlood domain:
MATCH (u:User) WHERE u.enabled = true
MATCH (g:Group) WHERE g.objectid ENDS WITH '-512'
WITH u, g MATCH p=shortestPath((u)-[*1..]->(g))
RETURN count(DISTINCT u) AS users_who_can_reach_domain_admins
Most organizations run something like this the first time and find a number in the thousands. Getting it into the dozens is a multi-quarter project — and a genuinely meaningful one. Put it on a dashboard. Make it go down.
Also track the two adjacent lists, which are cheap to compute and should both be short and deliberate: who can DCSync, and where Domain Admin sessions are landing on non-DCs.
Tear the lab down
# ANALYST: stop BloodHound and drop its data
docker compose down -v
Then revert your VM snapshots. If you want to keep the lab, at minimum leave it powered off and off any network that touches something real.
Where to go next
- Abuse the edges you found. Each edge type has a matching technique —
ForceChangePasswordwithnet rpc passwordor PowerView,AddMember,WriteDaclwithdacledit. Working through them teaches you how AD permissions actually behave. My Impacket tutorial covers the credential and lateral-movement half of this in detail. - Try
-c DCOnlyand re-run your detections. Watch which of your alerts still fire. This is the fastest way to understand the limits of your own monitoring. - Collect from your real environment, with permission. Nothing in a lab prepares you for the first honest look at a directory that has been running for a decade.
- Learn Cypher properly. It is a small language and it pays for itself immediately.
- Look at AzureHound if you run Entra ID / Azure. Cloud identity has its own graph and its own ugly edges.
Further reading
- BloodHound Community Edition — source and releases
- BloodHound CE quickstart — the authoritative install steps
- bloodhound-cli — the installer/manager binary
- SharpHound flags reference
- Traversable and non-traversable edges — the full edge catalogue
- Supported Cypher in CE — what CE's parser does and does not implement
- BadBlood — populate a lab domain with realistic mess (unmaintained but still useful)
- GOAD — actively maintained vulnerable AD lab, incl. a 2-VM MINILAB
- Defender for Identity alert reference
- Microsoft: securing privileged access / Enterprise Access Model
- Windows LAPS
- MITRE ATT&CK T1069 — Permission Groups Discovery
- MITRE ATT&CK T1482 — Domain Trust Discovery
A note on the figures: they are authored illustrations rendered in this site's style, showing the output and structures you should expect, rather than captures from one specific host. Version strings, object counts, and timings in your lab will differ. Tool flags and setup steps change between releases — when in doubt, the official documentation linked above wins over any tutorial, including this one.
Founder · Packet.Zip
Aamir Lakhani is a leading senior security strategist responsible for providing IT security solutions to major enterprises and government organizations. He creates technical security strategies and leads security implementation projects for…