packet.zip

BloodHound for Beginners: Mapping the Attack Paths Hiding in Active Directory

Aamir Lakhani21 min read

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.

Diagram showing Active Directory objects as graph nodes — a user, a group, a computer, and the domain — connected by labeled edges MemberOf, AdminTo, and part of.
Figure 1 — Objects become nodes; permissions become directed edges.

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:

Graph diagram showing a five-hop attack path: user jdoe is MemberOf HELPDESK, which has ForceChangePassword over svc_backup, which is MemberOf BACKUP OPS, which is AdminTo FILE01, which HasSession for adm_bob, who is MemberOf DOMAIN ADMINS, which can DCSync the LAB.LOCAL domain.
Figure 2 — Five hops from an ordinary user account to dumping every hash in the domain.

Read it left to right:

  1. jdoe is a normal employee. He is in the HELPDESK group — completely legitimate.
  2. HELPDESK has ForceChangePassword over svc_backup. Someone granted that so helpdesk could unstick service accounts. Also legitimate-ish.
  3. svc_backup is in BACKUP OPERATORS, because backups need privileges.
  4. Backup Operators are AdminTo FILE01 — local administrator on the file server.
  5. adm_bob, a Domain Admin, has a live session on FILE01 because he was troubleshooting something last Tuesday.
  6. 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:

Reference table of common BloodHound edges — MemberOf, AdminTo, HasSession, GenericAll, ForceChangePassword, AddMember, WriteDacl, WriteOwner, DCSync, AllowedToDelegate, CanRDP — with what each means and how it gets abused.
Figure 9 — The edge cheat sheet. Worth keeping open in a tab while you explore.

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:

  1. Initial access via phishing, a stolen VPN credential, or an exposed service.
  2. Run BloodHound early — often within hours — to understand the environment faster than the defenders do.
  3. Follow the shortest path to Tier 0.
  4. 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

Lab topology diagram: DC01 Windows Server 2022 at 192.168.56.10 running AD DS and DNS for lab.local, WKSTN-07 Windows 10/11 client at 192.168.56.20, and ANALYST Kali box at 192.168.56.30 running BloodHound CE in Docker on port 8080, all on an isolated network.
Figure 3 — Three VMs on an isolated network. Two will do if you are short on RAM.

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:

PowerShell terminal output renaming the computer to DC01, setting a static IP of 192.168.56.10, installing the AD-Domain-Services feature, and running Install-ADDSForest for lab.local.
Figure 4 — Promoting a fresh server to a domain controller for 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.

PowerShell terminal output cloning the BadBlood repository and running Invoke-BadBlood.ps1, which warns not to run in production, then creates OUs, 2500 users, 500 groups, 100 computers, and randomises ACLs.
Figure 5 — BadBlood ages your lab domain by about fifteen years in five minutes.
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:

Terminal output downloading and extracting bloodhound-cli, running ./bloodhound-cli install, showing containers become healthy, and printing the generated initial admin password.
Figure 6 — 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-cli before 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:

PowerShell terminal output running SharpHound.exe with -c All, showing resolved collection methods, an LDAP search of lab.local, progress counters reaching 3102 objects, and a created zip file; followed by the equivalent bloodhound-ce-python command.
Figure 7 — SharpHound enumerating the domain. Note that it needs no special privileges.
.\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:

OptionWhat it collects
-c GroupGroup memberships only — fast and quiet
-c ACLObject ACLs — where the interesting edges live
-c SessionWho is logged in where (touches every host over SMB)
-c LocalAdminLocal administrator rights on machines
-c LocalGroupsComposite: LocalAdmin + RDP + DCOM + PSRemote
-c DCOnlyLDAP-only — never touches workstations, much stealthier
-c AllEverything

-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.ps1 alongside the exe, which exports Invoke-BloodHound if 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:

Terminal-style figure listing five Cypher queries for BloodHound CE: shortest path to Domain Admins, who can DCSync, Kerberoastable privileged users, unconstrained delegation, and a count of users who can reach Domain Admins.
Figure 8 — Five queries worth saving. The last one is the metric to track over time.

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, exists subqueries, MERGE). Second, "high value" has moved: older tutorials test g.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 to COALESCE(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.

PowerShell terminal output grouping Security event 4662 by account showing one user with 2891 directory reads versus 44 and 12 for others, counting event 5145 IPC$ share accesses at 96 hosts, and describing a honeypot account detection.
Figure 10 — What collection looks like from the DC. One account, the entire directory, sixty seconds.

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:

PipeAPIWhat it is asking
\PIPE\srvsvcNetSessionEnumwho is logged on here?
\PIPE\wkssvcNetWkstaUserEnumsame question, different API
\PIPE\winregremote registrysession 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.

Diagram contrasting a flat attack path where jdoe reaches FILE01 with a Domain Admin session, against a hardened version where the AdminTo edge is removed, plus a list of five high-impact fixes and guidance to measure the number of users who can reach Domain Admins.
Figure 11 — You do not patch attack paths. You re-design them out.

In rough order of impact:

  1. Tier your administration. Tier 0 accounts (Domain Admins and equivalents) must never log into Tier 1/2 machines. This single rule kills the HasSession edges 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.
  2. Remove standing local-admin rights, especially where they arrive via nested groups. Just-in-time elevation beats permanent membership.
  3. Windows LAPS — unique, rotated local administrator passwords per machine. Stops one dumped local hash turning into access everywhere.
  4. 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.
  5. Kill unconstrained delegation. It is legacy, it is dangerous, and its remaining legitimate uses are rare.
  6. Prune nested groups and audit ACLs. Most of the ugly edges are GenericAll / WriteDacl grants nobody remembers making.
  7. 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 — ForceChangePassword with net rpc password or PowerView, AddMember, WriteDacl with dacledit. 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 DCOnly and 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


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.

Aamir Lakhani

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

~/related

Keep digging

More research along the same attack path.