BloodHound

The BloodHound cheat sheet covers AD attack path analysis, data collection with SharpHound/BloodHound-Python, Cypher query builder and library, 80+ edge definitions (Core AD, ADCS, cross-domain, coercion, Azure), BloodHound CE API usage, and operational workflows.

#Getting Started

#Quick Start: Zero to DA in 5 Commands

# 1. Start BloodHound CE
curl -L https://ghst.ly/getbhce | docker compose -f - up

# 2. Collect data (from Windows, domain-joined)
SharpHound.exe -c All --zipfilename bh_$(hostname).zip

# 3. Collect from Linux (network access to DC)
bloodhound-python -u user -p 'P@ss' -d corp.local -ns 10.10.10.10 -c All --zip

# 4. Upload via UI or API
curl -s -X POST http://localhost:8080/api/v2/file-upload \
  -H "Authorization: Bearer $TOKEN" -F "file=@bh_HOST.zip"

# 5. Mark owned principals, run pre-built path query
# UI: Search node -> right-click -> "Mark as Owned"
# Cypher: MATCH (n {name:"[email protected]"}) SET n.owned=true

#BloodHound CE Installation

# Docker Compose (recommended, pulls everything)
curl -L https://ghst.ly/getbhce | docker compose -f - up

# Retrieve the auto-generated admin password
docker compose logs | grep "Initial Password"
# Access: http://localhost:8080  (admin / <password>)

# Pin to a specific version
curl -L https://ghst.ly/getbhce -o docker-compose.yml
# Edit BLOODHOUND_VERSION in the file, then:
docker compose up -d

#Data Collection Strategy

Scenario Access Level Recommended Command
Domain-joined Windows DA / local admin SharpHound.exe -c All
Domain-joined Windows Low-priv domain user SharpHound.exe -c DCOnly,Group,ACL,Container,Trusts,ObjectProps
Non-joined Windows Have domain creds SharpHound.exe -c All -d corp.local --ldapusername user --ldappassword pass
Linux / external Password creds bloodhound-python -u user -p pass -d corp.local -ns DC_IP -c All
Linux / external NTLM hash bloodhound-python -u user --hashes :NThash -d corp.local -ns DC_IP
Linux / external Kerberos ccache KRB5CCNAME=user.ccache bloodhound-python -k -d corp.local -ns DC_IP -c All --no-pass
Linux / external No creds (unauthenticated) netexec smb 10.10.10.0/24 -u '' -p '' then bloodhound-python with guest/anonymous

#Cypher Query Builder

BloodHound Cypher Query Builder

Query Builder

Relationship Quick Reference

Relationship Meaning Abuse
MemberOf Group membership Inherit group privileges
AdminTo Local admin PSExec, WMI, etc.
HasSession Active session Token theft
DCSync DCSync rights Dump all hashes
GenericAll Full control Any abuse
GenericWrite Write properties Logon script, SPN set
WriteDacl Modify DACL Grant self GenericAll
WriteOwner Change owner Take ownership
ForceChangePassword Reset password Compromise account
AllowedToDelegate Constrained delegation S4U2Proxy
AllowedToAct RBCD Resource-Based CD
ReadLAPSPassword Read LAPS Get local admin creds

#Collectors

#SharpHound: Collection Methods Table

Method What It Collects Noise Level Notes
Default Group membership, domain trusts, local admins, sessions, ACLs, containers Medium Best starting point
All Everything except LoggedOn High Triggers most detections
DCOnly Groups, ACLs, GPOs, OUs, trusts, object properties (LDAP only) Very Low No computer connections, stealth choice
ComputerOnly Sessions, local groups, user rights from domain hosts High Hits every computer
Group AD group membership Low LDAP only
LocalAdmin Local Administrators group on all computers Medium Requires SMB
RDP Remote Desktop Users group Medium Requires SMB
DCOM Distributed COM Users group Medium Requires SMB
PSRemote WinRM/PowerShell Remote users Medium Requires SMB
Session Active user sessions (NetSessionEnum) High Very noisy, blocked by GPO
LoggedOn Privileged sessions via registry (needs local admin) High Requires admin on each host
Trusts Domain/forest trust relationships Low LDAP only
ACL DACL/ACE entries for all AD objects Low LDAP only
Container OUs, containers, GPO links Low LDAP only
ObjectProps LastLogon, PwdLastSet, UAC flags Low LDAP only
GPOLocalGroup Local group membership via GPO Low LDAP only
CARegistry ADCS CA registry settings Low Requires CA access
CertServices Certificate templates, enterprise CAs Low LDAP only
UserRights User rights assignments on computers Medium Requires SMB

#SharpHound: Full Flag Reference

Flag Default Description
-c, --collectionmethods Default Collection methods (comma-separated)
-d, --domain Current Target AD domain
-s, --searchforest false Collect all domains in forest
--stealth false Only touch high-value systems (quieter)
--computerfile - File with computer names/IPs to target
--distinguishedname - Limit search to base DN
-f, --ldapfilter - Filter collected principals by LDAP query
--excludedcs false Skip domain controllers
--collectallproperties false Collect all string LDAP properties
--outputdirectory . Output folder for ZIP/JSON
--outputprefix - Prefix for output filenames
--nozip false Don't compress output files
--zipfilename auto Custom ZIP filename
--zippassword - Encrypt ZIP with password
--randomfilenames false Randomize output file names (OPSEC)
--loop false Repeat computer collection
--loopduration 02:00:00 Total loop time (HH:MM:SS)
--loopinterval 00:05:00 Wait between loops (HH:MM:SS)
--domaincontroller auto Target specific DC (IP or hostname)
--ldapport 389 Custom LDAP port
--secureldap false Use LDAPS (port 636)
--ldapusername - Alternate LDAP username
--ldappassword - Alternate LDAP password
--disablesigning false Disable Kerberos signing/sealing
--disablecertverification false Disable LDAPS cert check
--dolocaladminsessionenum false Use local admin creds for sessions
--throttle 0 Millisecond delay per computer request
--jitter 0 % variation added to throttle
--threads 50 Enumeration thread count
--portchecktimeout 2000 Port 445 check timeout (ms)
--skipportcheck false Skip port 445 pre-check
--skipregistryloggedon false Skip registry session enum
--memcache false No cache file written to disk (OPSEC)
--randomfilenames false Randomize cache/output names
--verbosity 0 Log verbosity (0-2)

