BloodHound maps Active Directory relationships and finds attack paths that are invisible to manual enumeration. On Forest, it revealed a chain through 5 nested groups to WriteDACL on the domain. I would never have found that by hand. Never.

This is how I set it up, collect data, import it, and find the paths that matter.


Setup — BloodHound CE on Kali

BloodHound CE (Community Edition) replaced the old BloodHound. It runs as a Docker container with a web UI.

Install and Start

# Pull and start BloodHound CE
curl -L https://ghst.ly/getbhce | docker compose -f - up

# First run takes a few minutes to pull images
# Web UI: http://localhost:8080

First Login — The neo4j Password Gotcha

On first launch, BloodHound CE generates a random admin password. It’s printed in the Docker logs and is easy to miss.

# Find the initial password
docker compose logs | grep "Initial Password"
# or check the logs scrollback for the generated credentials

The gotcha I hit on Active: I couldn’t find the password in the terminal output because it scrolled past. Had to dig through Docker logs. Save it the first time.

Default credentials are usually admin / <random>. You’ll be forced to change the password on first login.

Verify It’s Running

Navigate to http://localhost:8080. You should see the BloodHound CE login page. Log in with the initial creds and set a new password.


Data Collection — bloodhound-python

bloodhound-python is the Linux collector. It queries the DC remotely — no need to run anything on the target.

The Command I Use Every Time

bloodhound-python -u 'USERNAME' -p 'PASSWORD' -ns $ip -d DOMAIN.local -c All

Flag Breakdown

FlagWhat it does
-uUsername
-pPassword
-nsNameserver (DC IP) — critical, tells it where to resolve DNS
-dDomain name
-c AllCollect everything — users, groups, sessions, ACLs, trusts, containers, GPOs

Output

Creates several JSON files in the current directory:

20260324_computers.json
20260324_domains.json
20260324_groups.json
20260324_users.json

Real Examples from My Boxes

# Active — GPP creds
bloodhound-python -u 'SVC_TGS' -p 'GPPstillStandingStrong2k18' -ns 10.129.3.112 -d active.htb -c All

# Forest — AS-REP creds
bloodhound-python -u 'svc-alfresco' -p 's3rvice' -ns 10.129.95.210 -d htb.local -c All

# Blackfield — AS-REP creds
bloodhound-python -u 'support' -p '#00^BlackKnight' -ns $ip -d BLACKFIELD.local -c All

