- Counts various Active Directory statistics and weaknesses. (Change `ENDS WITH ""` to `ENDS WITH "contoso.com"` to limit results to specified domain):
```
MATCH (u:User) WHERE toLower(u.name) ENDS WITH "" RETURN "Users in total" AS what, count(u) AS number UNION ALL
MATCH (u:Computer) WHERE toLower(u.name) ENDS WITH "" RETURN "Computers in total" AS what, count(u) AS number UNION ALL
MATCH (u:Group) WHERE toLower(u.name) ENDS WITH "" RETURN "Groups in total" AS what, count(u) AS number UNION ALL
MATCH (u:Domain) WHERE toLower(u.name) ENDS WITH "" RETURN "Domains in total" AS what, count(u) AS number UNION ALL
MATCH (u:OU) WHERE toLower(u.name) ENDS WITH "" RETURN "OUs in total" AS what, count(u) AS number UNION ALL
MATCH (u:GPO) WHERE toLower(u.name) ENDS WITH "" RETURN "GPOs in total" AS what, count(u) AS number UNION ALL
MATCH (u {pwdneverexpires: True}) WHERE toLower(u.name) ENDS WITH "" RETURN "Password Never Expires" AS what, count(u) AS number UNION ALL
MATCH (u {passwordnotreqd: True}) WHERE toLower(u.name) ENDS WITH "" RETURN "Password Not Required" AS what, count(u) AS number UNION ALL
MATCH (u {dontreqpreauth: true}) WHERE toLower(u.name) ENDS WITH "" RETURN "Pre-Authentication Not Required" AS what, count(u) AS number UNION ALL
MATCH (u:User {hasspn: True}) WHERE toLower(u.name) ENDS WITH "" AND NOT u.name STARTS WITH 'KRBTGT' RETURN "Kerberoastable" AS what, count(u) AS number UNION ALL
MATCH (u:User {dontreqpreauth: true}) WHERE toLower(u.name) ENDS WITH "" RETURN "ASREProastable" AS what, count(u) AS number UNION ALL
MATCH (u {admincount: True}) WHERE toLower(u.name) ENDS WITH "" RETURN "adminCount=1" AS what, count(u) AS number UNION ALL
MATCH (u) WHERE toLower(u.name) ENDS WITH "" AND u.userpassword =~ ".+" RETURN "userPassword Not Empty" AS what, count(u) AS number UNION ALL
MATCH (u:Computer {unconstraineddelegation: True}), (g:Group) WHERE toLower(u.name) ENDS WITH "" AND g.name starts with 'DOMAIN CONTROLLERS' MATCH (u) WHERE (u)-[:MemberOf]->(g) RETURN "Unconstrained Delegation Computers" AS what, count(u) AS number UNION ALL
MATCH (u {owned: true}) WHERE toLower(u.name) ENDS WITH "" RETURN "Owned Principals" AS what, count(u) AS number UNION ALL
MATCH (u {highvalue: true}) WHERE toLower(u.name) ENDS WITH "" RETURN "High Value" AS what, count(u) AS number
- Returns all objects that have SPNs set and checks whether they are allowed to delegate, have admincount set or can be used for unconstrained delegation:
```
MATCH (c {hasspn: True}) RETURN c.name as name, c.allowedtodelegate as AllowedToDelegate, c.unconstraineddelegation as UnconstrainedDelegation, c.admincount as AdminCount, c.serviceprincipalnames as SPNs
- Returns Top 100 **Outbound Control Rights** --> **Group Delegated Object Control** principals in domain and whether that object is member of high privileged group (such a `Domain Admins` or `Domain Controllers`):
MATCH (u:User {admincount: True, hasspn: True}) WHERE NOT u.name starts with 'KRBTGT' RETURN u.name, u.displayname, u.hasspn as Kerberoastable, u.description, u.objectid
MATCH (u:User {admincount: True}) WHERE NOT u.name starts with 'KRBTGT' RETURN u.name, u.displayname, u.owned as owned, u.hasspn as Kerberoastable, u.dontreqpreauth as ASREPRoastable, u.description, u.objectid
MATCH (u:User {hasspn: True}) WHERE NOT u.name starts with 'KRBTGT' RETURN u.name, u.displayname, u.description, u.objectid
```
- Return Kerberoastable users with a path to High Value groups:
```
MATCH p=shortestPath((u:User {hasspn: true})-[r:MemberOf*1..]->(g:Group {highvalue: true})) RETURN u.name AS kerberoastable_user, g.name AS high_value_group, u.displayname AS user_displayname
- Pulls Kerberoastable users and returns their **Outbound Control Rights** --> **First Degree Object Control** in domain:
```
MATCH (u:User {hasspn: True}), p=(u)-[r1]->(n)
WHERE NOT u.name starts with 'KRBTGT' AND r1.isacl=true
WITH u.name as name, LABELS(u)[1] as type,
COUNT(DISTINCT(n)) as controlled
WHERE name IS NOT NULL
RETURN type, name, controlled
ORDER BY controlled DESC
```
- Pulls Kerberoastable users and returns their **Outbound Control Rights** --> **Group Delegated Object Control** in domain and whether that object is member of high privileged group (such a `Domain Admins` or `Domain Controllers`):
```
MATCH (u:User {hasspn: True}), p=(u)-[r1:MemberOf*1..]->(g:Group)-[r2]->(n)
WHERE NOT u.name starts with 'KRBTGT' AND r2.isacl=true
WITH u.name as name, LABELS(u)[1] as type, g.highvalue as highly_privileged,
COUNT(DISTINCT(n)) as controlled
WHERE name IS NOT NULL
RETURN type, name, highly_privileged, controlled
ORDER BY controlled DESC
```
- Pulls Kerberoastable users and returns their **Outbound Control Rights** --> **Transitive Object Control** in domain (TAKES ENORMOUS TIME TO COMPUTE! You were warned):
```
MATCH (u:User {hasspn: True}), p=shortestPath((u)-[r1:MemberOf|AddMember|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns*1..]->(n))
- Returns username and number of computers where it has admin rights to for top 10 users (author: [jeffmcjunkin](https://gist.github.com/jeffmcjunkin/7b4a67bb7dd0cfbfbd83768f3aa6eb12) ):
- Returns group and number of computers that group has admin rights to - for top 10 groups (author: [jeffmcjunkin](https://gist.github.com/jeffmcjunkin/7b4a67bb7dd0cfbfbd83768f3aa6eb12) ):
- Show all users that are administrators on more than one machine (author: [jeffmcjunkin](https://gist.github.com/jeffmcjunkin/7b4a67bb7dd0cfbfbd83768f3aa6eb12) ):
- Show all users that are administrative on at least one machine, ranked by the number of machines they are admin on. (author: [jeffmcjunkin](https://gist.github.com/jeffmcjunkin/7b4a67bb7dd0cfbfbd83768f3aa6eb12) ):
```
MATCH (u:User)
WITH u
OPTIONAL MATCH (u)-[r:AdminTo]->(c:Computer)
WITH u,COUNT(c) as expAdmin
OPTIONAL MATCH (u)-[r:MemberOf*1..]->(g:Group)-[r2:AdminTo]->(c:Computer)
WHERE NOT (u)-[:AdminTo]->(c)
WITH u,expAdmin,COUNT(DISTINCT(c)) as unrolledAdmin
RETURN u.name,expAdmin,unrolledAdmin,expAdmin + unrolledAdmin as totalAdmin
- Find the most privileged groups on the domain (author: [hausec](https://hausec.com/2019/09/09/bloodhound-cypher-cheatsheet/) ):
```
MATCH (g:Group) OPTIONAL MATCH (g)-[:AdminTo]->(c1:Computer) OPTIONAL MATCH (g)-[:MemberOf*1..]->(:Group)-[:AdminTo]->(c2:Computer) WITH g, COLLECT(c1) + COLLECT(c2) AS tempVar UNWIND tempVar AS computers RETURN g.name AS GroupName,COUNT(DISTINCT(computers)) AS AdminRightCount ORDER BY AdminRightCount DESC
```
- Find groups with most local admins (either explicit admins or derivative/unrolled) (modified version of query taken from almighty [hausec](https://hausec.com/2019/09/09/bloodhound-cypher-cheatsheet/) ):
```
MATCH (g:Group) WITH g OPTIONAL MATCH (g)-[r:AdminTo]->(c1:Computer) WITH g,COUNT(c1) as explicitAdmins OPTIONAL MATCH (g)-[r:MemberOf*1..]->(a:Group)-[r2:AdminTo]->(c2:Computer) WITH g,explicitAdmins,COUNT(DISTINCT(c2)) as unrolledAdmins where g.name IS NOT NULL AND (explicitAdmins + unrolledAdmins) > 0 RETURN g.name,explicitAdmins,unrolledAdmins, explicitAdmins + unrolledAdmins as totalAdmins ORDER BY totalAdmins DESC
MATCH (u)-[:MemberOf*1..]->(g:Group) WHERE g.name starts with "ENTERPRISE ADMINS" RETURN g.name AS GroupName, count(u) AS MembersCounted UNION ALL
MATCH (u)-[:MemberOf*1..]->(g:Group) WHERE g.name starts with "DOMAIN ADMINS" RETURN g.name AS GroupName, count(u) AS MembersCounted UNION ALL
MATCH (u)-[:MemberOf*1..]->(g:Group) WHERE g.name starts with "SCHEMA ADMIN" RETURN g.name AS GroupName, count(u) AS MembersCounted UNION ALL
MATCH (u)-[:MemberOf*1..]->(g:Group) WHERE g.name starts with "ACCOUNT OPERATORS" RETURN g.name AS GroupName, count(u) AS MembersCounted UNION ALL
MATCH (u)-[:MemberOf*1..]->(g:Group) WHERE g.name starts with "BACKUP OPERATORS" RETURN g.name AS GroupName, count(u) AS MembersCounted UNION ALL
MATCH (u)-[:MemberOf*1..]->(g:Group) WHERE g.name starts with "PRINT OPERATORS" RETURN g.name AS GroupName, count(u) AS MembersCounted UNION ALL
MATCH (u)-[:MemberOf*1..]->(g:Group) WHERE g.name starts with "SERVER OPERATORS" RETURN g.name AS GroupName, count(u) AS MembersCounted UNION ALL
MATCH (u)-[:MemberOf*1..]->(g:Group) WHERE g.name starts with "DOMAIN CONTROLLERS" RETURN g.name AS GroupName, count(u) AS MembersCounted UNION ALL
MATCH (u)-[:MemberOf*1..]->(g:Group) WHERE g.name starts with "READ-ONLY DOMAIN CONTROLLERS" RETURN g.name AS GroupName, count(u) AS MembersCounted UNION ALL
MATCH (u)-[:MemberOf*1..]->(g:Group) WHERE g.name starts with "GROUP POLICY CREATOR OWNERS" RETURN g.name AS GroupName, count(u) AS MembersCounted UNION ALL
MATCH (u)-[:MemberOf*1..]->(g:Group) WHERE g.name starts with "CRYPTOGRAPHIC OPERATORS" RETURN g.name AS GroupName, count(u) AS MembersCounted UNION ALL
MATCH (u)-[:MemberOf*1..]->(g:Group) WHERE g.name starts with "DISTRIBUTED COM USERS" RETURN g.name AS GroupName, count(u) AS MembersCounted
```
### Computers
- Returns computer names and their operating system for statistics purposes
```
MATCH (c:Computer) WHERE c.operatingsystem is not null RETURN c.name as Name, c.operatingsystem as OS
```
- Returns a summary report of machines grouped by their operating systems versions, along with number of machines running specific OS version:
```
MATCH (c:Computer) WHERE c.operatingsystem is not null MATCH (n:Computer {operatingsystem: c.operatingsystem}) RETURN c.operatingsystem as OS, count(distinct n) AS Number ORDER BY Number DESC
```
- Returns non-DC computers that enable unconstrained delegation along with their LDAP DN paths and operating systems.:
```
MATCH (c:Computer {unconstraineddelegation: True}), (g:Group) WHERE g.name starts with 'DOMAIN CONTROLLERS' MATCH (c) WHERE NOT (c)-[:MemberOf]->(g) RETURN c.name, c.distinguishedname, c.operatingsystem
- Riccardo Ancarani's cypher queries (src: [GPOPowerParser](https://github.com/RiccardoAncarani/GPOPowerParser)) useful for any lateral movement insights:
- Retrieves nodes that contain UNC paths to SMB shares in their description fields:
```
MATCH (n) WHERE n.description CONTAINS '\\\\' RETURN n.name, n.description
```
- Print **Security Solutions** (think SIEM, EDRs, AVs, Anomaly detection systems, etc) deployed in the company by searching for keywords in _name, description, distinguishedname_ of all objects (User, Group, Computer, OU, ...)
- Find all other Rights Domain Users shouldn't have (author: [jeffmcjunkin](https://gist.github.com/jeffmcjunkin/7b4a67bb7dd0cfbfbd83768f3aa6eb12) ):
```
MATCH p=(m:Group)-[r:Owns|WriteDacl|GenericAll|WriteOwner|ExecuteDCOM|GenericWrite|AllowedToDelegate|ForceChangePassword]->(n:Computer) WHERE m.name STARTS WITH 'DOMAIN USERS' RETURN p
- Authored by **Knavesec** on a #cypher_queries Bloodhound slack: Prints graph paths of the returns yielded by query in `p` variable. Modify the first line to determine paths you would like to be printed (for later grepping, searching). Example:
```
match p=shortestPath((g:Group)-[*1..]->(n {highvalue:true})) where g.objectid ends with "-513"
WITH [node in nodes(p) | coalesce(node.name, '')] as nodeLabels,
[rel in relationships(p) | type(rel)] as relationshipLabels,
length(p) as path_len
WITH reduce(path='', x in range(0,path_len-1) | path + nodeLabels[x] + ' - ' + relationshipLabels[x] + ' -> ') as path,