#SharpHound: Stealth & OPSEC Examples

# Quietest: LDAP only, no computer connections, randomized filenames
SharpHound.exe -c DCOnly --memcache --randomfilenames --outputdirectory C:\Windows\Temp

# Low-noise: add local group and ACL, still mostly LDAP
SharpHound.exe -c DCOnly,LocalAdmin,ACL,Container,ObjectProps,Trusts

# Session loop (for catching DA logons), throttled, jittered
SharpHound.exe -c Session --loop --loopduration 04:00:00 --loopinterval 00:15:00 --throttle 2000 --jitter 25

# Target single OU with specific creds
SharpHound.exe -c All --distinguishedname "OU=Servers,DC=corp,DC=local" --ldapusername svc_scan --ldappassword 'P@ss!'

# Use encrypted ZIP for exfil
SharpHound.exe -c All --zipfilename results.zip --zippassword 'S3cur3!'

# Domain-joined context with alternate creds (runas scenario)
SharpHound.exe -c All -d otherdomain.local --ldapusername domuser --ldappassword pass --domaincontroller 10.10.20.5

# Enumerate specific computers from file
SharpHound.exe -c ComputerOnly --computerfile C:\hosts.txt --skipportcheck

#BloodHound-Python: Full Options

# CE version (BloodHound 5.x+)
pip install bloodhound-ce
bloodhound-ce-python -u user -p 'pass' -d corp.local -ns 10.10.10.10 -c All

# Legacy version
pip install bloodhound
bloodhound-python -u user -p 'pass' -d corp.local -ns 10.10.10.10 -c All

# NTLM hash auth
bloodhound-python -u user --hashes 'aad3b435b51404eeaad3b435b51404ee:NThash' \
  -d corp.local -ns 10.10.10.10 -c All

# Kerberos auth via ccache
KRB5CCNAME=/tmp/user.ccache bloodhound-python -u user -d corp.local \
  -ns 10.10.10.10 -c All -k --no-pass

# AES key auth
bloodhound-python -u user --aeskey <aes256key> -d corp.local -ns 10.10.10.10 -c All -k

# DNS via TCP (useful when UDP is blocked)
bloodhound-python -u user -p pass -d corp.local -ns 10.10.10.10 -c All --dns-tcp

# Auth method selection (auto, ntlm, kerberos)
bloodhound-python -u user -p pass -d corp.local -ns 10.10.10.10 --auth-method ntlm -c All

# Global Catalog server for multi-domain
bloodhound-python -u user -p pass -d corp.local -ns 10.10.10.10 -gc gc.corp.local -c All

# Zip output, custom directory, workers
bloodhound-python -u user -p pass -d corp.local -ns 10.10.10.10 \
  -c All --zip -o /tmp/bh/ -w 5

# Stealth: DCOnly equivalent
bloodhound-python -u user -p pass -d corp.local -ns 10.10.10.10 \
  -c DCOnly,Group,ACL,Container,Trusts,ObjectProps

# Specific collection methods
bloodhound-python -u user -p pass -d corp.local -ns 10.10.10.10 \
  -c Group,LocalAdmin,ACL,Session,Trusts,ObjectProps,Container
Flag Description
-u Username (user or user@domain)
-p Password
--hashes NTLM hash (LM:NT or :NT)
-k Use Kerberos authentication
--no-pass Don't prompt for password (use with -k)
--aeskey AES key for Kerberos
-d Domain name (required)
-ns DNS server / nameserver (DC IP)
-dc Specific DC hostname override
-gc Global Catalog server
-c Collection methods (comma-separated)
--auth-method Force auth: auto, ntlm, kerberos
--dns-tcp Use TCP for DNS (bypass UDP blocks)
--zip Compress output into ZIP
-o Output directory
-w Worker thread count (default 10)
--exclude-dcs Skip domain controllers
-v Verbosity

#AzureHound: Entra ID / Azure Collection

# Download AzureHound
wget https://github.com/SpecterOps/azurehound/releases/latest/download/azurehound-linux-amd64.zip
unzip azurehound-linux-amd64.zip && chmod +x azurehound

# --- Authentication Methods ---

# Username + Password (MFA-less accounts only)
./azurehound list -u "[email protected]" -p 'P@ssw0rd' -t "TENANT_ID" -o azure.json

# Service Principal (App registration with secret)
./azurehound list --app "$APP_ID" --secret "$APP_SECRET" -t "$TENANT_ID" -o azure.json

# Service Principal with certificate
./azurehound list --app "$APP_ID" --cert "$CERT_PATH" --key "$KEY_PATH" -t "$TENANT_ID" -o azure.json

