Skip to content

BloodHound Analysis

BloodHound & Cypher Queries Cheatsheet

Installation & Setup

BloodHound Installation

# Install Neo4j (for old BloodHound)
wget -O - https://debian.neo4j.com/neotechnology.gpg.key | sudo apt-key add -
echo 'deb https://debian.neo4j.com stable 4.0' | sudo tee /etc/apt/sources.list.d/neo4j.list
sudo apt update
sudo apt install neo4j

# Download BloodHound
wget https://github.com/BloodHoundAD/BloodHound/releases/download/4.3.1/BloodHound-linux-x64.zip
unzip BloodHound-linux-x64.zip

# Start Neo4j
sudo neo4j console
# Default creds: neo4j:neo4j (will force password change)

# Run BloodHound
./BloodHound --no-sandbox

BloodHound Community Edition (CE) Installation

# Using Docker
curl -L https://ghst.ly/bloodhoundcommunityedition | docker compose -f - up

# Access at https://localhost:8080
# Default creds: admin@bloodhound / admin (change on first login)

Data Collection

SharpHound.exe (Windows)

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

# Specific collection methods
.\SharpHound.exe -c DCOnly
.\SharpHound.exe -c Session
.\SharpHound.exe -c LoggedOn
.\SharpHound.exe -c ObjectProps
.\SharpHound.exe -c ACL
.\SharpHound.exe -c Trusts
.\SharpHound.exe -c Container
.\SharpHound.exe -c Group
.\SharpHound.exe -c GPOLocalGroup
.\SharpHound.exe -c PSRemote
.\SharpHound.exe -c DCOM
.\SharpHound.exe -c RDP
.\SharpHound.exe -c LocalAdmin

# Stealth options
.\SharpHound.exe -c All --stealth
.\SharpHound.exe -c All --throttle 100 --jitter 15

# Loop collection (for sessions)
.\SharpHound.exe -c Session --loop --loopduration 2:00:00

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

# Output options
.\SharpHound.exe -c All --outputdirectory C:\temp --zipfilename data.zip

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

# LDAP options
.\SharpHound.exe -c All --ldapusername user --ldappassword pass

SharpHound.ps1 (PowerShell)

# Import module
Import-Module .\SharpHound.ps1

# Invoke collection
Invoke-BloodHound -CollectionMethod All

# With specific methods
Invoke-BloodHound -CollectionMethod DCOnly,Session,ACL

# Stealth and timing
Invoke-BloodHound -CollectionMethod All -Stealth -Throttle 100 -Jitter 15

# Loop for sessions
Invoke-BloodHound -CollectionMethod Session -Loop -LoopDuration 02:00:00 -LoopDelay 00:05:00

# Specify output
Invoke-BloodHound -CollectionMethod All -OutputDirectory C:\temp -ZipFileName bloodhound.zip

bloodhound.py (Linux)

# Install
pip install bloodhound

# Basic collection with password
bloodhound-python -d domain.local -u username -p password -c all -ns $dc_ip

# Collection with hash
bloodhound-python -d domain.local -u username --hashes :$ntlm_hash -c all -ns $dc_ip

# With Kerberos
bloodhound-python -d domain.local -u username -k -c all -ns $dc_ip

# Specific collection methods
bloodhound-python -d domain.local -u username -p password -c group,localadmin,session,trusts -ns $dc_ip

# With DNS TCP
bloodhound-python -d domain.local -u username -p password -c all -ns $dc_ip --dns-tcp

# Disable autogc (garbage collection)
bloodhound-python -d domain.local -u username -p password -c all -ns $dc_ip --disable-autogc

# With zip output
bloodhound-python -d domain.local -u username -p password -c all -ns $dc_ip --zip

AzureHound (Azure/AzureAD)

# Collection for Azure
azurehound -u "user@tenant.onmicrosoft.com" -p "password" list --tenant "tenant.onmicrosoft.com" -o output.json

# With refresh token
azurehound refresh-token -r $refresh_token list -o output.json

