More BloodHound Cypher queries
04 Oct 2020Hello,
In this blog post i will share my Cypher queries which i’m using in my daily engagements. I aim to be complementary to the cheatsheets you can found out there and to the default queries you will find in BloodHound.
I will also comment these ones if needed to provide further information.
First a word about logon sessions enumeration :
Logon sessions enumeration is really important to know who is loggedon on which machine and from where. When the NetSessionEnum API is called, no privilege is needed to have at least two levels of information, 0 and 10. We just need to be Authenticated Users.
You also can continuously enumerate logon sessions thanks to BloodHound ingestor:
SharpHound.exe -c SessionLoop
Find also a great article about Bloodhound, its sessions enumeration and the NetCease script remediation here.
Queries
Count the distinct users logon sessions on a domain :
MATCH (u1:user)
WITH count(u1) as totalUsers
MATCH (c:Computer)-[r:HasSession]->(u2:User)
RETURN COUNT(DISTINCT(u2))
Get All computers with their users logon sessions :
MATCH c=(C:Computer)-[r2:HasSession*1]-(U:User)
WHERE U.name =~ ".*" return c
Get computers having a session from usernames like “ADM.*” (matching specific OS) :
MATCH p=((S:Computer)-[r:HasSession*1]->(T:User))
WHERE T.name =~ "ADM.*"
AND S.operatingsystem =~ ".*XP.*"
RETURN p
Find all Domain Admins :
MATCH (n:Group) WHERE n.objectid =~ "(?i)S-1-5-21-.*-512" WITH n MATCH p=(n)<-[r:MemberOf*1..]-(m) RETURN n,r,m
For all Well-known security identifiers: https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/security-identifiers-in-windows.
Find all Domain Admins (nested SID S-1-5-21-.*-512) having a session opened on a domain computer :
MATCH (m:User)-[r:MemberOf*1..]->(n:Group) WHERE n.objectid =~ "(?i)S-1-5-.*-512" WITH m MATCH q=((m)<-[:HasSession]-(o:Computer)) RETURN q
Show all high value target principals :
MATCH (m {highvalue:true}) RETURN m
List of unique users with a path to a “highvalue” targets :
MATCH (u:User) MATCH (g {highvalue:true}) MATCH p = shortestPath((u:User)-[r:AddMember|AdminTo|AllExtendedRights|AllowedToDelegate|CanRDP|Contains|ExecuteDCOM|ForceChangePassword|GenericAll|GenericWrite|GpLink|HasSession|MemberOf|Owns|ReadLAPSPassword|TrustedBy|WriteDacl|WriteOwner|GetChanges|GetChangesAll*1..]->(g)) RETURN DISTINCT(u.name) AS USER, u.enabled as ENABLED,count(p) as PATHS order by u.name
Shortest paths from Domain Users to “highvalue” targets :
MATCH p=shortestPath((g:Group {name:"DOMAIN [email protected]"})-[*1..]->(n {highvalue:true})) WHERE g.objectid ENDS WITH '-513' AND g<>n return p
Find user with a username like “ADM.*” who logged on a computer which has a owned local admin :
MATCH c=(U:User {owned:true})-[r:AdminTo*1]-(C:Computer)-[r2:HasSession*1]-(P:User) WHERE P.name =~ "A_.*" return c
Find all edges that a “specific user” has against all the nodes (HasSession is not calculated, as it is an edge that comes from computer to user, not from user to computer) :
MATCH (n:User) WHERE n.name =~ '[email protected]' MATCH (m) WHERE NOT m.name = n.name MATCH p=allShortestPaths((n)-[r:MemberOf|HasSession|AdminTo|AllExtendedRights|AddMember|ForceChangePassword|GenericAll|GenericWrite|Owns|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM|AllowedToDelegate|ReadLAPSPassword|Contains|GpLink|AddAllowedToAct|AllowedToAct|SQLAdmin*1..]->(m)) RETURN p
Find all the edges that any UNPRIVILEGED user (based on the admincount:False) has against all the nodes :
MATCH (n:User {admincount:False}) MATCH (m) WHERE NOT m.name = n.name MATCH p=allShortestPaths((n)-[r:MemberOf|HasSession|AdminTo|AllExtendedRights|AddMember|ForceChangePassword|GenericAll|GenericWrite|Owns|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM|AllowedToDelegate|ReadLAPSPassword|Contains|GpLink|AddAllowedToAct|AllowedToAct|SQLAdmin*1..]->(m)) RETURN p
Find interesting edges related to “ACL Abuse” that unprivileged users have against other users :
MATCH (n:User {admincount:False}) MATCH (m:User) WHERE NOT m.name = n.name MATCH p=allShortestPaths((n)-[r:AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|Owns|WriteDacl|WriteOwner*1..]->(m)) RETURN p
Find interesting edges related to “ACL Abuse” that unprivileged users have against computers :
MATCH (n:User {admincount:False}) MATCH p=allShortestPaths((n)-[r:AllExtendedRights|GenericAll|GenericWrite|Owns|WriteDacl|WriteOwner|AdminTo|CanRDP|ExecuteDCOM|ForceChangePassword*1..]->(m:Computer)) RETURN p
Find if unprivileged users have rights to add members into groups :
MATCH (n:User {admincount:False}) MATCH p=allShortestPaths((n)-[r:AddMember*1..]->(m:Group)) RETURN p
Find only the AdminTo privileges (edges) of the domain users against the domain computers :
MATCH p1=shortestPath(((u1:User)-[r1:MemberOf*1..]->(g1:Group))) MATCH p2=(u1)-[:AdminTo*1..]->(c:Computer) RETURN p2
Find which groups canRDP to computers :
MATCH (n:Group) MATCH (m:Computer) MATCH p=allShortestPaths((n)-[r:CanRDP*1..]->(m)) RETURN p
The canRDP edge runs from a user or a group to a computer and incates that the principals are part of the Remote Desktop Users local group on the target system.
Find what groups have local admin rights :
MATCH p=(m:Group)-[r:AdminTo]->(n:Computer) RETURN m.name, n.name ORDER BY m.name
Find what users have local admin rights :
MATCH p=(m:User)-[r:AdminTo]->(n:Computer) RETURN m.name, n.name ORDER BY m.name
Mark a user as owned :
MATCH (n:User) WHERE n.name STARTS WITH toUpper('brobin') SET n.owned=true RETURN n
Displays all GPOs :
Match (n: GPO) return n
Displays all GPOs containing a “keyword” in their name :
Match (n: GPO) WHERE n.name CONTAINS "SERVER" return n
Find if a domain user has some interesting rights on a GPO :
MATCH p = (u: User) - [r: AllExtendedRights | GenericAll | GenericWrite | Owns | WriteDacl | WriteOwner | GpLink * 1 ..] -> (g: GPO) RETURN p LIMIT 25
If i have some fresh queries, i will post them on this page regularly.
If you also want to contribute: here is the repo.
Wish you a good day,
Phackt.