# JWT token (from az cli or manual acquisition)
JWT=$(az account get-access-token --resource https://graph.microsoft.com | jq -r .accessToken)
./azurehound list --jwt "$JWT" -t "$TENANT_ID" -o azure.json

# Refresh token
./azurehound list -r "$REFRESH_TOKEN" -t "$TENANT_ID" -o azure.json

# List specific object types
./azurehound list users -t "$TENANT_ID" -o users.json
./azurehound list groups -t "$TENANT_ID" -o groups.json
./azurehound list service-principals -t "$TENANT_ID" -o sps.json
./azurehound list subscriptions -t "$TENANT_ID" -o subs.json

# Via proxy
./azurehound list --proxy "http://127.0.0.1:8080" -t "$TENANT_ID" -o azure.json

# BloodHound Enterprise mode (streaming)
./azurehound configure   # Set BHE URL + API key
./azurehound start       # Stream to BHE continuously
Flag Description
-t, --tenant Tenant ID or domain
-u Username (UPN)
-p Password
--app App/client ID
--secret Client secret
--cert Certificate file path
--key Private key file path
-j, --jwt Pre-acquired JWT access token
-r, --refresh-token Refresh token
-o Output file (JSON)
--proxy Proxy URL
--log-file Log output to file
-v, --verbosity Verbosity (-1 to 2)

#Cypher Query Library

#Attack Paths: DA, EA, Schema Admins

// Shortest path to Domain Admins from owned principals
MATCH p=shortestPath((n {owned:true})-[*1..]->(g:Group))
WHERE g.objectid ENDS WITH '-512' AND n<>g
RETURN p

// Shortest path to Enterprise Admins (EA)
MATCH p=shortestPath((n {owned:true})-[*1..]->(g:Group))
WHERE g.objectid ENDS WITH '-519' AND n<>g
RETURN p

// Paths to Schema Admins
MATCH p=shortestPath((n {owned:true})-[*1..]->(g:Group))
WHERE g.objectid ENDS WITH '-518' AND n<>g
RETURN p

// Paths to Account Operators
MATCH p=shortestPath((n {owned:true})-[*1..]->(g:Group))
WHERE g.objectid ENDS WITH '-548' AND n<>g
RETURN p

// All users with ANY path to DA (show count, sorted)
MATCH (u:User), (g:Group) WHERE g.objectid ENDS WITH '-512'
MATCH p=shortestPath((u)-[*1..]->(g))
RETURN u.name, length(p) AS hops ORDER BY hops ASC LIMIT 50

// Non-admin users with path to DA (find weak links)
MATCH (u:User {admincount:false, enabled:true})
MATCH (g:Group) WHERE g.objectid ENDS WITH '-512'
MATCH p=shortestPath((u)-[*1..10]->(g))
RETURN u.name, length(p) AS hops ORDER BY hops ASC LIMIT 25

// All paths to DA, limit 3 hops (faster for large graphs)
MATCH (g:Group) WHERE g.objectid ENDS WITH '-512'
MATCH p=(n)-[*1..3]->(g) WHERE NOT n=g
RETURN p LIMIT 20

#ACL Abuse Paths

// Find owned principals with WriteDacl, WriteOwner, GenericAll, GenericWrite on any node
MATCH p=(n {owned:true})-[:WriteDacl|WriteOwner|GenericAll|GenericWrite|Owns]->(m)
RETURN p LIMIT 50

// Find users with GenericAll over Domain Admins group
MATCH p=(u)-[:GenericAll]->(g:Group) WHERE g.objectid ENDS WITH '-512' RETURN p

// Find principals with DCSync rights (three relevant edges)
MATCH p=(n)-[:DCSync|GetChangesAll|AllExtendedRights]->(d:Domain) RETURN p

// Find ForceChangePassword edges to enabled users
MATCH p=(n)-[:ForceChangePassword]->(u:User {enabled:true}) RETURN p

// AddKeyCredentialLink (Shadow Credentials)
MATCH p=(n)-[:AddKeyCredentialLink]->(m) RETURN p

// WriteSPN edges (set-SPN for targeted Kerberoasting)
MATCH p=(n)-[:WriteSPN]->(u:User) RETURN p

// WriteAccountRestrictions (for RBCD abuse)
MATCH p=(n)-[:WriteAccountRestrictions]->(m:Computer) RETURN p

// AllExtendedRights on user objects (can change password / read LAPS)
MATCH p=(n)-[:AllExtendedRights]->(u:User) WHERE n<>u RETURN p

// AddMember rights to high-value groups
MATCH p=(n)-[:AddMember|AddSelf]->(g:Group {highvalue:true}) RETURN p

// WriteDacl on domain objects (dangerous)
MATCH p=(n)-[:WriteDacl]->(d:Domain) RETURN p

// Full ACL abuse chain via any combination
MATCH p=shortestPath((n {owned:true})-[:MemberOf|GenericAll|GenericWrite|WriteDacl|WriteOwner|ForceChangePassword|Owns|AllExtendedRights|AddMember|AddKeyCredentialLink*1..]->(g:Group))
WHERE g.objectid ENDS WITH '-512' AND n<>g
RETURN p LIMIT 10

#Kerberos Attack Paths

// All Kerberoastable users (exclude krbtgt)
MATCH (u:User {hasspn:true, enabled:true})
WHERE NOT u.name STARTS WITH 'KRBTGT'
RETURN u.name, u.serviceprincipalnames, u.pwdlastset ORDER BY u.pwdlastset ASC

// Kerberoastable users with path to DA
MATCH (u:User {hasspn:true})
MATCH (g:Group) WHERE g.objectid ENDS WITH '-512'
MATCH p=shortestPath((u)-[*1..]->(g))
RETURN u.name, length(p) AS hops ORDER BY hops ASC

// Kerberoastable users who are local admin somewhere
MATCH (u:User {hasspn:true})-[:AdminTo|MemberOf*1..]->(:Group)-[:AdminTo]->(c:Computer)
RETURN u.name, c.name

// AS-REP Roastable users
MATCH (u:User {dontreqpreauth:true, enabled:true}) RETURN u.name

// Unconstrained delegation (computers, excluding DCs)
MATCH (c:Computer {unconstraineddelegation:true})
WHERE NOT EXISTS {
  MATCH (c)-[:MemberOf*1..]->(g:Group) WHERE g.objectid ENDS WITH '-516'
}
RETURN c.name, c.operatingsystem

// Constrained delegation (users and computers)
MATCH (n) WHERE n.allowedtodelegate IS NOT NULL AND n.allowedtodelegate <> []
RETURN labels(n)[0] AS type, n.name, n.allowedtodelegate

// Resource-based constrained delegation (RBCD)
MATCH p=(n)-[:AllowedToAct]->(m:Computer) RETURN p

// Shortest path to unconstrained delegation systems from owned
MATCH p=shortestPath((n {owned:true})-[*1..]->(c:Computer {unconstraineddelegation:true}))
WHERE NOT n=c RETURN p

#ADCS Attack Paths

// Find all ADCS ESC1 attack paths
MATCH p=()-[:ADCSESC1]->() RETURN p

// Find all ADCS ESC3 attack paths
MATCH p=()-[:ADCSESC3]->() RETURN p

// Find all ADCS ESC4 attack paths (template write control)
MATCH p=()-[:ADCSESC4]->() RETURN p

// Find all ADCS ESC6a/6b (EDITF_ATTRIBUTESUBJECTALTNAME2)
MATCH p=()-[:ADCSESC6a|ADCSESC6b]->() RETURN p

// Find all ADCS ESC9a/9b
MATCH p=()-[:ADCSESC9a|ADCSESC9b]->() RETURN p

// Find all ADCS ESC10a/10b
MATCH p=()-[:ADCSESC10a|ADCSESC10b]->() RETURN p

// Find all ADCS ESC13 paths
MATCH p=()-[:ADCSESC13]->() RETURN p

// Find all ESC paths combined
MATCH p=()-[r:ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13]->()
RETURN type(r) AS esc_type, startNode(p).name AS attacker, endNode(p).name AS target

// Who has enrollment rights on published templates?
MATCH p=(n)-[:Enroll|AllExtendedRights|GenericAll]->(t:CertTemplate)-[:PublishedTo]->(ca:EnterpriseCA)
RETURN n.name, t.name, ca.name

// Find templates with enrollee-supplied SAN (ESC1 indicator)
MATCH (t:CertTemplate) WHERE t.enrolleesuppliessubject = true
MATCH p=(t)-[:PublishedTo]->(:EnterpriseCA)
RETURN t.name, t.requiresmanagerapproval, t.authenticationenabled

// Find ManageCA / ManageCertificates rights (ESC7)
MATCH p=(n)-[:ManageCA|ManageCertificates]->(ca:EnterpriseCA) RETURN p

// GoldenCert paths
MATCH p=()-[:GoldenCert]->() RETURN p

// Coerce + relay to ADCS
MATCH p=()-[:CoerceAndRelayNTLMToADCS]->() RETURN p

// EnrollOnBehalfOf (delegation agent abuse)
MATCH p=(n)-[:EnrollOnBehalfOf|DelegatedEnrollmentAgent]->(t:CertTemplate) RETURN p

// ADCS infrastructure chain
MATCH p=(ca:EnterpriseCA)-[:IssuedSignedBy]->(root:RootCA)-[:RootCAFor]->(d:Domain) RETURN p
MATCH p=(ca:EnterpriseCA)-[:TrustedForNTAuth]->(store:NTAuthStore)-[:NTAuthStoreFor]->(d:Domain) RETURN p

#Cross-Domain & Trust Attacks

// Map all domain trusts
MATCH p=(d1:Domain)-[:CrossForestTrust|SameForestTrust|TrustedBy|Trusts]->(d2:Domain) RETURN p

// Find foreign users in local groups
MATCH (u:User)-[:MemberOf]->(g:Group) WHERE u.domain <> g.domain RETURN u.name, g.name

// Find cross-domain admin paths
MATCH p=(n)-[r]->(m) WHERE n.domain <> m.domain RETURN p LIMIT 50

// Foreign group membership
MATCH (g1:Group)-[:MemberOf]->(g2:Group) WHERE g1.domain <> g2.domain RETURN g1.name, g2.name

// SID History abuse paths
MATCH p=(n)-[:HasSIDHistory]->(m) RETURN p

// SID spoofing paths (cross-forest)
MATCH p=()-[:SpoofSIDHistory]->() RETURN p

// TGT delegation abuse across trusts
MATCH p=()-[:AbuseTGTDelegation]->() RETURN p

// DCFor (which computers are DCs for which domains)
MATCH p=(c:Computer)-[:DCFor]->(d:Domain) RETURN c.name, d.name

// Ownership across domains
MATCH (n)-[r:Owns|GenericAll|WriteDacl]->(m) WHERE n.domain <> m.domain RETURN n.name, type(r), m.name

#OU / Container / GPO Delegation

// GPO applied to high-value OUs
MATCH p=(gpo:GPO)-[:GPLink]->(ou:OU)-[:Contains*1..]->(n {highvalue:true}) RETURN p

// Who controls GPOs (write access)?
MATCH p=(n)-[:GenericAll|GenericWrite|Owns|WriteDacl|WriteOwner]->(gpo:GPO) RETURN p

// GPO applies to computers (find scope)
MATCH p=()-[:GPOAppliesTo]->() RETURN p

// WriteGPLink (add GPO to OU)
MATCH p=(n)-[:WriteGPLink]->(ou:OU) RETURN p

// Container delegation (GenericAll on OU)
MATCH p=(n)-[:GenericAll]->(ou:OU) RETURN p

// OU contains DA (map scope of OU control)
MATCH (ou:OU)-[:Contains*1..]->(u:User)-[:MemberOf*1..]->(g:Group)
WHERE g.objectid ENDS WITH '-512'
RETURN ou.name, u.name

// GPO links to Domain Controllers OU
MATCH p=(gpo:GPO)-[:GPLink]->(ou:OU {name:"DOMAIN [email protected]"}) RETURN p

// Propagated ACEs via container inheritance
MATCH p=()-[:PropagatesACEsTo]->() RETURN p

#Computer-to-Computer Paths

// Computers with local admin on other computers (rare but high-impact)
MATCH p=(c1:Computer)-[:AdminTo]->(c2:Computer) RETURN p

// DCOM, RDP, PSRemote between computers
MATCH p=(c1:Computer)-[:ExecuteDCOM|CanRDP|CanPSRemote]->(c2:Computer) RETURN p

// Computers with sessions leading to DA
MATCH (u:User)-[:MemberOf*1..]->(g:Group) WHERE g.objectid ENDS WITH '-512'
MATCH p=(c:Computer)-[:HasSession]->(u)
RETURN c.name, u.name

// Shortest path: any owned computer to DA
MATCH (c:Computer {owned:true}), (g:Group) WHERE g.objectid ENDS WITH '-512'
MATCH p=shortestPath((c)-[*1..]->(g))
RETURN p LIMIT 10

// Computers allowed to delegate to DCs
MATCH (c:Computer)-[:AllowedToDelegate]->(dc:Computer)
WHERE EXISTS { MATCH (dc)-[:DCFor]->(:Domain) }
RETURN c.name, dc.name, c.allowedtodelegate

// MSSQL servers (SQL admin paths)
MATCH (c:Computer) WHERE ANY(spn IN c.serviceprincipalnames WHERE toUpper(spn) CONTAINS 'MSSQL')
RETURN c.name, c.serviceprincipalnames

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

// Coerce + relay NTLM to SMB
MATCH p=()-[:CoerceAndRelayNTLMToSMB]->() RETURN p

// Coerce + relay NTLM to LDAP/LDAPS
MATCH p=()-[:CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS]->() RETURN p

#Users, Passwords & Stale Accounts

// Users with password stored in description (common misconfiguration)
MATCH (u:User) WHERE u.description =~ '(?i).*(pass|pwd|password|cred|secret|key|login).*'
RETURN u.name, u.description

// Computers without LAPS (lapsexpirationtime is null/absent)
MATCH (c:Computer) WHERE c.haslaps = false OR c.haslaps IS NULL
RETURN c.name, c.operatingsystem ORDER BY c.operatingsystem

// Computers with LAPS readable by non-admins
MATCH p=(n)-[:ReadLAPSPassword]->(c:Computer)
WHERE NOT n.objectid ENDS WITH '-512' AND NOT n.objectid ENDS WITH '-516'
RETURN n.name, c.name

// Stale user accounts (not logged in for 90 days, still enabled)
MATCH (u:User {enabled:true})
WHERE u.lastlogontimestamp < (datetime().epochseconds - (90 * 86400))
AND NOT u.lastlogontimestamp IN [-1.0, 0.0]
RETURN u.name, datetime({epochSeconds: toInteger(u.lastlogontimestamp)}) AS last_logon
ORDER BY u.lastlogontimestamp ASC

// Never-logged-in enabled accounts
MATCH (u:User {enabled:true}) WHERE u.lastlogontimestamp = -1.0 RETURN u.name

// Passwords not changed in 365+ days (enabled accounts)
MATCH (u:User {enabled:true})
WHERE u.pwdlastset < (datetime().epochseconds - (365 * 86400))
AND NOT u.pwdlastset IN [-1.0, 0.0]
RETURN u.name, datetime({epochSeconds: toInteger(u.pwdlastset)}) AS pwd_set
ORDER BY u.pwdlastset ASC

// Password never expires (enabled accounts)
MATCH (u:User {enabled:true, pwdneverexpires:true}) RETURN u.name

// Service accounts with SPNs (Kerberoastable, old passwords)
MATCH (u:User {hasspn:true, enabled:true})
WHERE u.pwdlastset < (datetime().epochseconds - (365 * 86400))
AND NOT u.name STARTS WITH 'KRBTGT'
RETURN u.name, u.serviceprincipalnames,
       datetime({epochSeconds: toInteger(u.pwdlastset)}) AS pwd_set
ORDER BY u.pwdlastset ASC

// GMSA passwords readable by principals
MATCH p=(n)-[:ReadGMSAPassword]->(u:User) RETURN p

// Accounts with adminCount=1 (protected by SDProp)
MATCH (u:User {admincount:true, enabled:true}) RETURN u.name ORDER BY u.name

#Owned / Marking & Custom Workflow

// Mark single user as owned
MATCH (u:User {name:"[email protected]"}) SET u.owned = true

// Bulk mark owned from list
MATCH (u:User) WHERE u.name IN ["[email protected]","[email protected]","[email protected]"]
SET u.owned = true

// Mark computer as owned
MATCH (c:Computer {name:"WORKSTATION01.CORP.LOCAL"}) SET c.owned = true

// Mark custom high-value targets
MATCH (c:Computer {name:"FILESERVER01.CORP.LOCAL"}) SET c.highvalue = true
MATCH (g:Group {name:"IT [email protected]"}) SET g.highvalue = true

// Find path from ALL owned to ALL high-value
MATCH p=shortestPath((n {owned:true})-[*1..]->(m {highvalue:true}))
WHERE n <> m RETURN p LIMIT 25

// List all owned nodes by type
MATCH (n {owned:true}) RETURN labels(n)[0] AS type, n.name ORDER BY type

// List groups of owned users (lateral movement surface)
MATCH (u:User {owned:true})
MATCH p=(u)-[:MemberOf*1..]->(g:Group)
RETURN u.name, g.name

// Reset all owned flags (cleanup)
MATCH (n {owned:true}) SET n.owned = false

#Pre-Built Queries Reference

#Built-In Query List (BloodHound CE)

Category Query Name Purpose
Domain Info Find all Domain Admins All members of DA group
Domain Info Map Domain Trusts Trust relationships between domains
Domain Info Computers with unsupported OS Legacy OS (2000/2003/2008/XP/Vista/7)
Dangerous Rights Find Principals with DCSync GetChangesAll / DCSync on domain
Dangerous Rights Find users with Foreign Domain Group Membership Cross-domain group nesting
Dangerous Rights Computers where Domain Users are Local Admin Domain Users in Administrators
Dangerous Rights LAPS Password Readers Non-admins who can read LAPS
Dangerous Rights All paths from Domain Users to HVT High-value target exposure
Dangerous Rights Dangerous privileges for Domain Users DCO,ExecuteDCOM,GenericAll etc.
Kerberos Kerberoastable Members of High Value Groups SPN users in privileged groups
Kerberos All Kerberoastable Accounts All users with SPN
Kerberos Kerberoastable Users with most privileges Ranked by admin count
Kerberos AS-REP Roastable Users dontreqpreauth=true
Kerberos Shortest Paths to Unconstrained Delegation Paths to machines with unconstrained deleg
Kerberos Shortest Paths from Kerberoastable Users Kerberoastable to computer
Kerberos Shortest Paths to DA from Kerberoastable Kerberoastable to DA path
Sessions Find Domain Admin Logons to non-DCs DA token exposure on workstations
Sessions Workstations where Domain Users can RDP RDP exposure
Sessions Servers where Domain Users can RDP RDP on servers
Shortest Paths Shortest Path from Owned Principals Owned node to computers
Shortest Paths Shortest Paths to DA from Owned Owned to Domain Admin
Shortest Paths Shortest Paths to High Value Targets Any node to HVT
Shortest Paths Shortest Paths from Domain Users to HVT Domain Users group to HVT
Shortest Paths Find Shortest Paths to DA Any to DA
Shortest Paths Shortest Paths from Owned to HVT Owned to HVT
Azure Find all Global Administrators AZGlobalAdmin role members
Azure High Privileged Role Members GA/UA/CA/EA/HA roles
Azure OnPrem synced users in privileged roles Hybrid attack surface
Azure Azure Users path to HVT AZUser to highvalue
Azure OnPrem synced users path to HVT Hybrid path to HVT
Azure Shortest paths to privileged roles Any to GA/EA roles
Azure Azure Apps path to HVT AZApp to highvalue
Azure Shortest paths to Azure Subscriptions Any to subscription
Azure Shortest paths from owned to HVT Owned AZ node to HVT
Azure Shortest paths from owned to privileged roles Owned to GA/EA
Azure Service Principals with MS Graph privilege AZMGGrantAppRoles
Azure Service Principals MS Graph App Roles High-risk Graph permissions
Azure Direct Controllers of MS Graph AddOwner/AddSecret on Graph SP
Azure Shortest paths to MS Graph Any to Microsoft Graph SP

#Node Types Reference

#AD Node Types

Node Type Description Key Properties
User AD user account hasspn, dontreqpreauth, admincount, enabled, pwdlastset
Computer AD computer object unconstraineddelegation, allowedtodelegate, haslaps, operatingsystem
Group AD security/distribution group admincount, highvalue
Domain AD domain root object functionallevel, trusttype
OU Organizational Unit blocksinheritance
Container AD container (not OU) -
GPO Group Policy Object gpcpath
CertTemplate ADCS certificate template enrolleesuppliessubject, requiresmanagerapproval, schemaversion
EnterpriseCA Enterprise Certificate Authority caname, dnshostname, flags
RootCA Root Certificate Authority certchain
AiaCA Authority Information Access CA -
NTAuthStore NT Auth Certificate Store certthumbprints

#Azure (Entra ID) Node Types

Node Type Description
AZUser Azure / Entra ID user
AZGroup Azure AD group
AZApp Azure App registration
AZServicePrincipal Service principal (enterprise app)
AZDevice Azure-joined device
AZTenant Azure tenant root
AZSubscription Azure subscription
AZResourceGroup Azure resource group
AZVM Azure virtual machine
AZKeyVault Azure Key Vault
AZRole Azure RBAC / Entra role definition
AZManagedIdentity Managed identity

#Edge / Relationship Reference

#Core AD Edges

Edge Abuse Method Severity
AdminTo PSExec, WMI, DCOM, WinRM, Token impersonation Critical
MemberOf Inherits all rights of the group High
HasSession Token stealing, credential dumping Critical
DCSync secretsdump.py -just-dc / Mimikatz lsadump::dcsync Critical
GetChanges + GetChangesAll Combined = DCSync capability Critical
GenericAll Change password, add to group, modify any attr Critical
GenericWrite Write specific attributes (e.g. scriptpath, allowedtodelegate) Critical
WriteDacl Grant self any right on object Critical
WriteOwner Take ownership, then WriteDacl Critical
Owns Implies WriteOwner (take ownership) Critical
ForceChangePassword net user, Set-ADAccountPassword without knowing current High
AllExtendedRights Read LAPS, change password, cert enrollment High
AddMember Add principals to group High
AddSelf Add yourself to group High
AddKeyCredentialLink Shadow Credentials (Whisker/Pywhisker) High
WriteSPN Set SPN for targeted Kerberoasting High
WriteAccountRestrictions Write msDS-AllowedToActOnBehalfOfOtherIdentity (RBCD) High
AllowedToDelegate Constrained delegation TGS request for any user High
AllowedToAct RBCD - obtain ticket as any user to target High
CanRDP Remote Desktop access Medium
CanPSRemote WinRM / Enter-PSSession Medium
ExecuteDCOM DCOM lateral movement (MMC20, ShellBrowserWindow) Medium
SQLAdmin xp_cmdshell, lateral movement via SQL Medium
ReadLAPSPassword Retrieve LAPS plaintext password High
ReadGMSAPassword Retrieve GMSA NT hash High
DumpSMSAPassword Dump sMSA password Medium
SyncLAPSPassword Sync LAPS password High
HasSIDHistory Old SID history for privilege escalation High
GPLink GPO applied to OU/container/site High
WriteGPLink Link custom GPO to OU High
Contains OU contains objects (scope of delegation) Info
DCFor Computer is a DC for this domain Info

#ADCS Edges

Edge Description Abuse
ADCSESC1 Enrollee-supplied SAN + auth EKU Enroll cert for any user including DA
ADCSESC3 Enrollment agent + template chain Issue cert on behalf of any user
ADCSESC4 Write control over template Modify template to enable ESC1
ADCSESC6a/6b EDITF_ATTRIBUTESUBJECTALTNAME2 on CA SAN on any template
ADCSESC9a/9b StrongCertificateBindingEnforcement bypass Certificate mapping attack
ADCSESC10a/10b Weak certificate binding Certificate auth bypass
ADCSESC13 OID group link abuse Cert grants group membership
GoldenCert ManageCA abuse to forge certs Forge cert for any principal
Enroll Enrollment right on cert template Request certificate
PublishedTo Template published to Enterprise CA Template is issuable
HasEnrollmentRights Principal can enroll on template First step to ESC1
ManageCA CA admin rights Issue/revoke certs, change config
ManageCertificates Certificate officer rights Approve pending requests
IssuedSignedBy CA cert chain relationship Maps PKI hierarchy
TrustedForNTAuth CA cert in NTAuth store CA can authenticate to AD
NTAuthStoreFor NTAuth store belongs to domain PKI trust anchor
RootCAFor Root CA for domain PKI root
EnterpriseCAFor Enterprise CA for domain Issuing CA
EnrollOnBehalfOf Enrollment agent template Agent can enroll for others
DelegatedEnrollmentAgent Explicit enrollment agent Scoped agent delegation
OIDGroupLink Template linked to AD group via OID ESC13 group membership
ExtendedByPolicy Template extended by issuance policy Policy chain
WritePKIEnrollmentFlag Write enrollment flags on template Modify template behavior
WritePKINameFlag Write name flags on template Enable SAN abuse
HostsCAService Computer runs CA service CA location

#Cross-Domain & Coercion Edges

Edge Description
CrossForestTrust Cross-forest trust relationship
SameForestTrust Same-forest trust relationship
SpoofSIDHistory SID history forgery across trust
AbuseTGTDelegation TGT delegation across trust (unconstrained)
HasTrustKeys Trust keys (for inter-realm ticket forgery)
CoerceToTGT Coerce authentication for TGT
CoerceAndRelayNTLMToSMB NTLM relay to SMB (NTLM auth capture)
CoerceAndRelayNTLMToADCS NTLM relay to ADCS HTTP endpoint
CoerceAndRelayNTLMToLDAP NTLM relay to LDAP
CoerceAndRelayNTLMToLDAPS NTLM relay to LDAPS
SyncedToADUser Azure user synced from on-prem AD user

#Azure Edges

Edge Description Abuse
AZGlobalAdmin Global Administrator role Full tenant control
AZPrivilegedRoleAdmin Privileged Role Administrator Assign/modify any role
AZResetPassword Can reset user's password Account takeover
AZOwns Owns Azure object Full object control
AZContributor Azure RBAC Contributor on resource Modify resource
AZUserAccessAdministrator Manage access to resource Assign roles
AZHasRole Has Azure RBAC role assignment Scope-specific rights
AZMemberOf Member of Azure AD group Inherits group rights
AZAddMembers Can add members to group Escalate via group
AZAddOwner Can add owners to app/group Escalate ownership
AZAddSecret Can add client secret to app Authenticate as app
AZExecuteCommand Run command on Azure VM (Intune) RCE on Azure VM
AZVMContributor VM Contributor role Manage VM
AZMGAddSecret MS Graph - App.ReadWrite.All Add secrets to any app
AZMGAddOwner MS Graph - add owner via Graph Escalate app ownership
AZMGGrantAppRoles MS Graph - AppRoleAssignment.ReadWrite Grant Graph roles
AZMGGrantRole MS Graph - RoleManagement.ReadWrite Grant directory roles
AZKeyVaultContributor Key Vault Contributor Manage vault

#Operational Workflow

#Attack Path Methodology

  1. Collect data - Run SharpHound (DCOnly first for stealth) or BloodHound-Python from Linux; use AzureHound for Entra ID
  2. Upload & ingest - Drag-and-drop ZIP into BloodHound CE UI or use the file-upload API endpoint
  3. Mark initial access - Right-click compromised nodes, "Mark as Owned"; use Cypher bulk SET for many nodes
  4. Run pre-built queries - Start with "Shortest Paths to DA from Owned Principals" and "Find Principals with DCSync"
  5. Enumerate attack surface - Query for Kerberoastable, AS-REP Roastable, unconstrained delegation, ADCS ESC paths
  6. Prioritize paths - Filter by hop count; prefer ACL abuse > delegation > group membership chains
  7. Pivot laterally - For each intermediate hop, use appropriate tool (impacket, Rubeus, Certipy, Evil-WinRM)
  8. Re-collect sessions - Loop-collect sessions every 15 min; DA tokens appear on workstations during business hours
  9. Escalate via ADCS - Check all ADCSESC* edges; ESC1/ESC6 are most common and highest impact
  10. Document chain - Export path screenshots; note each node + edge + tool used per hop

#Post-DA Objectives

// Find all Enterprise Admins (cross-domain)
MATCH p=(n)-[:MemberOf*1..]->(g:Group) WHERE g.objectid ENDS WITH '-519' RETURN p

// Find AdminSDHolder-protected accounts
MATCH (u:User {admincount:true}) RETURN u.name ORDER BY u.name

// Find all computers (map full environment)
MATCH (c:Computer {enabled:true}) RETURN c.name, c.operatingsystem ORDER BY c.operatingsystem

// Find all trusts to pivot laterally into child/parent domains
MATCH p=(d1:Domain)-[r:CrossForestTrust|SameForestTrust]->(d2:Domain) RETURN p

// KRBTGT for golden ticket operations
MATCH (u:User) WHERE u.name STARTS WITH 'KRBTGT' RETURN u.name, u.domain

// Find backup operators (VSS shadow copy, NTDS.dit)
MATCH (n)-[:MemberOf*1..]->(g:Group) WHERE g.objectid ENDS WITH '-551' RETURN n.name, labels(n)

#BloodHound CE API

#Authentication & Setup

# Get JWT token (login)
TOKEN=$(curl -s -X POST http://localhost:8080/api/v2/login \
  -H "Content-Type: application/json" \
  -d '{"login_method":"secret","secret":"admin_password","username":"admin"}' \
  | jq -r '.data.session_token')

# Create API key (for scripting, persists without login)
curl -s -X POST http://localhost:8080/api/v2/tokens \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"token_name":"automation_key"}' | jq .

# Use API key in requests
API_KEY="your_api_key_here"
curl -s http://localhost:8080/api/v2/domains \
  -H "Authorization: Bearer $API_KEY" | jq .

#Common API Endpoints

BASE="http://localhost:8080/api/v2"

# List collected domains
curl -s "$BASE/domains" -H "Authorization: Bearer $TOKEN" | jq '.data[].name'

# Run Cypher query
curl -s -X POST "$BASE/graphs/cypher" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"query":"MATCH (u:User {enabled:true}) RETURN u.name LIMIT 10","include_properties":true}' \
  | jq '.data.nodes'