# Specific collections
azurehound -u "user@tenant.onmicrosoft.com" -p "password" list users -o users.json
azurehound -u "user@tenant.onmicrosoft.com" -p "password" list groups -o groups.json

Neo4j Cypher Queries (Old BloodHound)

User Queries

Find All Domain Admins

MATCH (n:Group) WHERE n.name =~ '(?i).*domain admins.*' WITH n MATCH (n)<-[r:MemberOf*1..]-(m) RETURN m

Find Specific User

MATCH (n:User) WHERE n.name =~ '(?i)username@domain.local' RETURN n

Show All Properties of a User

MATCH (n:User) WHERE n.name =~ '(?i)username@domain.local' RETURN n

Find All Logged On Users

MATCH p=(c:Computer)-[:HasSession]->(u:User) RETURN p

Find Users with DCSync Rights

MATCH p=(u:User)-[:DCSync]->(d:Domain) RETURN p

Find Kerberoastable Users

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

Find ASREPRoastable Users

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

Find Users with Password Never Expires

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

Find Users with Password Not Required

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

Find Enabled Users

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

Find Disabled Users

MATCH (u:User) WHERE u.enabled=false RETURN u

Find Admin Users

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

Computer Queries

Find All Computers

MATCH (c:Computer) RETURN c

Find Domain Controllers

MATCH (c:Computer)-[:MemberOf*1..]->(g:Group) WHERE g.name =~ '(?i).*domain controllers.*' RETURN c

Find Computers with Unconstrained Delegation

MATCH (c:Computer) WHERE c.unconstraineddelegation=true AND NOT c.name =~ '(?i).*DC.*' RETURN c

Find Computers with Constrained Delegation

MATCH (c:Computer) WHERE c.allowedtodelegate IS NOT NULL RETURN c

Find Computers Allow to Delegate

MATCH (c:Computer) WHERE c.allowedtodelegate IS NOT NULL RETURN c

Find Computers with LAPS

MATCH (c:Computer) WHERE c.haslaps=true RETURN c

Find Computers without LAPS

MATCH (c:Computer) WHERE c.haslaps=false RETURN c

Find Computers with Sessions

MATCH (c:Computer)-[:HasSession]->(u:User) RETURN c,u

Path Queries

Shortest Path from User to Domain Admin

MATCH p=shortestPath((u:User {name:'username@domain.local'})-[*1..]->(g:Group {name:'DOMAIN ADMINS@DOMAIN.LOCAL'})) RETURN p

All Paths from User to High Value Targets

MATCH p=allShortestPaths((u:User {name:'username@domain.local'})-[*1..]->(h)) WHERE h.highvalue=true RETURN p

Find Shortest Path to Domain Admin from Any Owned User

MATCH p=shortestPath((u:User)-[*1..]->(g:Group)) WHERE u.owned=true AND g.name =~ '(?i).*domain admins.*' RETURN p

Find All Paths from Owned Users to High Value

MATCH p=allShortestPaths((u:User)-[*1..]->(h)) WHERE u.owned=true AND h.highvalue=true RETURN p

Find Shortest Path from Kerberoastable Users

MATCH p=shortestPath((u:User)-[*1..]->(g:Group)) WHERE u.hasspn=true AND g.name =~ '(?i).*domain admins.*' RETURN p

Path from Specific User to Any Goal

MATCH p=shortestPath((n)-[*1..]->(c)) WHERE n.name =~ '(?i)username@domain.local' AND NOT c=n RETURN p

Group Queries

Find All Groups

MATCH (g:Group) RETURN g

Find High Value Groups

MATCH (g:Group) WHERE g.highvalue=true RETURN g

Find Groups with Most Members

MATCH (g:Group)<-[:MemberOf]-(u) RETURN g.name, COUNT(u) ORDER BY COUNT(u) DESC

Find Nested Group Memberships

MATCH p=(u:User)-[:MemberOf*1..5]->(g:Group) WHERE u.name =~ '(?i)username@domain.local' RETURN p

Find Groups that Can RDP