Gotchas

  • -ns is not optional in practice. Without it, DNS resolution fails because your Kali box doesn’t know how to resolve the domain. Always set it to the DC IP.
  • -c All is the right default. Some guides say to use -c DCOnly for stealth. For CTF boxes and OSCP, just grab everything.
  • Domain name matters. Some boxes use domain.htb, others use domain.htb.local, others use domain.local. Get it from nmap LDAP output or nxc smb output. Wrong domain name = collection fails silently or returns incomplete data.
  • If passwords have special characters ($, !, #), use single quotes in bash.

Importing Data into BloodHound CE

  1. Log into BloodHound CE at http://localhost:8080
  2. Click the Upload button (top right, arrow icon)
  3. Select all the JSON files from bloodhound-python
  4. Wait for ingestion to complete

That’s it. The graph database is populated. Time to query.


Queries That Matter

These are the queries I actually run. In order.

1. Shortest Path to Domain Admin

This is the first thing I check. Every time.

In BloodHound CE:

  • Go to ExplorePathfinding
  • Start node: your owned user
  • End node: DOMAIN ADMINS@DOMAIN.LOCAL
  • Hit search

On Forest, this showed:

svc-alfresco → Service Accounts → Privileged IT Accounts → Account Operators
→ GenericAll over Exchange Windows Permissions
→ WriteDACL over HTB.LOCAL domain

Five groups deep. Invisible without BloodHound.

2. Kerberoastable Users

MATCH (u:User) WHERE u.hasspn=true RETURN u

Or use the built-in “List all Kerberoastable Users” query in the Analysis tab.

On Active, this showed the Administrator account had SPN active/CIFS:445. That’s unusual — normally you Kerberoast service accounts, not the actual Administrator. But the SPN was there, and the password was weak.

3. AS-REP Roastable Users

MATCH (u:User) WHERE u.dontreqpreauth=true RETURN u

Or the built-in “List all AS-REP Roastable Users” query.

Found svc-alfresco on Forest and support on Blackfield this way.

4. Users with DCSync Rights

MATCH p=(u)-[:MemberOf|GetChanges|GetChangesAll*1..]->(d:Domain) RETURN p

Shows who can already DCSync without any privilege escalation. If you own one of these accounts, go straight to impacket-secretsdump.

5. Find Principals with Dangerous Rights

Look at the Analysis tab for pre-built queries:

  • Shortest path from owned principals
  • Find all paths to Domain Admins
  • Users with local admin rights
  • Group memberships for a specific user

Understanding the Graph — Edge Types That Matter

When you see a path in BloodHound, the edges (arrows between nodes) tell you what kind of access you have. These are the ones I’ve actually exploited:

GenericAll

Full control over the target object. You can:

  • Reset their password
  • Add them to groups
  • Modify their attributes
  • Set an SPN on them (targeted Kerberoasting)

On Forest, svc-alfresco’s group chain gave GenericAll over Exchange Windows Permissions. That meant I could add myself to that group.

WriteDACL

Can modify the access control list of the target object. This is the big one. WriteDACL on a domain object means you can grant yourself any permission — including DCSync rights.

On Forest, Exchange Windows Permissions had WriteDACL over the HTB.LOCAL domain. After adding myself to that group, I used PowerView to grant DCSync rights:

Add-DomainObjectAcl -TargetIdentity "DC=htb,DC=local" -PrincipalIdentity svc-alfresco -Rights DCSync

Then impacket-secretsdump pulled every hash in the domain.

ForceChangePassword

Can change the target user’s password without knowing the current one. Done via rpcclient:

rpcclient -U 'support%#00^BlackKnight' $ip
rpcclient $> setuserinfo2 audit2020 23 'Password@1234'

On Blackfield, support had ForceChangePassword over audit2020. Changed the password, used audit2020’s access to the forensic share, grabbed the LSASS dump.

Other Edges Worth Knowing

EdgeWhat It MeansExploitation
GenericWriteCan modify attributesSet SPN → Kerberoast, or set logon script
WriteOwnerCan take ownershipTake ownership → give yourself GenericAll
AddMemberCan add users to the groupAdd yourself or a compromised user
ReadLAPSPasswordCan read LAPS passwordsGet local admin password for that machine
AllowedToDelegateKerberos delegationConstrained/unconstrained delegation attacks

Attack Paths I’ve Seen

Path 1: Nested Group Abuse → DCSync (Forest)

Owned User → Group → Group → Group → GenericAll → Target Group → WriteDACL → Domain

The play: Add yourself to the group that has WriteDACL. Use WriteDACL to grant yourself DCSync rights. Run secretsdump.

Path 2: ForceChangePassword → Lateral Movement (Blackfield)

Owned User → ForceChangePassword → Target User → Access to new resources

The play: Change the target’s password. Log in as them. See what they have access to that you didn’t.

Path 3: Direct Kerberoasting (Active)

Owned User → GetUserSPNs → Administrator has SPN → Crack TGS → Domain Admin

The play: BloodHound shows which high-value accounts have SPNs. If Administrator or another DA account has one, Kerberoast it.


My BloodHound Workflow

Every AD box, same process:

  1. Get any valid creds — null session, AS-REP Roast, creds in files, password spray, whatever
  2. Run bloodhound-python with -c All
  3. Upload JSON files to BloodHound CE
  4. Mark your user as owned — right-click → Mark as Owned
  5. Run pathfinding — owned user → Domain Admins
  6. Check pre-built queries — Kerberoastable, AS-REP Roastable, DCSync rights
  7. Walk the path BloodHound shows you

Step 5 is where the magic happens. BloodHound does the graph traversal across every group membership, ACL, and delegation in the domain. It finds paths you would never find manually.

The truth: I have never solved an AD box without BloodHound showing me something I missed. On Forest, the path was through 5 nested groups. On Blackfield, it showed the ForceChangePassword edge. On Active, it confirmed the Administrator SPN. BloodHound is not optional. It’s the map.