# Search for a node
curl -s "$BASE/search?q=Administrator&type=User" \
  -H "Authorization: Bearer $TOKEN" | jq .

# Get node details by object ID
curl -s "$BASE/objects/S-1-5-21-XXXX-YYYY-ZZZZ-500" \
  -H "Authorization: Bearer $TOKEN" | jq .

# Upload collection file
curl -s -X POST "$BASE/file-upload" \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@bloodhound_output.zip"

# List file upload jobs
curl -s "$BASE/file-upload" \
  -H "Authorization: Bearer $TOKEN" | jq '.data[] | {id, status, filename}'

# Get attack paths to high-value target
curl -s "$BASE/attack-paths?finding_type=ComputerLocalAdminRights" \
  -H "Authorization: Bearer $TOKEN" | jq .

# Get graph path between two nodes
curl -s "$BASE/graphs/path?start_node=OWNED_USER_OBJECTID&end_node=DA_GROUP_OBJECTID" \
  -H "Authorization: Bearer $TOKEN" | jq .

#Custom Query Management

Custom queries are stored locally in ~/.config/bloodhound/customqueries.json (legacy) or managed via the BloodHound CE UI under Explore > Custom Queries.

// customqueries.json format for BloodHound CE
{
  "queries": [
    {
      "name": "Find Users with Password in Description",
      "category": "Users",
      "queryList": [
        {
          "final": true,
          "query": "MATCH (u:User) WHERE u.description =~ '(?i).*(pass|pwd|password|cred).*' RETURN u.name, u.description"
        }
      ]
    },
    {
      "name": "ADCS: All ESC Paths",
      "category": "ADCS",
      "queryList": [
        {
          "final": true,
          "query": "MATCH p=()-[r:ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13]->() RETURN type(r) AS esc, startNode(p).name AS src, endNode(p).name AS dst"
        }
      ]
    },
    {
      "name": "Computers Without LAPS",
      "category": "Computers",
      "queryList": [
        {
          "final": true,
          "query": "MATCH (c:Computer) WHERE c.haslaps = false OR c.haslaps IS NULL RETURN c.name, c.operatingsystem ORDER BY c.operatingsystem"
        }
      ]
    },
    {
      "name": "Parameterized: Shortest Paths to Group",
      "category": "Paths",
      "queryList": [
        {
          "final": false,
          "title": "Select target group",
          "query": "MATCH (g:Group) RETURN g.name ORDER BY g.name"
        },
        {
          "final": true,
          "query": "MATCH p=shortestPath((n {owned:true})-[*1..]->(g:Group {name:$result})) WHERE n<>g RETURN p"
        }
      ]
    }
  ]
}
# Import custom queries file to BloodHound CE via API
curl -s -X PUT "$BASE/customqueries" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @customqueries.json

