BloodHound
My cheatsheet of custom bloodhound queries
General Queries
List all owned users
MATCH (m:User) WHERE m.owned=TRUE RETURN m
List all owned computers
MATCH (m:Computer) WHERE m.owned=TRUE RETURN m
List all owned groups
MATCH (m:Group) WHERE m.owned=TRUE RETURN m
List all High Valued Targets
MATCH (m) WHERE m.highvalue=TRUE RETURN m
List the groups of all owned users
MATCH (m:User) WHERE m.owned=TRUE
WITH m
MATCH p=(m)-[:MemberOf*1..]->(n:Group)
RETURN p
Find the Shortest path to a high value target from an owned object
MATCH p=shortestPath((g {owned:true})-[*1..]->(n {highvalue:true}))
WHERE g<>n
RETURN p
Find the Shortest path to an unconstrained delegation system from an owned object
MATCH (n)
MATCH p=shortestPath((n)-[*1..]->(m:Computer {unconstraineddelegation: true}))
WHERE NOT n=m AND n.owned = true
RETURN p
Kerberoasting & AS-REP Roasting
Find all Kerberoastable Users
MATCH (n:User) WHERE n.hasspn=true RETURN n
Find Kerberoastable Users (password last set < 5 years ago)
MATCH (u:User)
WHERE u.hasspn=true AND u.pwdlastset < (datetime().epochseconds - (1825 * 86400))
AND NOT u.pwdlastset IN [-1.0, 0.0]
RETURN u.name, u.pwdlastset ORDER BY u.pwdlastset
Find Kerberoastable Users with a path to DA
MATCH (u:User {hasspn:true})
MATCH (g:Group) WHERE g.objectid ENDS WITH '-512'
MATCH p = shortestPath((u)-[*1..]->(g))
RETURN p
Find users that can be AS-REP roasted
MATCH (u:User {dontreqpreauth: true}) RETURN u
Kerberoastable Users with passwords > 5 years ago
MATCH (u:User)
WHERE n.hasspn=true AND WHERE u.pwdlastset < (datetime().epochseconds - (1825 * 86400))
AND NOT u.pwdlastset IN [-1.0, 0.0]
RETURN u
Kerberoastable users in high value groups
MATCH (u:User)-[r:MemberOf*1..]->(g:Group)
WHERE g.highvalue=true AND u.hasspn=true
RETURN u
Kerberoastable users and AdminTo
OPTIONAL MATCH (u1:User) WHERE u1.hasspn=true
OPTIONAL MATCH (u1)-[r:AdminTo]->(c:Computer)
RETURN u
RDP Access and Admin Rights
Machines Domain Users can RDP into
MATCH p=(g:Group)-[:CanRDP]->(c:Computer)
WHERE g.objectid ENDS WITH '-513'
RETURN p
Groups with RDP access
MATCH p=(m:Group)-[r:CanRDP]->(n:Computer) RETURN p
Groups with password reset rights
MATCH p=(m:Group)-[r:ForceChangePassword]->(n:User) RETURN p
Groups with local admin rights
MATCH p=(m:Group)-[r:AdminTo]->(n:Computer) RETURN p
Users with local admin rights
MATCH p=(m:User)-[r:AdminTo]->(n:Computer) RETURN p
Active Domain Admin sessions
MATCH (n:User)-[:MemberOf]->(g:Group)
WHERE g.objectid ENDS WITH '-512'
MATCH p = (c:Computer)-[:HasSession]->(n)
RETURN p
Constrained & Unconstrained Delegation
Computers with Unconstrained Delegation
MATCH (c:Computer {unconstraineddelegation:true}) RETURN c
Computers that allow Unconstrained Delegation but aren’t DCs
MATCH (c1:Computer)-[:MemberOf*1..]->(g:Group)
WHERE g.objectid ENDS WITH '-516'
WITH COLLECT(c1.name) AS domainControllers
MATCH (c2:Computer {unconstraineddelegation:true})
WHERE NOT c2.name IN domainControllers
RETURN c2
Constrained delegation relationships
MATCH p=(u:User)-[:AllowedToDelegate]->(c:Computer) RETURN p
Computers with constrained delegation permissions
MATCH (c:Computer)
WHERE c.allowedtodelegate IS NOT NULL
RETURN c
User and Group Insights
Unsupported OS
MATCH (H:Computer)
WHERE H.operatingsystem = '.*(2000|2003|2008|xp|vista|7|me).*'
RETURN H
Users logged in within the last 90 days
MATCH (u:User)
WHERE u.lastlogon < (datetime().epochseconds - (90 * 86400))
AND NOT u.lastlogon IN [-1.0, 0.0]
RETURN u
Users with passwords set in last 90 days
MATCH (u:User)
WHERE u.pwdlastset < (datetime().epochseconds - (90 * 86400))
AND NOT u.pwdlastset IN [-1.0, 0.0]
RETURN u
Users never logged in and active
MATCH (n:User)
WHERE n.lastlogontimestamp=-1.0 AND n.enabled=TRUE
RETURN n
All GPOs
MATCH (n:GPO) RETURN n
Groups containing 'admin'
MATCH (n:Group)
WHERE n.name CONTAINS 'ADMIN'
RETURN n
Show high value target's groups
MATCH p=(n:User)-[r:MemberOf*1..]->(m:Group {highvalue:true}) RETURN p
Groups with both users and computers
MATCH (c:Computer)-[r:MemberOf*1..]->(groupsWithComps:Group)
WITH groupsWithComps
MATCH (u:User)-[r:MemberOf*1..]->(groupsWithComps)
RETURN DISTINCT(groupsWithComps) as groupsWithCompsAndUsers
Users in VPN group
MATCH p=(u:User)-[:MemberOf]->(g:Group)
WHERE toUPPER(g.name) CONTAINS 'VPN'
RETURN p
Unprivileged users can add members to groups
MATCH (n:User {admincount:False})
MATCH p=allShortestPaths((n)-[r:AddMember*1..]->(m:Group))
RETURN p
Foreign Object Access
Object in one domain with access to another
MATCH p=(n)-[r]->(m)
WHERE NOT n.domain = m.domain
RETURN p
Object from domain A that can touch foreign object
-- Requires node selection
MATCH p=(n {domain:{result}})-[r]->(d)
WHERE NOT d.domain=n.domain
RETURN p
All sessions for users in a specific domain
-- Requires node selection
MATCH p=(m:Computer)-[r:HasSession]->(n:User {domain:{result}})
RETURN p
All edges any owned user has on a computer
MATCH p=shortestPath((m:User)-[r*]->(b:Computer))
WHERE m.owned
RETURN p
Azure Queries
Return All Azure Users that are part of the 'Global Administrator' Role
MATCH p =(n)-[r:AZGlobalAdmin*1..]->(m) RETURN p
Return All On-Prem users with edges to Azure
MATCH p=(m:User)-[r:AZResetPassword|AZOwns|AZUserAccessAdministrator|AZContributor|AZAddMembers|AZGlobalAdmin|AZVMContributor|AZOwnsAZAvereContributor]->(n)
WHERE m.objectid CONTAINS 'S-1-5-21'
RETURN p
Find all paths to an Azure VM
MATCH p = (n)-[r]->(g:AZVM) RETURN p
Find all paths to an Azure KeyVault
MATCH p = (n)-[r]->(g:AZKeyVault) RETURN p
Return All Azure Users and their Groups
MATCH p=(m:AZUser)-[r:MemberOf]->(n)
WHERE NOT m.objectid CONTAINS 'S-1-5'
RETURN p
Return All Azure AD Groups synchronized with On-Prem AD
MATCH (n:Group)
WHERE n.objectid CONTAINS 'S-1-5' AND n.azsyncid IS NOT NULL
RETURN n
Find all Privileged Service Principals
MATCH p = (g:AZServicePrincipal)-[r]->(n) RETURN p
Find all Owners of Azure Applications
MATCH p = (n)-[r:AZOwns]->(g:AZApp) RETURN p
Last updated