Active Directory Enumeration: Mapping a Domain with BloodHound
A hands-on Active Directory enumeration guide: collect the domain with SharpHound, the Python collector or netexec, load it into BloodHound, and read the graph for the shortest path to Domain Admins.
Active Directory enumeration is the work of building a full map of a Windows domain before you move on anything inside it. One foothold account rarely has the access you want, but it can read almost the entire directory: every user, every group, who administers which computer, who is logged on where, and which permissions quietly let one object take over another. This guide shows how to collect all of that with BloodHound, how to read the graph it draws, and how to turn a line on that graph into a concrete plan to reach Domain Admin. It ends on the defender side, because the same graph that finds the path also shows you which edges to cut.
Read it in order the first time to build the model, then keep it open as a reference. Every command here is for a domain you own or are explicitly authorized to test, such as a lab on 10.10.10.0/24.
Run this only against a domain you own or have written permission to assess, such as a home lab or an engagement with signed authorization. Collecting a directory you are not allowed to touch is unauthorized access and a crime in most places.
01Why Active Directory enumeration comes first
Active Directory enumeration comes first because a domain is a web of relationships, not a flat list of boxes. A single compromised user is only a starting point. What decides how far you get is everything that account can see about the rest of the directory, and a normal domain user can read most of it by design. The job of enumeration is to pull that readable structure out and shape it into something you can reason about.
There are six things worth collecting on day one, and together they describe almost every move you can make inside the domain:
| What you collect | Why it matters |
|---|---|
| Users | Every account, its groups, service accounts with an SPN, stale or never-expiring passwords. |
| Groups | Membership and nesting, so you can see who inherits privilege without being a direct member. |
| Computers | Servers and workstations, their operating systems, and delegation settings. |
| Sessions | Which user is logged on to which machine right now, the gold for lateral movement. |
| ACLs | Object permissions that let one principal reset a password, add a member, or replicate hashes. |
| Trusts | Relationships to other domains and forests that may widen the target. |
Reading that by hand through net commands and LDAP filters is slow and easy to get wrong. The value of a graph tool is that it joins all six into one picture and lets you ask questions across them at once.
02What BloodHound is and the graph model
BloodHound is a tool from SpecterOps that models a domain as a graph: a set of nodes joined by directed edges. Nodes are the objects you already know, such as users, groups, computers, domains, organizational units and GPOs. Edges are the relationships between them, and the important point is that every edge is a thing an attacker can act on. An edge is not trivia, it is a move.
A few edges show up constantly. MemberOf ties a user to a group. AdminTo says a principal is local administrator on a computer. HasSession means a user has a session on a machine, so stealing that machine gives you their credentials. Control edges such as GenericAll, GenericWrite, WriteDacl, WriteOwner, Owns and ForceChangePassword mean one object can modify another. GetChanges and GetChangesAll together grant DCSync, the right to pull password hashes straight from a domain controller.
Legacy BloodHound stored this graph in Neo4j behind an Electron app. The current Community Edition runs as a set of containers with a browser interface, but the model is identical. You collect data, load it, and walk the edges. A line of connected edges from an account you control to a high value target is an attack path, and finding those paths is the whole point.
03Collecting the data with SharpHound, the Python collector, and netexec
Collecting the data means asking the directory and the computers a long list of questions, then saving the answers as JSON for BloodHound to ingest. There are three collectors in common use, and which one you pick depends on where you are running from.
SharpHound is the native collector written in C#. It runs on a domain-joined or reachable Windows host and is the most complete, because it can query computers directly for sessions and local group membership. The collection methods control how much it touches.
| Collection method | What it gathers |
|---|---|
DCOnly | Everything readable from the domain controller over LDAP only. Quiet: it never talks to member computers. |
Session / LoggedOn | Who is logged on to each computer, queried against the machines themselves. |
LocalGroup | Local administrator, RDP and DCOM membership on each computer. |
ACL | Object permissions, the source of the control edges. |
Trusts | Domain and forest trust relationships. |
All | Every method above. The loudest and most complete option. |
# SharpHound from a Windows foothold, pointed at the lab DC # 0xpriv.dev/network .\SharpHound.exe -c All -d lab.local --domaincontroller 10.10.10.2 --zipfilename lab.zip # quiet first pass: LDAP against the DC only, no member computers touched .\SharpHound.exe -c DCOnly -d lab.local --domaincontroller 10.10.10.2
The Python collector, bloodhound-python, runs from Linux and needs no Windows host at all. It talks to the directory over LDAP and to computers over SMB, which makes it the usual choice from a Kali box or a jump host. Give it a nameserver, because it resolves everything through the domain's own DNS.
# the Python collector from Linux, resolving through the DC bloodhound-python -u 'j.doe' -p 'Summer2026!' -d lab.local \ -dc dc01.lab.local -ns 10.10.10.2 -c All --zip
netexec, the successor to CrackMapExec, has an LDAP module that collects for BloodHound in one line. It is handy when you already have netexec open for spraying or share enumeration and want the graph data without switching tools.
# netexec collecting straight into a BloodHound zip over LDAP nxc ldap 10.10.10.2 -u j.doe -p 'Summer2026!' \ --bloodhound --collection all --dns-server 10.10.10.2
Run a DCOnly pass first. It gives you users, groups, ACLs and trusts without ever touching a member computer, so the graph is already useful before you make any noise collecting sessions.
04Importing the data and reading the graph
Importing the data is the step where the raw JSON becomes a graph you can click through. Each collector drops a zip of JSON files, and you upload that zip through the BloodHound interface. On the Community Edition this is the file ingest page; on legacy BloodHound you drag the zip onto the window. Either way the objects and edges are parsed into the database and you are ready to query.
$ bloodhound-python -u j.doe -p 'Summer2026!' -d lab.local -dc dc01.lab.local -ns 10.10.10.2 -c All --zip INFO: Found AD domain: lab.local INFO: Connecting to LDAP server: dc01.lab.local INFO: Found 1 domains in the forest INFO: Found 214 users INFO: Found 61 groups INFO: Found 18 computers INFO: Compressing output into 20261011_bloodhound.zip
The first thing to do after import is mark what you control. Right-click the account you compromised and set it as Owned. Mark high value targets such as Domain Admins as Tier Zero. BloodHound uses those tags in its path queries, so the shortest path from an owned node to a high value node is a single click away once the tags are set.
Reading the graph is a loop: run a query, look at the edges it draws, right-click an edge to read its help, and follow the chain. Every edge has built-in help that explains what it means and how to abuse it, which turns the graph into a self-documenting plan.
05The pre-built queries that matter
The pre-built queries are saved graph questions that answer the problems you care about without writing any Cypher yourself. BloodHound ships a list of them, and four earn their keep on almost every domain.
| Query | What it answers |
|---|---|
| Shortest Paths to Domain Admins | Every chain of edges from the accounts you own to the Domain Admins group. |
| Find Kerberoastable Users | Accounts with a service principal name, whose ticket you can request and crack offline. |
| Principals with DCSync Rights | Who holds GetChanges and GetChangesAll and can therefore replicate password hashes. |
| Dangerous ACL edges | GenericAll, WriteDacl, WriteOwner and similar control a principal has over a privileged object. |
Under the hood each of these is a Cypher query, and you can read or adapt them in the query tab. The shortest path logic is the one worth understanding, because it is what turns a pile of edges into a route.
// every control edge from an owned principal toward Domain Admins MATCH p = shortestPath( (s)-[:MemberOf|AdminTo|HasSession|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns|ForceChangePassword|AddMember|GetChanges|GetChangesAll*1..]->(t:Group) ) WHERE s.owned = true AND t.name = 'DOMAIN [email protected]' RETURN p
The shortest path is not always the easiest. A single ForceChangePassword edge on a low privilege account can beat a four-hop chain that ends in a locked-down Tier Zero server. Read each edge before you commit to a route.
06Turning a path into a plan
Turning a path into a plan means reading each edge in order and writing down the exact action it allows, so the graph becomes a checklist instead of a picture. The method is the same for any path BloodHound draws.
Start from what you own
Confirm the first node is an account whose credentials or session you already control, so the path is actually reachable.
Read each edge
Right-click every edge and read its help. An AdminTo edge means a dump on that host, a GenericAll on a user means a password reset or shadow credentials, a GetChangesAll means DCSync.
Pick the quietest hop
Where two edges reach the same node, prefer the one that touches fewer machines or needs no new tooling, to keep the noise down.
Write the commands
Translate each edge into the standard tool for it, such as impacket for DCSync or a ticket request for a kerberoastable account, then execute hop by hop.
By the end you have a short script of moves, each one justified by an edge you can point at in the graph. That is the difference between guessing and knowing.
07A worked walkthrough on the lab domain
The worked walkthrough ties it together against the lab domain on 10.10.10.0/24, with the domain controller at 10.10.10.2. Assume a single low privilege account, j.doe, recovered from a password spray. The goal is Domain Admin, and the only tools are a collector and BloodHound.
First, collect over LDAP with netexec and feed the result straight to the graph.
$ nxc ldap 10.10.10.2 -u j.doe -p 'Summer2026!' --bloodhound --collection all --dns-server 10.10.10.2 LDAP 10.10.10.2 389 DC01 [+] lab.local\j.doe:Summer2026! LDAP 10.10.10.2 389 DC01 Resolved collection methods: group, localadmin, session, trusts, acl, container LDAP 10.10.10.2 389 DC01 Done collecting data for the domain LDAP 10.10.10.2 389 DC01 Compressed output: 10.10.10.2_bloodhound.zip
Upload the zip, mark j.doe as Owned, and run Shortest Paths to Domain Admins. The graph draws a three-hop path, and reading it left to right gives the plan outright.
The chain reads cleanly. j.doe is a member of IT Support, that group can force a password change on the service account svc-sql, and svc-sql holds DCSync rights on the domain. So the plan is three moves: reset the service account password, authenticate as it, then replicate the krbtgt and administrator hashes.
$ impacket-secretsdump -just-dc-user Administrator 'lab.local/svc-sql:[email protected]' [*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash) [*] Using the DRSUAPI method to get NTDS.DIT secrets lab.local\Administrator:500:aad3b435b51404ee...:31d6cfe0d16ae931...
Three edges on a graph became three commands, and the hash at the end is Domain Admin. The ForceChangePassword step is noisy, because it rewrites a real account's password, so on a live engagement you would note it and restore the password afterward. In a lab it is the fast route.
08Detecting BloodHound collection
Detecting a collection run is realistic because the activity has a shape, even though every single query is one a normal user is allowed to make. The tell is volume and pattern rather than any one request.
The loudest signal is LDAP query volume. A collector pulls the entire directory in seconds: users, groups, ACLs and trusts, all from one source, far faster than a human ever browses. On a domain controller with directory service access logging, those show up as event 1644 when expensive and inefficient query logging is turned on, and as a sharp burst in LDAP traffic to port 389 from a single host. Session collection adds a second signal, a single machine reaching out to many computers over SMB to call NetSessionEnum and NetWkstaUserEnum in a tight loop, which is exactly what normal workstations never do.
SharpHound also leaves host-side traces when it runs on Windows, since the binary and its .NET assembly behavior are known to endpoint tooling. The most reliable detection, though, is a honeypot object: a decoy account given a tempting service principal name or a fake privileged group membership, wired to alert the moment anything queries or touches it. A real user never has a reason to look at it, so a hit is a strong signal that someone is mapping the domain.
09Shutting down the attack paths
Shutting down the attack paths is the defensive flip side of everything above, because the graph that found the route also names the edges to cut. You will never remove every edge, but you can break the chains that reach Tier Zero.
Run BloodHound against your own domain on a schedule and treat each new path to Tier Zero as a finding to close. The attacker and the defender are reading the same graph, so the side that reads it more often wins.
10Frequently asked questions
Do I need to be a Domain Admin to collect with BloodHound?FAQ
No. Any authenticated domain user can read almost the entire directory, which is exactly why enumeration is so productive. A standard account is enough to collect users, groups, ACLs and trusts, and usually sessions too. Elevated rights only make collection faster and quieter, not possible.
What is the difference between SharpHound and the Python collector?FAQ
SharpHound is the native C# collector and runs on Windows, where it can query computers directly for the most complete session and local group data. The Python collector, bloodhound-python, runs on Linux and talks to the directory over LDAP and to hosts over SMB, so you can map a domain from a Kali box without any Windows foothold.
Is a DCOnly collection enough?FAQ
Often it is enough to find a path. DCOnly pulls users, groups, ACLs and trusts straight from the domain controller over LDAP and never touches member computers, so it is quiet and fast. You miss live session data, which matters for lateral movement, so add a session pass once you know the DCOnly graph is worth deeper collection.
Will BloodHound collection set off alerts?FAQ
It can. No single query is unusual, but the volume and pattern are: a burst of LDAP reads from one host, or one machine calling session enumeration across many computers. Defenders who watch LDAP volume, log expensive queries, or plant honeypot objects will see a full collection run. A DCOnly pass is quieter than All.
How do I read a BloodHound attack path?FAQ
Read it left to right, one edge at a time, starting from a node you control. Right-click each edge for its built-in help, which explains what the relationship allows and the standard way to abuse it. Each edge becomes one concrete action, and the ordered list of those actions is your plan to reach the target.
11Wrapping up
Active Directory enumeration turns a single foothold into a map of the whole domain, and BloodHound turns that map into a graph you can question. Collect with SharpHound, the Python collector or netexec, mark what you own, and let the shortest path query draw the route from your account to Domain Admin. Read each edge as a move, and the graph becomes a plan. The same discipline on the defensive side, tiering, pruning ACLs, limiting sessions and watching LDAP, is what cuts those paths before anyone else maps them.
references
Founder of 0xpriv, covering offensive security, web application vulnerabilities, and practical security research through PHP, Python, and hands-on technical analysis.