# List existing custom queries
curl -s "$BASE/customqueries" -H "Authorization: Bearer $TOKEN" | jq '.data[].name'

# Delete a custom query by ID
curl -s -X DELETE "$BASE/customqueries/42" -H "Authorization: Bearer $TOKEN"

#Key Nodes & Properties Reference

#Node Property Cheat Sheet

Property Node Type Cypher Filter Meaning
owned Any {owned:true} Compromised by attacker
highvalue Any {highvalue:true} Designated high-value target
enabled User/Computer {enabled:true} Account is active
admincount User/Group {admincount:true} Protected by AdminSDHolder
hasspn User {hasspn:true} Kerberoastable
dontreqpreauth User {dontreqpreauth:true} AS-REP Roastable
unconstraineddelegation Computer/User {unconstraineddelegation:true} Unconstrained delegation enabled
allowedtodelegate Computer/User WHERE n.allowedtodelegate IS NOT NULL Constrained delegation targets
pwdneverexpires User {pwdneverexpires:true} Password never expires
pwdlastset User epoch seconds Last password change timestamp
lastlogontimestamp User epoch seconds Last AD logon (replicates ~14 days)
haslaps Computer {haslaps:false} LAPS not deployed
operatingsystem Computer regex match OS version string
description User regex match Often contains passwords
serviceprincipalnames User/Computer list contains SPNs registered on object
functionallevel Domain string Domain functional level
objectid Any ends with RID SID/object identifier
enrolleesuppliessubject CertTemplate {enrolleesuppliessubject:true} ESC1 indicator
requiresmanagerapproval CertTemplate {requiresmanagerapproval:false} Template auto-issues

