BloodHound Cypher Queries
Reference queries for BloodHound (CE v5+/Legacy) against collected AD graph data. Run these in the BloodHound UI "Raw Query" box or via neo4j-shell/cypher-shell.
Replace DOMAIN.LOCAL with the actual domain, uppercase.
Attack path discovery
// Shortest path from any owned principal to Domain Admins
MATCH p=shortestPath((n {owned:true})-[*1..]->(g:Group {name:"DOMAIN ADMINS@DOMAIN.LOCAL"}))
RETURN p
// Shortest path from Kerberoastable users to DA
MATCH p=shortestPath((u:User {hasspn:true})-[*1..]->(g:Group {name:"DOMAIN ADMINS@DOMAIN.LOCAL"}))
RETURN p
// All paths (bounded) from a specific user to high-value
MATCH p=allShortestPaths((u:User {name:"ALICE@DOMAIN.LOCAL"})-[*1..6]->(n {highvalue:true}))
RETURN pUsers
// Domain Admins
MATCH (u:User)-[:MemberOf*1..]->(g:Group {name:"DOMAIN ADMINS@DOMAIN.LOCAL"})
RETURN u.name, u.enabled, u.pwdlastset, u.lastlogon
// Kerberoastable users (SPN set, not krbtgt, enabled)
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
// AS-REP roastable (DONT_REQ_PREAUTH)
MATCH (u:User {dontreqpreauth:true, enabled:true})
RETURN u.name, u.pwdlastset
// Users with password never expires
MATCH (u:User {pwdneverexpires:true, enabled:true})
RETURN u.name, u.pwdlastset
// Users whose description field contains "password" (low-hanging cleartext)
MATCH (u:User)
WHERE toLower(u.description) CONTAINS "password" OR toLower(u.description) CONTAINS "pwd"
RETURN u.name, u.descriptionComputers & delegation
// Unconstrained delegation (excluding DCs)
MATCH (c:Computer {unconstraineddelegation:true})
WHERE NOT (c)-[:MemberOf*1..]->(:Group {name:"DOMAIN CONTROLLERS@DOMAIN.LOCAL"})
RETURN c.name, c.operatingsystem
// Constrained delegation targets
MATCH (u)-[:AllowedToDelegate]->(c:Computer)
RETURN u.name, c.name
// Resource-Based Constrained Delegation (RBCD)
MATCH (u)-[:AllowedToAct]->(c:Computer)
RETURN u.name, c.name
// Computers where Domain Users can RDP
MATCH p=(g:Group {name:"DOMAIN USERS@DOMAIN.LOCAL"})-[:CanRDP]->(c:Computer)
RETURN c.nameDCSync rights
// Non-DA principals with DCSync (GetChanges + GetChangesAll)
MATCH (n)-[:MemberOf|GetChanges|GetChangesAll*1..]->(d:Domain {name:"DOMAIN.LOCAL"})
WHERE NOT (n)-[:MemberOf*1..]->(:Group {name:"DOMAIN ADMINS@DOMAIN.LOCAL"})
AND NOT (n)-[:MemberOf*1..]->(:Group {name:"ENTERPRISE ADMINS@DOMAIN.LOCAL"})
RETURN DISTINCT n.name, labels(n)ACL abuse
// Principals with GenericAll/WriteOwner/WriteDACL on high-value objects
MATCH (n)-[r:GenericAll|GenericWrite|WriteOwner|WriteDacl|Owns|AllExtendedRights]->(h {highvalue:true})
RETURN n.name, type(r), h.name
// Who can reset the password of high-value users
MATCH p=(n)-[:ForceChangePassword]->(u:User {highvalue:true})
RETURN p
// Users who can add themselves to privileged groups
MATCH p=(n)-[:AddMember|AddSelf]->(g:Group)
WHERE g.highvalue = true
RETURN pGPO abuse
// Who can edit GPOs linked to high-value OUs
MATCH (n)-[r:GenericAll|GenericWrite|WriteOwner|WriteDacl]->(g:GPO)
MATCH (g)-[:GpLink]->(o)
WHERE o.highvalue = true OR (o)-[:Contains*1..]->(:Computer {highvalue:true})
RETURN DISTINCT n.name, g.name, o.nameSession / logon intelligence
// Where do Domain Admins have active sessions? (potential credential theft targets)
MATCH (c:Computer)-[:HasSession]->(u:User)-[:MemberOf*1..]->(:Group {name:"DOMAIN ADMINS@DOMAIN.LOCAL"})
RETURN c.name, u.name
// Computers where owned principal is a local admin
MATCH (u {owned:true})-[:AdminTo|MemberOf*1..]->(c:Computer)
RETURN DISTINCT c.name, u.nameMark-as-owned helpers
// Mark a user as owned after compromise (enables path queries from it)
MATCH (u:User {name:"ALICE@DOMAIN.LOCAL"}) SET u.owned=true RETURN u.name
// Mark a computer as owned
MATCH (c:Computer {name:"WS01.DOMAIN.LOCAL"}) SET c.owned=true RETURN c.nameTool versions validated
- BloodHound CE >= 5.11
- SharpHound >= 2.5
- bloodhound-python >= 1.7.2
- Neo4j >= 5.x (CE) or 4.4 (legacy)