MATCH p=(g:Group)-[:CanRDP]->(c:Computer) RETURN p

Find Groups that Can PSRemote

MATCH p=(g:Group)-[:CanPSRemote]->(c:Computer) RETURN p

Find Groups with Local Admin Rights

MATCH p=(g:Group)-[:AdminTo]->(c:Computer) RETURN p

ACL Queries

Find All GenericAll Rights

MATCH p=(u)-[:GenericAll]->(c) RETURN p

Find GenericAll Rights on Domain

MATCH p=(u)-[:GenericAll]->(d:Domain) RETURN p

Find WriteDACL Rights

MATCH p=(u)-[:WriteDacl]->(c) RETURN p

Find WriteOwner Rights

MATCH p=(u)-[:WriteOwner]->(c) RETURN p

Find Users with ForceChangePassword

MATCH p=(u)-[:ForceChangePassword]->(c:User) RETURN p

Find AddMembers Rights

MATCH p=(u)-[:AddMember]->(g:Group) RETURN p

Find GenericWrite Rights

MATCH p=(u)-[:GenericWrite]->(c) RETURN p

Find Owns Relationships

MATCH p=(u)-[:Owns]->(c) RETURN p

Find All ACL Rights from Owned Users

MATCH p=(u:User)-[r]->(n) WHERE u.owned=true AND r.isacl=true RETURN p

Find Objects Controllable by Owned Users

MATCH p=(u:User {owned:true})-[r]->(n) WHERE r.isacl=true RETURN p

GPO Queries

Find All GPOs

MATCH (g:GPO) RETURN g

Find GPO Affecting Computers

MATCH p=(g:GPO)-[:GPLink]->(c:Computer) RETURN p

Find GPO Affecting OUs

MATCH p=(g:GPO)-[:GPLink]->(o:OU) RETURN p

Find Users that Can Modify GPOs

MATCH p=(u:User)-[:GenericWrite|WriteProperty|WriteDacl|WriteOwner]->(g:GPO) RETURN p

Computer Admin Queries

Find All Local Admins

MATCH p=(u)-[:AdminTo]->(c:Computer) RETURN p

Find Users that are Local Admin on Computers

MATCH p=(u:User)-[:AdminTo]->(c:Computer) RETURN p

Find Groups that are Local Admin on Computers

MATCH p=(g:Group)-[:AdminTo]->(c:Computer) RETURN p

Find Computers where Domain Users are Admin

MATCH p=(g:Group)-[:AdminTo]->(c:Computer) WHERE g.name =~ '(?i).*domain users.*' RETURN p

Find All RDP Rights

MATCH p=(u)-[:CanRDP]->(c:Computer) RETURN p

Find All PSRemote Rights

MATCH p=(u)-[:CanPSRemote]->(c:Computer) RETURN p

Find All DCOM Rights

MATCH p=(u)-[:ExecuteDCOM]->(c:Computer) RETURN p

Find All SQL Admin Rights

MATCH p=(u)-[:SQLAdmin]->(c:Computer) RETURN p

Delegation Queries

Find All Delegation Relationships

MATCH (u)-[:AllowedToDelegate]->(c) RETURN u,c

Find Users That Can Be Delegated

MATCH (u:User)-[:AllowedToDelegate]->(c:Computer) RETURN u,c

Find Computers That Allow Unconstrained Delegation

MATCH (c:Computer) WHERE c.unconstraineddelegation=true RETURN c

Find Users/Computers Trusted for Delegation

MATCH (n) WHERE n.trustedtoauth=true RETURN n

Session Queries

Find All Sessions

MATCH p=(c:Computer)-[:HasSession]->(u:User) RETURN p

Find Privileged Sessions

MATCH p=(c:Computer)-[:HasSession]->(u:User)-[:MemberOf*1..]->(g:Group) WHERE g.highvalue=true RETURN p

Find Sessions of Domain Admins

MATCH p=(c:Computer)-[:HasSession]->(u:User)-[:MemberOf*1..]->(g:Group) WHERE g.name =~ '(?i).*domain admins.*' RETURN p

