0
Network Security INTERMEDIATE 14 min read Updated

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.

founder & security researcher · 39 writeups
Active Directory Enumeration: Mapping a Domain with BloodHound

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.

Scope

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 collectWhy it matters
UsersEvery account, its groups, service accounts with an SPN, stale or never-expiring passwords.
GroupsMembership and nesting, so you can see who inherits privilege without being a direct member.
ComputersServers and workstations, their operating systems, and delegation settings.
SessionsWhich user is logged on to which machine right now, the gold for lateral movement.
ACLsObject permissions that let one principal reset a password, add a member, or replicate hashes.
TrustsRelationships 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.

nodes joined by edges that are really moves
Userj.doe MemberOf GroupIT Support GenericAll GroupHelpdesk MemberOf DA

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 methodWhat it gathers
DCOnlyEverything readable from the domain controller over LDAP only. Quiet: it never talks to member computers.
Session / LoggedOnWho is logged on to each computer, queried against the machines themselves.
LocalGroupLocal administrator, RDP and DCOM membership on each computer.
ACLObject permissions, the source of the control edges.
TrustsDomain and forest trust relationships.
AllEvery method above. The loudest and most complete option.
powershell
# 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.

bash
# 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.

bash
# 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
Start quiet, then go deep

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.

the Python collector finishing a run
$ 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.

QueryWhat it answers
Shortest Paths to Domain AdminsEvery chain of edges from the accounts you own to the Domain Admins group.
Find Kerberoastable UsersAccounts with a service principal name, whose ticket you can request and crack offline.
Principals with DCSync RightsWho holds GetChanges and GetChangesAll and can therefore replicate password hashes.
Dangerous ACL edgesGenericAll, 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.

cypher
// 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
Paths are not all equal

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.

collecting the lab domain with netexec
$ 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 path the lab graph returns
j.doeowned MemberOf IT Support ForceChangePassword svc-sqlDCSync GetChangesAll Domain

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.

executing the last edge, DCSync, with impacket
$ 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.

How to shut it down
Enforce admin tiering. Keep Tier Zero accounts, the ones that control the domain, off Tier One and Tier Two machines entirely. If a Domain Admin never logs on to a workstation, there is no HasSession edge to steal.
Prune dangerous ACLs and nested groups. Audit GenericAll, WriteDacl, WriteOwner and GetChangesAll on privileged objects, and flatten deep group nesting so privilege is not inherited by surprise.
Limit session exposure. Reduce where privileged accounts log on with policy and jump hosts, so a single compromised machine does not expose high value credentials.
Monitor LDAP and plant honeypots. Alert on burst LDAP reads and session enumeration, and seed decoy privileged objects that should never be queried.
Use the Protected Users group. Members cannot authenticate with NTLM, cannot use weak Kerberos ciphers, and are not allowed credential delegation, which closes several edges at once.

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

Was this writeup useful?
Alex Rowan Founder & Security Researcher at 0xpriv

Founder of 0xpriv, covering offensive security, web application vulnerabilities, and practical security research through PHP, Python, and hands-on technical analysis.

related writeups

more in Network →

discussion

0 comments
`code` supported · comments are reviewed before they appear