#Well-Known RID Reference

RID Suffix Group Name Why It Matters
-500 Administrator Built-in local/domain admin
-502 krbtgt Golden ticket source
-512 Domain Admins DA group - primary target
-513 Domain Users All users - wide blast radius
-514 Domain Guests Restricted access
-516 Domain Controllers DC computer group
-517 Cert Publishers Can write certs to AD
-518 Schema Admins Modify AD schema
-519 Enterprise Admins EA - forest-wide DA
-520 Group Policy Creator Owners Create/own GPOs
-544 Administrators (local) Local admin
-548 Account Operators Can modify most user accounts
-549 Server Operators Logon to DCs, start/stop services
-550 Print Operators Can logon to DCs
-551 Backup Operators Read any file including NTDS.dit
-553 RAS and IAS Servers Network access

#BloodHound CE vs Legacy

#Version Comparison

Feature BloodHound CE Legacy (v4.x)
Backend Neo4j + REST API Neo4j direct
Interface Web UI (port 8080) Electron app
Auth Username/password + API keys No auth
Multi-user Yes No
Collectors SharpHound 2.x / CE SharpHound 1.x
ADCS support Full ESC1-ESC13 Partial
Azure/Entra Full AzureHound Limited
Docker support Official compose Manual
API REST API v2 None

BloodHound CE is the current actively developed version. Legacy v4.x is no longer maintained. All commands and queries in this cheatsheet target CE unless noted otherwise.

#Also See

#Cyber Aurelien Guidi