Find Computers with Most Sessions

MATCH (c:Computer)-[:HasSession]->(u:User) RETURN c.name, COUNT(u) ORDER BY COUNT(u) DESC

Trust Queries

Find All Trusts

MATCH (d:Domain)-[:TrustedBy]->(d2:Domain) RETURN d,d2

Find External Trusts

MATCH (d:Domain)-[r:TrustedBy]->(d2:Domain) WHERE r.trusttype = 'External' RETURN d,d2

Find Transitive Trusts

MATCH (d:Domain)-[r:TrustedBy]->(d2:Domain) WHERE r.transitive=true RETURN d,d2

Special Queries

Find All Owned Users

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

Find All Owned Computers

MATCH (c:Computer) WHERE c.owned=true RETURN c

Find All High Value Targets

MATCH (h) WHERE h.highvalue=true RETURN h

Find Principals with Most Admin Rights

MATCH (u)-[:AdminTo]->(c:Computer) RETURN u.name, COUNT(c) ORDER BY COUNT(c) DESC

Find Users that Can DCSync

MATCH p=(u:User)-[:DCSync|GetChanges|GetChangesAll]->(d:Domain) RETURN p

Find Objects with Most Outbound Control

MATCH (u)-[r]->(n) WHERE r.isacl=true RETURN u.name, COUNT(n) ORDER BY COUNT(n) DESC

Find Objects with Most Inbound Control

MATCH (u)-[r]->(n) WHERE r.isacl=true RETURN n.name, COUNT(u) ORDER BY COUNT(u) DESC

Find Kerberoastable Users with Admin Rights

MATCH p=(u:User)-[:AdminTo]->(c:Computer) WHERE u.hasspn=true RETURN p

Find ASREP Roastable Users with Admin Rights

MATCH p=(u:User)-[:AdminTo]->(c:Computer) WHERE u.dontreqpreauth=true RETURN p

Find Computers in Specific OU

MATCH p=(c:Computer)-[:Contains*1..]->(o:OU) WHERE o.name =~ '(?i).*servers.*' RETURN p

Find All Certificate Templates

MATCH (ct:CertTemplate) RETURN ct

Find Enrollable Certificate Templates

MATCH p=(u:User)-[:Enroll]->(ct:CertTemplate) RETURN p

Azure Queries (If Azure data imported)

Find Azure Admin Roles

MATCH p=(u:AZUser)-[:AZGlobalAdmin|AZPrivilegedRoleAdmin]->(t:AZTenant) RETURN p

Find Azure Apps with Permissions

MATCH p=(a:AZApp)-[r]->(n) RETURN p

Find Azure Service Principals

MATCH (sp:AZServicePrincipal) RETURN sp

Custom/Advanced Queries

Find All Paths Less Than X Length

MATCH p=shortestPath((u:User)-[*1..5]->(g:Group)) WHERE u.owned=true AND g.highvalue=true AND length(p) <= 5 RETURN p

Find Cross-Domain Paths

MATCH p=(u:User)-[*1..]->(g:Group) WHERE u.domain <> g.domain RETURN p

Find Users Never Logged In

MATCH (u:User) WHERE NOT (u)-[:HasSession]->(:Computer) RETURN u

Count All Relationships

MATCH ()-[r]->() RETURN type(r), COUNT(r) ORDER BY COUNT(r) DESC

Delete All Owned Markers

MATCH (n) WHERE n.owned=true SET n.owned=false

Mark Node as Owned

MATCH (n) WHERE n.name =~ '(?i)username@domain.local' SET n.owned=true

Mark Node as High Value

MATCH (n) WHERE n.name =~ '(?i)sensitive_server.domain.local' SET n.highvalue=true

Find All Edges from a Node

MATCH (n)-[r]->(m) WHERE n.name =~ '(?i)username@domain.local' RETURN n,r,m

Find All Edges to a Node

MATCH (n)-[r]->(m) WHERE m.name =~ '(?i)domain admins@domain.local' RETURN n,r,m

Export All User Names

MATCH (u:User) RETURN u.name

Export All Computer Names

MATCH (c:Computer) RETURN c.name

Complex Attack Path Query

MATCH p=allShortestPaths((u:User {owned:true})-[*1..]->(g:Group {name:'DOMAIN ADMINS@DOMAIN.LOCAL'})) 
WHERE NONE(r in relationships(p) WHERE type(r) = 'HasSession') 
AND NONE(n in nodes(p) WHERE n:Group AND n.name =~ '(?i).*denied.*')
RETURN p

BloodHound Tips & Tricks

Pre-Built Analytics Queries

- Find all Domain Admins
- Find Shortest Paths to Domain Admins
- Find Principals with DCSync Rights
- Users with Foreign Domain Group Membership
- Groups with Foreign Domain Group Membership
- Map Domain Trusts
- Shortest Paths to Unconstrained Delegation Systems
- Shortest Paths from Kerberoastable Users
- Shortest Paths to Domain Admins from Kerberoastable Users
- Shortest Path from Owned Principals
- Shortest Paths to Domain Admins from Owned Principals
- Shortest Paths to High Value Targets
- Find Computers with Unsupported Operating Systems
- Find AS-REP Roastable Users (DontReqPreAuth)

Marking Nodes

# Mark as Owned
MATCH (n) WHERE n.name =~ '(?i)username@domain.local' SET n.owned=true RETURN n

# Mark as High Value
MATCH (n) WHERE n.name =~ '(?i)target@domain.local' SET n.highvalue=true RETURN n

# Remove Owned flag
MATCH (n) WHERE n.name =~ '(?i)username@domain.local' SET n.owned=false RETURN n

# Bulk mark owned from file (in Neo4j browser)
LOAD CSV FROM 'file:///owned.csv' AS line 
MATCH (n) WHERE n.name = line[0] SET n.owned=true

Performance Tips

# Create indexes for better performance
CREATE INDEX ON :User(name)
CREATE INDEX ON :Computer(name)
CREATE INDEX ON :Group(name)
CREATE INDEX ON :Domain(name)

# Check existing indexes
CALL db.indexes()

# Get database statistics
CALL db.stats.retrieve('GRAPH COUNTS')

Export/Import

# Export database
sudo neo4j-admin dump --database=neo4j --to=/backup/bloodhound.dump

# Import database
sudo neo4j-admin load --database=neo4j --from=/backup/bloodhound.dump --force

# Export query results to CSV (in Neo4j)
MATCH (u:User) WHERE u.owned=true RETURN u.name
# Then click export to CSV

Important Notes

Query Case Sensitivity:

  • Use =~ for regex matching
  • Use (?i) for case-insensitive matching
  • Example: WHERE n.name =~ '(?i).*admin.*'

Common Node Types:

  • User
  • Computer
  • Group
  • Domain
  • GPO
  • OU
  • Container
  • CertTemplate
  • AZUser (Azure)
  • AZGroup (Azure)
  • AZTenant (Azure)

Common Relationship Types:

  • MemberOf
  • AdminTo
  • HasSession
  • TrustedBy
  • Contains
  • GPLink
  • CanRDP
  • CanPSRemote
  • ExecuteDCOM
  • AllowedToDelegate
  • ForceChangePassword
  • GenericAll
  • GenericWrite
  • Owns
  • WriteDacl
  • WriteOwner
  • ReadLAPSPassword
  • ReadGMSAPassword
  • AddMember
  • DCSync

Edge Properties:

  • isacl: Boolean indicating if edge is an ACL
  • isinhertied: Boolean for inherited ACLs
  • transitive: Boolean for trust transitivity

Node Properties:

  • name: Full object name
  • domain: Domain name
  • enabled: Account status
  • highvalue: High value target flag
  • owned: Compromised flag
  • hasspn: Has SPN (Kerberoastable)
  • dontreqpreauth: ASREP roastable
  • unconstraineddelegation: Unconstrained delegation
  • trustedtoauth: Trusted for delegation
  • admincount: Protected admin object
  • description: Object description