diff --git a/Queries.json b/Queries.json index f9d21b5..e019153 100644 --- a/Queries.json +++ b/Queries.json @@ -88,8 +88,8 @@ ], "category": "Domain Information", "description": "All incoming and local paths for a specific computer; incoming from domain objects and paths local inside the computer.", - "query": "// Replace 'HOSTNAME' with the computer's shortname eg. 'SRV01', not FQDN\nMATCH p=(n:Base)-[:RemoteInteractiveLogonPrivilege|AdminTo|CanRDP|LocalToComputer|MemberOfLocalGroup]-(m:Base)\nWHERE m.name CONTAINS 'HOSTNAME'\nAND m.name CONTAINS '.' // Only see computer-related objects (eg. not AD Groups)\nRETURN p", - "revision": 1, + "query": "// Replace 'HOSTNAME' with the computer's shortname eg. 'SRV01', not FQDN\nMATCH p=(n:Base)-[:RemoteInteractiveLogonRight|AdminTo|CanRDP|LocalToComputer|MemberOfLocalGroup]-(m:Base)\nWHERE m.name CONTAINS 'HOSTNAME'\nAND m.name CONTAINS '.' // Only see computer-related objects (eg. not AD Groups)\nRETURN p", + "revision": 2, "resources": [], "acknowledgements": [ "Martin Sohn Christensen, @martinsohndk" @@ -178,6 +178,24 @@ "Martin Sohn Christensen, @martinsohndk" ] }, + { + "name": "All GPOs applied to a specific Computer", + "guid": "1d75a21e-0d34-40c5-9360-281b60737d87", + "prebuilt": false, + "platforms": [ + "Active Directory" + ], + "category": "Domain Information", + "description": "View all GPOs that are applied to any specific computer. This query identifies GPOs that are applied at both the Domain Level and the OU level, saving time in large Active Directory environments where GPO inheritance is complex. Replace \"COMPUTER_NAME\" with the target computer name or a substring. Note this does not take OU 'Block inheritance' and GPO 'No Override' into account.", + "query": "// Replace \"HOSTNAME/FQDN\" with the computer's\nMATCH p=(c:Computer)<-[:Contains*..]-(:Base)<-[:GPLink]-(:GPO)\nWHERE toLower(c.name) CONTAINS toLower(\"HOSTNAME/FQDN\")\nRETURN p", + "revision": 1, + "resources": [ + "https://learn.microsoft.com/en-us/previous-versions/windows/desktop/Policy/overriding-and-blocking-group-policy" + ], + "acknowledgements": [ + "Adnan Ullah Khan, @auk0x01" + ] + }, { "name": "Accounts with SID History to a same-domain account", "guid": "275d2d58-0cad-4cad-8103-e0874cece666", @@ -295,6 +313,20 @@ "Martin Sohn Christensen, @martinsohndk" ] }, + { + "name": "Enrollment rights on certificate templates published to Enterprise CA with vulnerable HTTP(S) endpoint (ESC8)", + "guid": "1c1435b1-bad0-49f2-ba7d-932e047c0af4", + "prebuilt": true, + "platforms": [ + "Active Directory" + ], + "category": "Active Directory Certificate Services", + "description": null, + "query": "MATCH p = (:Base)-[:Enroll|GenericAll|AllExtendedRights]->(ct:CertTemplate)-[:PublishedTo]->(eca:EnterpriseCA)\nWHERE eca.hasvulnerableendpoint = True\nRETURN p\nLIMIT 1000", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, { "name": "Foreign principals in Tier Zero / High Value targets", "guid": "95bec736-86ef-4017-8465-9b9b66548b17", @@ -333,8 +365,8 @@ ], "category": "Shortest Paths", "description": null, - "query": "MATCH p=shortestPath((s:Base)-[:AD_ATTACK_PATHS*1..]->(t:Base))\nWHERE (s:Tag_Owned)\nAND s<>t\nRETURN p\nLIMIT 1000", - "revision": 2, + "query": "MATCH p=shortestPath((s:Base)-[:AD_ATTACK_PATHS*1..]->(t:Base))\nWHERE ((s:Tag_Owned) OR COALESCE(s.system_tags, '') CONTAINS 'owned')\nAND s<>t\nRETURN p\nLIMIT 1000", + "revision": 3, "resources": [], "acknowledgements": [] }, @@ -347,8 +379,8 @@ ], "category": "Kerberos Interaction", "description": null, - "query": "MATCH (u:User)\nWHERE (u:Tag_Tier_Zero) AND u.hasspn=true\nAND u.enabled = true\nAND NOT u.objectid ENDS WITH '-502'\nAND NOT COALESCE(u.gmsa, false) = true\nAND NOT COALESCE(u.msa, false) = true \nRETURN u\nLIMIT 100", - "revision": 1, + "query": "MATCH (u:User)\nWHERE ((u:Tag_Tier_Zero) OR COALESCE(u.system_tags, '') CONTAINS 'admin_tier_0') AND u.hasspn=true\nAND u.enabled = true\nAND NOT u.objectid ENDS WITH '-502'\nAND NOT COALESCE(u.gmsa, false) = true\nAND NOT COALESCE(u.msa, false) = true \nRETURN u\nLIMIT 100", + "revision": 2, "resources": [ "https://attack.mitre.org/techniques/T1558/003/" ], @@ -368,6 +400,20 @@ "resources": [], "acknowledgements": [] }, + { + "name": "Entra Users with Entra Admin Role approval (group delegated)", + "guid": "b70a6512-21e1-4d6e-926a-fba44646085d", + "prebuilt": true, + "platforms": [ + "Azure" + ], + "category": "General", + "description": null, + "query": "MATCH p = (:AZUser)-[:AZMemberOf]->(:AZGroup)-[:AZRoleApprover]->(:AZRole)\nRETURN p LIMIT 100", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, { "name": "Domains affected by Exchange privilege escalation risk", "guid": "f2d09c94-b6f2-4901-9a2d-f8bacd61edc7", @@ -416,6 +462,22 @@ "resources": [], "acknowledgements": [] }, + { + "name": "Locations of Owned objects", + "guid": "350b8b8a-ea4c-44f3-874b-c9316de6c41b", + "prebuilt": false, + "platforms": [ + "Azure" + ], + "category": "General", + "description": null, + "query": "MATCH p = (t:AZBase)<-[:AZContains*1..]-(:AZTenant)\nWHERE ((t:Tag_Owned) OR COALESCE(t.system_tags, '') CONTAINS 'owned')\nRETURN p\nLIMIT 1000", + "revision": 1, + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] + }, { "name": "Enrollment rights on published certificate templates with no security extension", "guid": "0677b70c-4e04-4e89-a6a2-f5764604a6a7", @@ -504,22 +566,6 @@ "Martin Sohn Christensen, @martinsohndk" ] }, - { - "name": "Collection health of specific computer", - "guid": "bb95c9c5-984c-4057-a430-000d684c069a", - "prebuilt": false, - "platforms": [ - "Active Directory" - ], - "category": "Domain Information", - "description": "Returns Local groups and their members, and Principals with privileges", - "query": "MATCH p=(m:Base)-[:RemoteInteractiveLogonRight|AdminTo|CanRDP|LocalToComputer|MemberOfLocalGroup]-(n:Base)\n\n// Insert computer FQDN\nWHERE m.name ENDS WITH \"HOSTNAME.DOMAIN.LOCAL\"\n\nRETURN p", - "revision": 1, - "resources": [], - "acknowledgements": [ - "Martin Sohn Christensen, @martinsohndk" - ] - }, { "name": "Principals with foreign domain group membership", "guid": "8fb3214a-5a75-4ecd-b293-c121abd94b4b", @@ -574,9 +620,9 @@ "Active Directory" ], "category": "Shortest Paths", - "description": null, + "description": "WARNING! MANY-TO-MANY SHORTEST PATH QUERIES USE EXCESSIVE SYSTEM RESOURCES AND TYPICALLY WILL NOT COMPLETE", "query": "// MANY TO MANY SHORTEST PATH QUERIES USE EXCESSIVE SYSTEM RESOURCES AND TYPICALLY WILL NOT COMPLETE\nMATCH p=shortestPath((s:Tag_Owned)-[:AD_ATTACK_PATHS*1..]->(t:Base))\nWHERE s<>t\nAND ((t:Tag_Tier_Zero) OR COALESCE(t.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p\nLIMIT 1000", - "revision": 2, + "revision": 4, "resources": [], "acknowledgements": [] }, @@ -608,6 +654,21 @@ "resources": [], "acknowledgements": [] }, + { + "name": "Synced Entra Users with Entra Admin Role approval (group delegated)", + "guid": "ead56ecb-fb88-427c-8f39-75e774bb9a0a", + "prebuilt": true, + "platforms": [ + "Active Directory", + "Azure" + ], + "category": "Cross Platform Attack Paths", + "description": null, + "query": "MATCH p = (:User)-[:SyncedToEntraUser]->(:AZUser)-[:AZMemberOf]->(:AZGroup)-[:AZRoleApprover]->(:AZRole)\nRETURN p LIMIT 100", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, { "name": "Domain controllers with weak certificate binding enabled", "guid": "a2444d99-10b5-412d-8fea-4b063cfddd2c", @@ -809,6 +870,21 @@ "Martin Sohn Christensen, @martinsohndk" ] }, + { + "name": "Synced Entra Users with Entra Admin Roles group delegated eligibility", + "guid": "bc610e20-e5c0-41f3-9e8e-7378f87a3f71", + "prebuilt": true, + "platforms": [ + "Active Directory", + "Azure" + ], + "category": "Cross Platform Attack Paths", + "description": null, + "query": "MATCH p = (:User)-[:SyncedToEntraUser]->(:AZUser)-[:AZMemberOf]->(:AZGroup)-[:AZRoleEligible]->(:AZRole)\nRETURN p LIMIT 100", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, { "name": "Computers with membership in Protected Users", "guid": "a26372f4-2e92-49f6-8993-6657fbc1569a", @@ -818,8 +894,8 @@ ], "category": "NTLM Relay Attacks", "description": null, - "query": "MATCH p = (:Base)-[:MemberOf*1..]->(g:Group)\nWHERE g.objectid ENDS WITH \"-525\"\nRETURN p LIMIT 1000", - "revision": 1, + "query": "MATCH p = (:Base)-[:MemberOf*1..]->(g:Group)\nWHERE g.objectid ENDS WITH '-525'\nRETURN p LIMIT 1000", + "revision": 2, "resources": [], "acknowledgements": [] }, @@ -891,6 +967,20 @@ "kaasimir, @kaasimir" ] }, + { + "name": "Tier Zero principals without AdminSDHolder protection", + "guid": "82ce5e2e-415b-489d-b891-304e8bb25998", + "prebuilt": true, + "platforms": [ + "Active Directory" + ], + "category": "Active Directory Hygiene", + "description": null, + "query": "MATCH (n:Base)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nAND n.adminsdholderprotected = false\nRETURN n\nLIMIT 500", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, { "name": "Domains where any user can join a computer to the domain", "guid": "421921fa-bc0f-4659-9680-b7481adcb132", @@ -900,8 +990,8 @@ ], "category": "Active Directory Hygiene", "description": null, - "query": "MATCH (n:Domain)\nWHERE n.machineaccountquota > 0\nRETURN n", - "revision": 1, + "query": "MATCH (d:Domain)\nWHERE d.machineaccountquota > 0\nRETURN d", + "revision": 2, "resources": [ "https://learn.microsoft.com/en-us/troubleshoot/windows-server/active-directory/default-workstation-numbers-join-domain", "https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/security-policy-settings/add-workstations-to-domain" @@ -947,10 +1037,10 @@ "platforms": [ "Azure" ], - "category": "Kerberos Interaction", + "category": "General", "description": "Maps the structure of Azure Management", "query": "MATCH p = (:AZTenant)-[:AZContains*1..]->(:AZResourceGroup)\nRETURN p\nLIMIT 1000", - "revision": 1, + "revision": 2, "resources": [ "https://learn.microsoft.com/en-us/azure/governance/management-groups/overview" ], @@ -1050,6 +1140,20 @@ "resources": [], "acknowledgements": [] }, + { + "name": "Entra Users with Entra Admin Roles group delegated eligibility", + "guid": "2e36c81b-25ed-40ba-afec-5f5f6443e095", + "prebuilt": true, + "platforms": [ + "Azure" + ], + "category": "General", + "description": null, + "query": "MATCH p = (:AZUser)-[:AZMemberOf]->(:AZGroup)-[:AZRoleEligible]->(:AZRole)\nRETURN p LIMIT 100", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, { "name": "Principals with DCSync privileges", "guid": "6e9beb8a-ad14-43de-bda1-644d174a5906", @@ -1102,7 +1206,7 @@ ] }, { - "name": "Non-Tier Zero account with excessive control", + "name": "Non-Tier Zero object with excessive control", "guid": "944cecfe-519b-4318-b226-e8520161b454", "prebuilt": false, "platforms": [ @@ -1111,7 +1215,7 @@ "category": "Dangerous Privileges", "description": "Returns non-Tier Zero principals with >= 1000 direct rights to other principals. This does not include rights from group memberships.", "query": "MATCH (n:Base)-[r:AD_ATTACK_PATHS]->(m:Base)\nWHERE NOT r:MemberOf\nAND NOT ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nWITH n, COLLECT(DISTINCT(m)) AS endNodes\nWHERE SIZE(endNodes) >= 1000\nRETURN n", - "revision": 3, + "revision": 4, "resources": [], "acknowledgements": [ "Martin Sohn Christensen, @martinsohndk" @@ -1126,9 +1230,13 @@ ], "category": "Active Directory Hygiene", "description": null, - "query": "MATCH p=(n:Computer)-[r:MemberOf]->(g:Group)\nWHERE NOT g.objectid ENDS WITH \"-515\" // Domain Computers\nAND NOT g.objectid ENDS WITH \"-516\" // Domain Controllers\nAND NOT g.objectid ENDS WITH \"-521\" // Read-Only Domain Controllers\nAND r.isprimarygroup = true\nRETURN p", - "revision": 1, - "resources": [], + "query": "MATCH p=(n:Computer)-[r:MemberOf]->(g:Group)\nWHERE NOT g.objectid ENDS WITH \"-515\" // Domain Computers\nAND NOT n.isdc = true\nAND NOT n.isreadonlydc = true\nAND r.isprimarygroup = true\nRETURN p", + "revision": 2, + "resources": [ + "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-ada3/e12954a4-6865-4432-94e6-00c310ca87c0", + "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/5dbcf875-e802-4357-a6e2-1bdff19ff9b5", + "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/73d11ea7-e634-453e-944d-559654cc91c5" + ], "acknowledgements": [ "Martin Sohn Christensen, @martinsohndk" ] @@ -1220,6 +1328,21 @@ "resources": [], "acknowledgements": [] }, + { + "name": "Synced Entra Users with Entra Admin Role approval (direct)", + "guid": "1bfe2d75-0a51-4dbe-abc6-178cc0e4476f", + "prebuilt": true, + "platforms": [ + "Active Directory", + "Azure" + ], + "category": "Cross Platform Attack Paths", + "description": null, + "query": "MATCH p = (:User)-[:SyncedToEntraUser]->(:AZUser)-[:AZRoleApprover]->(:AZRole)\nRETURN p LIMIT 100", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, { "name": "Object name conflict", "guid": "c561c4f8-ea45-453f-85a2-3fc2e20e7f8c", @@ -1316,6 +1439,20 @@ "resources": [], "acknowledgements": [] }, + { + "name": "CA Administrators and CA Managers (ESC7)", + "guid": "77a708b8-962e-4c3d-ad70-e994126aaeba", + "prebuilt": true, + "platforms": [ + "Active Directory" + ], + "category": "Active Directory Certificate Services", + "description": null, + "query": "MATCH p = (:Base)-[:ManageCertificates|ManageCA]->(:EnterpriseCA)\nRETURN p\nLIMIT 1000", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, { "name": "Domains exempting privileged groups from AdminSDHolder protections", "guid": "79f8d8f9-8291-4bf7-a13a-15989018075f", @@ -1343,8 +1480,8 @@ ], "category": "Active Directory Hygiene", "description": null, - "query": "MATCH (n:Base)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nAND (n:User or n:Computer)\nWITH n\nOPTIONAL MATCH (n)-[:MemberOf*1..]->(m:Group)\nWHERE m.objectid ENDS WITH '-519'\nWITH n, m\nWHERE m IS NULL\nRETURN n", - "revision": 1, + "query": "// Get all Tier Zero accounts that are members of Denied RODC Password Replication Group\nMATCH (n:Base)-[:MemberOf*1..]->(m:Group)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nAND (n:User OR n:Computer)\nAND m.objectid ENDS WITH '-519'\nWITH COLLECT(n.objectid) AS MembersOfDeniedGroup\n\n// Get all Tier Zero accounts\nMATCH (x:Base)\nWHERE ((x:Tag_Tier_Zero) OR COALESCE(x.system_tags, '') CONTAINS 'admin_tier_0')\nAND (x:User OR x:Computer)\n\n// Filter the members of Denied RODC Password Replication Group\nAND NOT x.objectid IN MembersOfDeniedGroup\nRETURN x", + "revision": 2, "resources": [], "acknowledgements": [ "Martin Sohn Christensen, @martinsohndk" @@ -1439,8 +1576,8 @@ ], "category": "General", "description": null, - "query": "MATCH p = (:AZBase)-[:AZGlobalAdmin*1..]->(:AZTenant)\nRETURN p\nLIMIT 1000", - "revision": 1, + "query": "MATCH p=(:AZBase)-[:AZHasRole*1..]->(t:AZRole)\nWHERE t.name =~ '(?i)Global Administrator.*'\nRETURN p\nLIMIT 1000", + "revision": 2, "resources": [], "acknowledgements": [] }, @@ -1565,9 +1702,23 @@ "Azure" ], "category": "Shortest Paths", - "description": null, + "description": "WARNING! MANY-TO-MANY SHORTEST PATH QUERIES USE EXCESSIVE SYSTEM RESOURCES AND TYPICALLY WILL NOT COMPLETE", "query": "MATCH p=shortestPath((s:AZApp)-[:AZ_ATTACK_PATHS*1..]->(t:AZBase))\nWHERE ((t:Tag_Tier_Zero) OR COALESCE(t.system_tags, '') CONTAINS 'admin_tier_0') AND s<>t\nRETURN p\nLIMIT 1000", - "revision": 2, + "revision": 3, + "resources": [], + "acknowledgements": [] + }, + { + "name": "Compromising permissions on ADCS nodes (ESC5)", + "guid": "396c7b67-fb5d-4c04-bb13-8007f0dfc9b1", + "prebuilt": true, + "platforms": [ + "Active Directory" + ], + "category": "Active Directory Certificate Services", + "description": null, + "query": "MATCH p = (n:Base)-[:Owns|WriteOwner|WriteDacl|GenericAll|GenericWrite]->(m:Base)\nWHERE m.distinguishedname CONTAINS \"PUBLIC KEY SERVICES\"\nAND NOT n.objectid ENDS WITH \"-512\" // Domain Admins\nAND NOT n.objectid ENDS WITH \"-519\" // Enterprise Admins\nAND NOT n.objectid ENDS WITH \"-544\" // Administrators\nRETURN p\nLIMIT 1000", + "revision": 1, "resources": [], "acknowledgements": [] }, @@ -1579,7 +1730,7 @@ "Azure" ], "category": "Shortest Paths", - "description": null, + "description": "WARNING! MANY-TO-MANY SHORTEST PATH QUERIES USE EXCESSIVE SYSTEM RESOURCES AND TYPICALLY WILL NOT COMPLETE", "query": "MATCH p=shortestPath((s:AZUser)-[:AZ_ATTACK_PATHS*1..]->(t:AZBase))\nWHERE ((t:Tag_Tier_Zero) OR COALESCE(t.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p\nLIMIT 1000", "revision": 3, "resources": [], @@ -1655,9 +1806,9 @@ "Azure" ], "category": "Shortest Paths", - "description": null, + "description": "WARNING! MANY-TO-MANY SHORTEST PATH QUERIES USE EXCESSIVE SYSTEM RESOURCES AND TYPICALLY WILL NOT COMPLETE", "query": "MATCH p=shortestPath((s:AZBase)-[:AZ_ATTACK_PATHS*1..]->(t:AZSubscription))\nWHERE s<>t\nRETURN p\nLIMIT 1000", - "revision": 2, + "revision": 3, "resources": [], "acknowledgements": [] }, @@ -1693,6 +1844,20 @@ "Martin Sohn Christensen, @martinsohndk" ] }, + { + "name": "Location of AdminSDHolder Protected objects", + "guid": "3408ccaf-1f42-4c10-b09a-e986661f84d7", + "prebuilt": true, + "platforms": [ + "Active Directory" + ], + "category": "Domain Information", + "description": null, + "query": "MATCH p = (n:Base)<-[:Contains*1..]-(:Domain)\nWHERE n.adminsdholderprotected = True\nRETURN p\nLIMIT 1000", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, { "name": "Computers with the outgoing NTLM setting set to Deny all", "guid": "a9ddca74-feeb-4dbf-8b0f-de08b3cfa8a6", @@ -1707,6 +1872,36 @@ "resources": [], "acknowledgements": [] }, + { + "name": "Enrollment rights on certificate templates published to Enterprise CA with User Specified SAN enabled (ESC6)", + "guid": "ab14e9dc-996c-4737-878c-583c19cdbf5a", + "prebuilt": true, + "platforms": [ + "Active Directory" + ], + "category": "Active Directory Certificate Services", + "description": null, + "query": "MATCH p = (:Base)-[:Enroll|GenericAll|AllExtendedRights]->(ct:CertTemplate)-[:PublishedTo]->(eca:EnterpriseCA)\nWHERE eca.isuserspecifiessanenabled = True\nRETURN p\nLIMIT 1000", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, + { + "name": "Locations of Owned objects", + "guid": "c88bfab4-3da0-4b36-b71d-7b324ebd2243", + "prebuilt": false, + "platforms": [ + "Active Directory" + ], + "category": "Domain Information", + "description": null, + "query": "MATCH p = (t:Base)<-[:Contains*1..]-(:Domain)\nWHERE ((t:Tag_Owned) OR COALESCE(t.system_tags, '') CONTAINS 'owned')\nRETURN p\nLIMIT 1000", + "revision": 1, + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] + }, { "name": "Users with passwords not rotated in over 1 year", "guid": "be70d1bd-b7eb-40b0-971c-eefc50eca032", @@ -1738,20 +1933,18 @@ ] }, { - "name": "Trace ACE inheritance", - "guid": "8c5454df-3ae8-412c-b271-3c4c55df7141", - "prebuilt": false, + "name": "Entra Users with Entra Admin Role direct eligibility", + "guid": "b87899ce-3a51-401a-ae39-ef271b566e3d", + "prebuilt": true, "platforms": [ - "Active Directory" + "Azure" ], - "category": "Domain Information", - "description": "When BloodHound shows that an inherited ACE applies to an object it does not show the source/where it is inherited from from (OU, Container, Domain root) - the source is where it should be remediated. This query can sometimes find the source of an inherited ACE, but only works if the ACE is set to also apply to the source itself.", - "query": "// Replace INSERT_OBJECT_ID with the affected principal\n// Replace 'GenericAll' with the specific edge you're tracing\nWITH \"INSERT_OBJECT_ID\" as OID\nMATCH p=()-[:GenericAll {isacl:true,isinherited:false}]->()-[:Contains*1..]->(:Base{objectid:OID})\nWHERE NONE(ou in NODES(p) WHERE ou:OU AND ou.isaclprotected IS NOT NULL)\nRETURN p", + "category": "General", + "description": null, + "query": "MATCH p = (:AZUser)-[:AZRoleEligible]->(:AZRole)\nRETURN p LIMIT 100", "revision": 1, "resources": [], - "acknowledgements": [ - "Walter.Legowski, @SadProcessor" - ] + "acknowledgements": [] }, { "name": "Foreign Service Principals With any Abusable MS Graph App Role Assignment", @@ -1883,6 +2076,20 @@ "Martin Sohn Christensen, @martinsohndk" ] }, + { + "name": "AdminSDHolder to protected objects relationship", + "guid": "c751f95c-8bb0-4be4-b027-84f5709c91d2", + "prebuilt": true, + "platforms": [ + "Active Directory" + ], + "category": "Active Directory Hygiene", + "description": null, + "query": "MATCH p=(n)-[:ProtectAdminGroups]->(m)\nRETURN p\nLIMIT 1000", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, { "name": "Disabled Tier Zero / High Value principals", "guid": "d65a801f-d3ef-4b7e-8030-99ebfd6dad12", @@ -1933,6 +2140,20 @@ "Martin Sohn Christensen, @martinsohndk" ] }, + { + "name": "Accounts with smart card required in domains where smart account passwords do not expire", + "guid": "bba7985e-f32a-4c62-b1b0-0365bf1455e6", + "prebuilt": true, + "platforms": [ + "Active Directory" + ], + "category": "Active Directory Hygiene", + "description": null, + "query": "MATCH p=(s:Domain)-[:Contains*1..]->(t:Base)\nWHERE s.expirepasswordsonsmartcardonlyaccounts = false\nAND t.enabled = true\nAND t.smartcardrequired = true\nRETURN p", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, { "name": "Overprivileged Microsoft Entra Connect accounts", "guid": "9e6e75b4-9ecc-45d4-a39b-b6427b813f0a", @@ -2121,6 +2342,20 @@ "resources": [], "acknowledgements": [] }, + { + "name": "Entra Users with Entra Admin Role approval (direct)", + "guid": "74d7993c-24af-4df7-8402-5c6fb22d088c", + "prebuilt": true, + "platforms": [ + "Azure" + ], + "category": "General", + "description": null, + "query": "MATCH p = (:AZUser)-[:AZRoleApprover]->(:AZRole)\nRETURN p LIMIT 100", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, { "name": "Accounts with SID History", "guid": "8172d52c-a975-49bd-9180-5b6efc59c9ab", @@ -2160,8 +2395,8 @@ ], "category": "Shortest Paths", "description": null, - "query": "MATCH p=shortestPath((s)-[:AD_ATTACK_PATHS*1..]->(t:Tag_Tier_Zero))\nWHERE s<>t\nRETURN p\nLIMIT 1000", - "revision": 2, + "query": "MATCH p=shortestPath((s)-[:AD_ATTACK_PATHS*1..]->(t:Base))\nWHERE ((t:Tag_Tier_Zero) OR (COALESCE(t.system_tags, '') CONTAINS 'admin_tier_0'))\nAND s<>t\nRETURN p\nLIMIT 1000", + "revision": 3, "resources": [], "acknowledgements": [] }, @@ -2421,7 +2656,7 @@ "Azure" ], "category": "Shortest Paths", - "description": null, + "description": "WARNING! MANY-TO-MANY SHORTEST PATH QUERIES USE EXCESSIVE SYSTEM RESOURCES AND TYPICALLY WILL NOT COMPLETE", "query": "MATCH p=shortestPath((s:AZBase)-[:AZ_ATTACK_PATHS*1..]->(t:AZRole))\nWHERE t.name =~ '(?i)Global Administrator|User Administrator|Cloud Application Administrator|Authentication Policy Administrator|Exchange Administrator|Helpdesk Administrator|Privileged Authentication Administrator|Privileged Role Administrator' AND s<>t\nRETURN p\nLIMIT 1000", "revision": 3, "resources": [], @@ -2583,6 +2818,21 @@ "Martin Sohn Christensen, @martinsohndk" ] }, + { + "name": "Synced Entra Users with Entra Admin Role direct eligibility", + "guid": "ea82e359-725c-4881-83e9-35007e859cf5", + "prebuilt": true, + "platforms": [ + "Active Directory", + "Azure" + ], + "category": "Cross Platform Attack Paths", + "description": null, + "query": "MATCH p = (:User)-[:SyncedToEntraUser]->(:AZUser)-[:AZRoleEligible]->(:AZRole)\nRETURN p LIMIT 100", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, { "name": "Tier Zero / High Value enabled users not requiring smart card authentication", "guid": "867f9f17-c149-4c4b-ad84-9a807622ff8c", @@ -2696,8 +2946,8 @@ ], "category": "Dangerous Privileges", "description": null, - "query": "MATCH p=(s:Group)-[:AD_ATTACK_PATHS]->(:Base)\nWHERE s.objectid ENDS WITH '-513'\nRETURN p\nLIMIT 1000", - "revision": 2, + "query": "MATCH p=(s:Group)-[r:AD_ATTACK_PATHS]->(:Base)\nWHERE s.objectid ENDS WITH '-513'\nAND NOT r:MemberOf\nRETURN p\nLIMIT 1000", + "revision": 3, "resources": [], "acknowledgements": [] } diff --git a/README.md b/README.md index a03d8ab..772194e 100644 --- a/README.md +++ b/README.md @@ -80,13 +80,15 @@ Command line usage is easy with the [BloodHound Operator](https://github.com/Sad First load the `Queries.json`: ```powershell -> $queries = Invoke-RestMethod "https://raw.githubusercontent.com/SpecterOps/BloodHoundQueryLibrary/refs/heads/main/Queries.json" +$queries = Invoke-RestMethod "https://raw.githubusercontent.com/SpecterOps/BloodHoundQueryLibrary/refs/heads/main/Queries.json" ``` Example: Run a query in BloodHound: ```powershell -> $queries[0] | BHInvoke +$queries[0] | BHInvoke +``` +``` Name : Tier Zero / High Value external Entra ID users @@ -104,10 +106,32 @@ Timestamp : 17-06-2025 13:55:27 Duration : 00:00:00.0265562 ``` -Example: Import a few queries to BloodHound's Custom Searches: +Example: Import a few queries to BloodHound's Custom Searches: + +```powershell +$queries[0..4] | New-BHPathQuery +``` + +Example: Test all queries ```powershell -> $queries[0..4] | New-BHPathQuery +$results = [System.Collections.ArrayList]::new() +$queries | % { + "$($results.Count + 1)/$($queries.Count) $($_.name)" + $results.Add([PSCustomObject]@{ + Name = $_.name + Time = (Measure-Command { + $errorMsg = $null + try { + $result = $_ | BHInvoke -WarningAction "Stop" + } catch { + $errorMsg = $_.Exception.Message + } + }).TotalSeconds + Result = $result + Error = $errorMsg + }) | Out-Null +} ``` ## Contributing diff --git a/docs/security-assessment-mapping.json b/docs/security-assessment-mapping.json index f45f5ad..5b3e351 100644 --- a/docs/security-assessment-mapping.json +++ b/docs/security-assessment-mapping.json @@ -1242,7 +1242,7 @@ { "bloodhound_query": { "guid": "944cecfe-519b-4318-b226-e8520161b454", - "name": "Non-Tier Zero account with excessive control" + "name": "Non-Tier Zero object with excessive control" }, "maps_to": [ { diff --git a/queries/Accounts with smart card required in domains where smart account passwords do not expire.yml b/queries/Accounts with smart card required in domains where smart account passwords do not expire.yml new file mode 100644 index 0000000..bcdbab2 --- /dev/null +++ b/queries/Accounts with smart card required in domains where smart account passwords do not expire.yml @@ -0,0 +1,15 @@ +name: Accounts with smart card required in domains where smart account passwords do not expire +guid: bba7985e-f32a-4c62-b1b0-0365bf1455e6 +prebuilt: true +platforms: Active Directory +category: Active Directory Hygiene +description: +query: |- + MATCH p=(s:Domain)-[:Contains*1..]->(t:Base) + WHERE s.expirepasswordsonsmartcardonlyaccounts = false + AND t.enabled = true + AND t.smartcardrequired = true + RETURN p +revision: 1 +resources: +acknowledgements: diff --git a/queries/AdminSDHolder to protected objects relationship.yml b/queries/AdminSDHolder to protected objects relationship.yml new file mode 100644 index 0000000..df1d9cd --- /dev/null +++ b/queries/AdminSDHolder to protected objects relationship.yml @@ -0,0 +1,13 @@ +name: AdminSDHolder to protected objects relationship +guid: c751f95c-8bb0-4be4-b027-84f5709c91d2 +prebuilt: true +platforms: Active Directory +category: Active Directory Hygiene +description: +query: |- + MATCH p=(n)-[:ProtectAdminGroups]->(m) + RETURN p + LIMIT 1000 +revision: 1 +resources: +acknowledgements: diff --git a/queries/All GPOs applied to a specific computer.yml b/queries/All GPOs applied to a specific computer.yml index 38f02a3..38b560d 100644 --- a/queries/All GPOs applied to a specific computer.yml +++ b/queries/All GPOs applied to a specific computer.yml @@ -1,7 +1,7 @@ name: All GPOs applied to a specific Computer guid: 1d75a21e-0d34-40c5-9360-281b60737d87 prebuilt: false -platform: Active Directory +platforms: Active Directory category: Domain Information description: View all GPOs that are applied to any specific computer. This query identifies GPOs that are applied at both the Domain Level and the OU level, saving time in large Active Directory environments where GPO inheritance is complex. Replace "COMPUTER_NAME" with the target computer name or a substring. Note this does not take OU 'Block inheritance' and GPO 'No Override' into account. query: |- diff --git a/queries/All Global Administrators.yml b/queries/All Global Administrators.yml index 7077923..4d30e57 100644 --- a/queries/All Global Administrators.yml +++ b/queries/All Global Administrators.yml @@ -5,10 +5,11 @@ platforms: Azure category: General description: query: |- - MATCH p = (:AZBase)-[:AZGlobalAdmin*1..]->(:AZTenant) + MATCH p=(:AZBase)-[:AZHasRole*1..]->(t:AZRole) + WHERE t.name =~ '(?i)Global Administrator.*' RETURN p LIMIT 1000 -revision: 1 +revision: 2 resources: acknowledgements: diff --git a/queries/All incoming and local paths for a specific computer.yml b/queries/All incoming and local paths for a specific computer.yml index e5604cf..9505ef5 100644 --- a/queries/All incoming and local paths for a specific computer.yml +++ b/queries/All incoming and local paths for a specific computer.yml @@ -6,11 +6,11 @@ category: Domain Information description: All incoming and local paths for a specific computer; incoming from domain objects and paths local inside the computer. query: |- // Replace 'HOSTNAME' with the computer's shortname eg. 'SRV01', not FQDN - MATCH p=(n:Base)-[:RemoteInteractiveLogonPrivilege|AdminTo|CanRDP|LocalToComputer|MemberOfLocalGroup]-(m:Base) + MATCH p=(n:Base)-[:RemoteInteractiveLogonRight|AdminTo|CanRDP|LocalToComputer|MemberOfLocalGroup]-(m:Base) WHERE m.name CONTAINS 'HOSTNAME' AND m.name CONTAINS '.' // Only see computer-related objects (eg. not AD Groups) RETURN p -revision: 1 +revision: 2 resources: acknowledgements: Martin Sohn Christensen, @martinsohndk diff --git a/queries/CA Administrators and CA Managers (ESC7).yml b/queries/CA Administrators and CA Managers (ESC7).yml new file mode 100644 index 0000000..0d6f522 --- /dev/null +++ b/queries/CA Administrators and CA Managers (ESC7).yml @@ -0,0 +1,13 @@ +name: CA Administrators and CA Managers (ESC7) +guid: 77a708b8-962e-4c3d-ad70-e994126aaeba +prebuilt: true +platforms: Active Directory +category: Active Directory Certificate Services +description: +query: |- + MATCH p = (:Base)-[:ManageCertificates|ManageCA]->(:EnterpriseCA) + RETURN p + LIMIT 1000 +revision: 1 +resources: +acknowledgements: diff --git a/queries/Collection health of specific computer.yml b/queries/Collection health of specific computer.yml deleted file mode 100644 index 943d6f6..0000000 --- a/queries/Collection health of specific computer.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Collection health of specific computer -guid: bb95c9c5-984c-4057-a430-000d684c069a -prebuilt: false -platforms: Active Directory -category: Domain Information -description: Returns Local groups and their members, and Principals with privileges -query: |- - MATCH p=(m:Base)-[:RemoteInteractiveLogonRight|AdminTo|CanRDP|LocalToComputer|MemberOfLocalGroup]-(n:Base) - - // Insert computer FQDN - WHERE m.name ENDS WITH "HOSTNAME.DOMAIN.LOCAL" - - RETURN p -revision: 1 -resources: -acknowledgements: Martin Sohn Christensen, @martinsohndk - diff --git a/queries/Compromising permissions on ADCS nodes (ESC5).yml b/queries/Compromising permissions on ADCS nodes (ESC5).yml new file mode 100644 index 0000000..0eedaa6 --- /dev/null +++ b/queries/Compromising permissions on ADCS nodes (ESC5).yml @@ -0,0 +1,17 @@ +name: Compromising permissions on ADCS nodes (ESC5) +guid: 396c7b67-fb5d-4c04-bb13-8007f0dfc9b1 +prebuilt: true +platforms: Active Directory +category: Active Directory Certificate Services +description: +query: |- + MATCH p = (n:Base)-[:Owns|WriteOwner|WriteDacl|GenericAll|GenericWrite]->(m:Base) + WHERE m.distinguishedname CONTAINS "PUBLIC KEY SERVICES" + AND NOT n.objectid ENDS WITH "-512" // Domain Admins + AND NOT n.objectid ENDS WITH "-519" // Enterprise Admins + AND NOT n.objectid ENDS WITH "-544" // Administrators + RETURN p + LIMIT 1000 +revision: 1 +resources: +acknowledgements: diff --git a/queries/Computers with membership in Protected Users.yml b/queries/Computers with membership in Protected Users.yml index 4fb7a4d..d9e8b73 100644 --- a/queries/Computers with membership in Protected Users.yml +++ b/queries/Computers with membership in Protected Users.yml @@ -6,9 +6,9 @@ category: NTLM Relay Attacks description: query: |- MATCH p = (:Base)-[:MemberOf*1..]->(g:Group) - WHERE g.objectid ENDS WITH "-525" + WHERE g.objectid ENDS WITH '-525' RETURN p LIMIT 1000 -revision: 1 +revision: 2 resources: acknowledgements: diff --git a/queries/Computers with non-default Primary Group membership.yml b/queries/Computers with non-default Primary Group membership.yml index 384d31b..4e798d5 100644 --- a/queries/Computers with non-default Primary Group membership.yml +++ b/queries/Computers with non-default Primary Group membership.yml @@ -7,11 +7,13 @@ description: query: |- MATCH p=(n:Computer)-[r:MemberOf]->(g:Group) WHERE NOT g.objectid ENDS WITH "-515" // Domain Computers - AND NOT g.objectid ENDS WITH "-516" // Domain Controllers - AND NOT g.objectid ENDS WITH "-521" // Read-Only Domain Controllers + AND NOT n.isdc = true + AND NOT n.isreadonlydc = true AND r.isprimarygroup = true RETURN p -revision: 1 +revision: 2 resources: +- https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-ada3/e12954a4-6865-4432-94e6-00c310ca87c0 +- https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/5dbcf875-e802-4357-a6e2-1bdff19ff9b5 +- https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/73d11ea7-e634-453e-944d-559654cc91c5 acknowledgements: Martin Sohn Christensen, @martinsohndk - diff --git a/queries/Dangerous privileges for Domain Users groups.yml b/queries/Dangerous privileges for Domain Users groups.yml index fdf2e01..8746be2 100644 --- a/queries/Dangerous privileges for Domain Users groups.yml +++ b/queries/Dangerous privileges for Domain Users groups.yml @@ -5,11 +5,12 @@ platforms: Active Directory category: Dangerous Privileges description: query: |- - MATCH p=(s:Group)-[:AD_ATTACK_PATHS]->(:Base) + MATCH p=(s:Group)-[r:AD_ATTACK_PATHS]->(:Base) WHERE s.objectid ENDS WITH '-513' + AND NOT r:MemberOf RETURN p LIMIT 1000 -revision: 2 +revision: 3 resources: acknowledgements: diff --git a/queries/Domains where any user can join a computer to the domain.yml b/queries/Domains where any user can join a computer to the domain.yml index d46f849..f1ff6b8 100644 --- a/queries/Domains where any user can join a computer to the domain.yml +++ b/queries/Domains where any user can join a computer to the domain.yml @@ -5,10 +5,10 @@ platforms: Active Directory category: Active Directory Hygiene description: query: |- - MATCH (n:Domain) - WHERE n.machineaccountquota > 0 - RETURN n -revision: 1 + MATCH (d:Domain) + WHERE d.machineaccountquota > 0 + RETURN d +revision: 2 resources: - https://learn.microsoft.com/en-us/troubleshoot/windows-server/active-directory/default-workstation-numbers-join-domain - https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/security-policy-settings/add-workstations-to-domain diff --git a/queries/Enrollment rights on certificate templates published to Enterprise CA with User Specified SAN enabled (ESC6).yml b/queries/Enrollment rights on certificate templates published to Enterprise CA with User Specified SAN enabled (ESC6).yml new file mode 100644 index 0000000..3fadf6f --- /dev/null +++ b/queries/Enrollment rights on certificate templates published to Enterprise CA with User Specified SAN enabled (ESC6).yml @@ -0,0 +1,14 @@ +name: Enrollment rights on certificate templates published to Enterprise CA with User Specified SAN enabled (ESC6) +guid: ab14e9dc-996c-4737-878c-583c19cdbf5a +prebuilt: true +platforms: Active Directory +category: Active Directory Certificate Services +description: +query: |- + MATCH p = (:Base)-[:Enroll|GenericAll|AllExtendedRights]->(ct:CertTemplate)-[:PublishedTo]->(eca:EnterpriseCA) + WHERE eca.isuserspecifiessanenabled = True + RETURN p + LIMIT 1000 +revision: 1 +resources: +acknowledgements: diff --git a/queries/Enrollment rights on certificate templates published to Enterprise CA with vulnerable HTTP(S) endpoint (ESC8).yml b/queries/Enrollment rights on certificate templates published to Enterprise CA with vulnerable HTTP(S) endpoint (ESC8).yml new file mode 100644 index 0000000..a02e6e9 --- /dev/null +++ b/queries/Enrollment rights on certificate templates published to Enterprise CA with vulnerable HTTP(S) endpoint (ESC8).yml @@ -0,0 +1,14 @@ +name: Enrollment rights on certificate templates published to Enterprise CA with vulnerable HTTP(S) endpoint (ESC8) +guid: 1c1435b1-bad0-49f2-ba7d-932e047c0af4 +prebuilt: true +platforms: Active Directory +category: Active Directory Certificate Services +description: +query: |- + MATCH p = (:Base)-[:Enroll|GenericAll|AllExtendedRights]->(ct:CertTemplate)-[:PublishedTo]->(eca:EnterpriseCA) + WHERE eca.hasvulnerableendpoint = True + RETURN p + LIMIT 1000 +revision: 1 +resources: +acknowledgements: diff --git a/queries/Entra Users with Entra Admin Role approval (direct).yml b/queries/Entra Users with Entra Admin Role approval (direct).yml new file mode 100644 index 0000000..9f0ff56 --- /dev/null +++ b/queries/Entra Users with Entra Admin Role approval (direct).yml @@ -0,0 +1,12 @@ +name: Entra Users with Entra Admin Role approval (direct) +guid: 74d7993c-24af-4df7-8402-5c6fb22d088c +prebuilt: true +platforms: Azure +category: General +description: +query: |- + MATCH p = (:AZUser)-[:AZRoleApprover]->(:AZRole) + RETURN p LIMIT 100 +revision: 1 +resources: +acknowledgements: diff --git a/queries/Entra Users with Entra Admin Role approval (group delegated).yml b/queries/Entra Users with Entra Admin Role approval (group delegated).yml new file mode 100644 index 0000000..b7a9f1a --- /dev/null +++ b/queries/Entra Users with Entra Admin Role approval (group delegated).yml @@ -0,0 +1,12 @@ +name: Entra Users with Entra Admin Role approval (group delegated) +guid: b70a6512-21e1-4d6e-926a-fba44646085d +prebuilt: true +platforms: Azure +category: General +description: +query: |- + MATCH p = (:AZUser)-[:AZMemberOf]->(:AZGroup)-[:AZRoleApprover]->(:AZRole) + RETURN p LIMIT 100 +revision: 1 +resources: +acknowledgements: diff --git a/queries/Entra Users with Entra Admin Role direct eligibility.yml b/queries/Entra Users with Entra Admin Role direct eligibility.yml new file mode 100644 index 0000000..12625c5 --- /dev/null +++ b/queries/Entra Users with Entra Admin Role direct eligibility.yml @@ -0,0 +1,12 @@ +name: Entra Users with Entra Admin Role direct eligibility +guid: b87899ce-3a51-401a-ae39-ef271b566e3d +prebuilt: true +platforms: Azure +category: General +description: +query: |- + MATCH p = (:AZUser)-[:AZRoleEligible]->(:AZRole) + RETURN p LIMIT 100 +revision: 1 +resources: +acknowledgements: diff --git a/queries/Entra Users with Entra Admin Roles group delegated eligibility.yml b/queries/Entra Users with Entra Admin Roles group delegated eligibility.yml new file mode 100644 index 0000000..8009438 --- /dev/null +++ b/queries/Entra Users with Entra Admin Roles group delegated eligibility.yml @@ -0,0 +1,12 @@ +name: Entra Users with Entra Admin Roles group delegated eligibility +guid: 2e36c81b-25ed-40ba-afec-5f5f6443e095 +prebuilt: true +platforms: Azure +category: General +description: +query: |- + MATCH p = (:AZUser)-[:AZMemberOf]->(:AZGroup)-[:AZRoleEligible]->(:AZRole) + RETURN p LIMIT 100 +revision: 1 +resources: +acknowledgements: diff --git a/queries/Kerberoastable members of Tier Zero High Value groups.yml b/queries/Kerberoastable members of Tier Zero High Value groups.yml index 08c6abe..3b1f2ac 100644 --- a/queries/Kerberoastable members of Tier Zero High Value groups.yml +++ b/queries/Kerberoastable members of Tier Zero High Value groups.yml @@ -6,14 +6,14 @@ category: Kerberos Interaction description: query: |- MATCH (u:User) - WHERE (u:Tag_Tier_Zero) AND u.hasspn=true + WHERE ((u:Tag_Tier_Zero) OR COALESCE(u.system_tags, '') CONTAINS 'admin_tier_0') AND u.hasspn=true AND u.enabled = true AND NOT u.objectid ENDS WITH '-502' AND NOT COALESCE(u.gmsa, false) = true AND NOT COALESCE(u.msa, false) = true RETURN u LIMIT 100 -revision: 1 +revision: 2 resources: https://attack.mitre.org/techniques/T1558/003/ acknowledgements: diff --git a/queries/Location of AdminSDHolder Protected objects.yml b/queries/Location of AdminSDHolder Protected objects.yml new file mode 100644 index 0000000..a060f9c --- /dev/null +++ b/queries/Location of AdminSDHolder Protected objects.yml @@ -0,0 +1,14 @@ +name: Location of AdminSDHolder Protected objects +guid: 3408ccaf-1f42-4c10-b09a-e986661f84d7 +prebuilt: true +platforms: Active Directory +category: Domain Information +description: +query: |- + MATCH p = (n:Base)<-[:Contains*1..]-(:Domain) + WHERE n.adminsdholderprotected = True + RETURN p + LIMIT 1000 +revision: 1 +resources: +acknowledgements: diff --git a/queries/Locations of Owned objects - AD.yml b/queries/Locations of Owned objects - AD.yml new file mode 100644 index 0000000..81e2154 --- /dev/null +++ b/queries/Locations of Owned objects - AD.yml @@ -0,0 +1,15 @@ +name: Locations of Owned objects +guid: c88bfab4-3da0-4b36-b71d-7b324ebd2243 +prebuilt: false +platforms: Active Directory +category: Domain Information +description: +query: |- + MATCH p = (t:Base)<-[:Contains*1..]-(:Domain) + WHERE ((t:Tag_Owned) OR COALESCE(t.system_tags, '') CONTAINS 'owned') + RETURN p + LIMIT 1000 +revision: 1 +resources: +acknowledgements: Martin Sohn Christensen, @martinsohndk + diff --git a/queries/Locations of Owned objects - AZ.yml b/queries/Locations of Owned objects - AZ.yml new file mode 100644 index 0000000..df528f2 --- /dev/null +++ b/queries/Locations of Owned objects - AZ.yml @@ -0,0 +1,15 @@ +name: Locations of Owned objects +guid: 350b8b8a-ea4c-44f3-874b-c9316de6c41b +prebuilt: false +platforms: Azure +category: General +description: +query: |- + MATCH p = (t:AZBase)<-[:AZContains*1..]-(:AZTenant) + WHERE ((t:Tag_Owned) OR COALESCE(t.system_tags, '') CONTAINS 'owned') + RETURN p + LIMIT 1000 +revision: 1 +resources: +acknowledgements: Martin Sohn Christensen, @martinsohndk + diff --git a/queries/Map Azure Management structure.yml b/queries/Map Azure Management structure.yml index a1b1049..180e1a2 100644 --- a/queries/Map Azure Management structure.yml +++ b/queries/Map Azure Management structure.yml @@ -2,13 +2,13 @@ name: Map Azure Management structure guid: c1bb109e-e6a4-4c91-864f-f78e1e42615e prebuilt: false platforms: Azure -category: Kerberos Interaction +category: General description: Maps the structure of Azure Management query: |- MATCH p = (:AZTenant)-[:AZContains*1..]->(:AZResourceGroup) RETURN p LIMIT 1000 -revision: 1 +revision: 2 resources: https://learn.microsoft.com/en-us/azure/governance/management-groups/overview acknowledgements: Martin Sohn Christensen, @martinsohndk diff --git a/queries/Non-Tier Zero account with excessive control.yml b/queries/Non-Tier Zero account with excessive control.yml index 12af951..4437149 100644 --- a/queries/Non-Tier Zero account with excessive control.yml +++ b/queries/Non-Tier Zero account with excessive control.yml @@ -1,4 +1,4 @@ -name: Non-Tier Zero account with excessive control +name: Non-Tier Zero object with excessive control guid: 944cecfe-519b-4318-b226-e8520161b454 prebuilt: false platforms: Active Directory @@ -11,7 +11,7 @@ query: |- WITH n, COLLECT(DISTINCT(m)) AS endNodes WHERE SIZE(endNodes) >= 1000 RETURN n -revision: 3 +revision: 4 resources: acknowledgements: Martin Sohn Christensen, @martinsohndk diff --git a/queries/Shortest paths from Azure Applications to Tier Zero High Value targets.yml b/queries/Shortest paths from Azure Applications to Tier Zero High Value targets.yml index da0c19a..79d3312 100644 --- a/queries/Shortest paths from Azure Applications to Tier Zero High Value targets.yml +++ b/queries/Shortest paths from Azure Applications to Tier Zero High Value targets.yml @@ -3,13 +3,13 @@ guid: 60ff7c58-a98e-4bc1-9e32-8378d2db0c43 prebuilt: true platforms: Azure category: Shortest Paths -description: +description: WARNING! MANY-TO-MANY SHORTEST PATH QUERIES USE EXCESSIVE SYSTEM RESOURCES AND TYPICALLY WILL NOT COMPLETE query: |- MATCH p=shortestPath((s:AZApp)-[:AZ_ATTACK_PATHS*1..]->(t:AZBase)) WHERE ((t:Tag_Tier_Zero) OR COALESCE(t.system_tags, '') CONTAINS 'admin_tier_0') AND s<>t RETURN p LIMIT 1000 -revision: 2 +revision: 3 resources: acknowledgements: diff --git a/queries/Shortest paths from Entra Users to Tier Zero High Value targets.yml b/queries/Shortest paths from Entra Users to Tier Zero High Value targets.yml index f653464..3f1aae4 100644 --- a/queries/Shortest paths from Entra Users to Tier Zero High Value targets.yml +++ b/queries/Shortest paths from Entra Users to Tier Zero High Value targets.yml @@ -3,7 +3,7 @@ guid: 58089b28-54e0-4fd2-bf66-3db480b00e2f prebuilt: true platforms: Azure category: Shortest Paths -description: +description: WARNING! MANY-TO-MANY SHORTEST PATH QUERIES USE EXCESSIVE SYSTEM RESOURCES AND TYPICALLY WILL NOT COMPLETE query: |- MATCH p=shortestPath((s:AZUser)-[:AZ_ATTACK_PATHS*1..]->(t:AZBase)) WHERE ((t:Tag_Tier_Zero) OR COALESCE(t.system_tags, '') CONTAINS 'admin_tier_0') diff --git a/queries/Shortest paths from Owned objects to Tier Zero.yml b/queries/Shortest paths from Owned objects to Tier Zero.yml index a307398..e92b152 100644 --- a/queries/Shortest paths from Owned objects to Tier Zero.yml +++ b/queries/Shortest paths from Owned objects to Tier Zero.yml @@ -3,7 +3,7 @@ guid: dfaa8e8f-2c79-4e92-a291-b1347f6e83b0 prebuilt: true platforms: Active Directory category: Shortest Paths -description: +description: WARNING! MANY-TO-MANY SHORTEST PATH QUERIES USE EXCESSIVE SYSTEM RESOURCES AND TYPICALLY WILL NOT COMPLETE query: |- // MANY TO MANY SHORTEST PATH QUERIES USE EXCESSIVE SYSTEM RESOURCES AND TYPICALLY WILL NOT COMPLETE MATCH p=shortestPath((s:Tag_Owned)-[:AD_ATTACK_PATHS*1..]->(t:Base)) @@ -11,7 +11,7 @@ query: |- AND ((t:Tag_Tier_Zero) OR COALESCE(t.system_tags, '') CONTAINS 'admin_tier_0') RETURN p LIMIT 1000 -revision: 2 +revision: 4 resources: acknowledgements: diff --git a/queries/Shortest paths from Owned objects.yml b/queries/Shortest paths from Owned objects.yml index 10e86aa..e6183e8 100644 --- a/queries/Shortest paths from Owned objects.yml +++ b/queries/Shortest paths from Owned objects.yml @@ -6,11 +6,11 @@ category: Shortest Paths description: query: |- MATCH p=shortestPath((s:Base)-[:AD_ATTACK_PATHS*1..]->(t:Base)) - WHERE (s:Tag_Owned) + WHERE ((s:Tag_Owned) OR COALESCE(s.system_tags, '') CONTAINS 'owned') AND s<>t RETURN p LIMIT 1000 -revision: 2 +revision: 3 resources: acknowledgements: diff --git a/queries/Shortest paths to Azure Subscriptions.yml b/queries/Shortest paths to Azure Subscriptions.yml index a17256f..c809a87 100644 --- a/queries/Shortest paths to Azure Subscriptions.yml +++ b/queries/Shortest paths to Azure Subscriptions.yml @@ -3,13 +3,13 @@ guid: 4785b305-c101-461c-80fc-3fb3ff67a8ce prebuilt: true platforms: Azure category: Shortest Paths -description: +description: WARNING! MANY-TO-MANY SHORTEST PATH QUERIES USE EXCESSIVE SYSTEM RESOURCES AND TYPICALLY WILL NOT COMPLETE query: |- MATCH p=shortestPath((s:AZBase)-[:AZ_ATTACK_PATHS*1..]->(t:AZSubscription)) WHERE s<>t RETURN p LIMIT 1000 -revision: 2 +revision: 3 resources: acknowledgements: diff --git a/queries/Shortest paths to Tier Zero High Value targets.yml b/queries/Shortest paths to Tier Zero High Value targets.yml index 580a030..50fb685 100644 --- a/queries/Shortest paths to Tier Zero High Value targets.yml +++ b/queries/Shortest paths to Tier Zero High Value targets.yml @@ -5,11 +5,12 @@ platforms: Active Directory category: Shortest Paths description: query: |- - MATCH p=shortestPath((s)-[:AD_ATTACK_PATHS*1..]->(t:Tag_Tier_Zero)) - WHERE s<>t + MATCH p=shortestPath((s)-[:AD_ATTACK_PATHS*1..]->(t:Base)) + WHERE ((t:Tag_Tier_Zero) OR (COALESCE(t.system_tags, '') CONTAINS 'admin_tier_0')) + AND s<>t RETURN p LIMIT 1000 -revision: 2 +revision: 3 resources: acknowledgements: diff --git a/queries/Shortest paths to privileged roles.yml b/queries/Shortest paths to privileged roles.yml index 664f332..5397c05 100644 --- a/queries/Shortest paths to privileged roles.yml +++ b/queries/Shortest paths to privileged roles.yml @@ -3,7 +3,7 @@ guid: 3dc73dd8-4873-4aeb-a88f-56a58c77f512 prebuilt: true platforms: Azure category: Shortest Paths -description: +description: WARNING! MANY-TO-MANY SHORTEST PATH QUERIES USE EXCESSIVE SYSTEM RESOURCES AND TYPICALLY WILL NOT COMPLETE query: |- MATCH p=shortestPath((s:AZBase)-[:AZ_ATTACK_PATHS*1..]->(t:AZRole)) WHERE t.name =~ '(?i)Global Administrator|User Administrator|Cloud Application Administrator|Authentication Policy Administrator|Exchange Administrator|Helpdesk Administrator|Privileged Authentication Administrator|Privileged Role Administrator' AND s<>t diff --git a/queries/Synced Entra Users with Entra Admin Role approval (direct).yml b/queries/Synced Entra Users with Entra Admin Role approval (direct).yml new file mode 100644 index 0000000..9363770 --- /dev/null +++ b/queries/Synced Entra Users with Entra Admin Role approval (direct).yml @@ -0,0 +1,14 @@ +name: Synced Entra Users with Entra Admin Role approval (direct) +guid: 1bfe2d75-0a51-4dbe-abc6-178cc0e4476f +prebuilt: true +platforms: +- Active Directory +- Azure +category: Cross Platform Attack Paths +description: +query: |- + MATCH p = (:User)-[:SyncedToEntraUser]->(:AZUser)-[:AZRoleApprover]->(:AZRole) + RETURN p LIMIT 100 +revision: 1 +resources: +acknowledgements: diff --git a/queries/Synced Entra Users with Entra Admin Role approval (group delegated).yml b/queries/Synced Entra Users with Entra Admin Role approval (group delegated).yml new file mode 100644 index 0000000..ab42435 --- /dev/null +++ b/queries/Synced Entra Users with Entra Admin Role approval (group delegated).yml @@ -0,0 +1,14 @@ +name: Synced Entra Users with Entra Admin Role approval (group delegated) +guid: ead56ecb-fb88-427c-8f39-75e774bb9a0a +prebuilt: true +platforms: +- Active Directory +- Azure +category: Cross Platform Attack Paths +description: +query: |- + MATCH p = (:User)-[:SyncedToEntraUser]->(:AZUser)-[:AZMemberOf]->(:AZGroup)-[:AZRoleApprover]->(:AZRole) + RETURN p LIMIT 100 +revision: 1 +resources: +acknowledgements: diff --git a/queries/Synced Entra Users with Entra Admin Role direct eligibility.yml b/queries/Synced Entra Users with Entra Admin Role direct eligibility.yml new file mode 100644 index 0000000..db1d12a --- /dev/null +++ b/queries/Synced Entra Users with Entra Admin Role direct eligibility.yml @@ -0,0 +1,14 @@ +name: Synced Entra Users with Entra Admin Role direct eligibility +guid: ea82e359-725c-4881-83e9-35007e859cf5 +prebuilt: true +platforms: +- Active Directory +- Azure +category: Cross Platform Attack Paths +description: +query: |- + MATCH p = (:User)-[:SyncedToEntraUser]->(:AZUser)-[:AZRoleEligible]->(:AZRole) + RETURN p LIMIT 100 +revision: 1 +resources: +acknowledgements: diff --git a/queries/Synced Entra Users with Entra Admin Roles group delegated eligibility.yml b/queries/Synced Entra Users with Entra Admin Roles group delegated eligibility.yml new file mode 100644 index 0000000..00b51b1 --- /dev/null +++ b/queries/Synced Entra Users with Entra Admin Roles group delegated eligibility.yml @@ -0,0 +1,14 @@ +name: Synced Entra Users with Entra Admin Roles group delegated eligibility +guid: bc610e20-e5c0-41f3-9e8e-7378f87a3f71 +prebuilt: true +platforms: +- Active Directory +- Azure +category: Cross Platform Attack Paths +description: +query: |- + MATCH p = (:User)-[:SyncedToEntraUser]->(:AZUser)-[:AZMemberOf]->(:AZGroup)-[:AZRoleEligible]->(:AZRole) + RETURN p LIMIT 100 +revision: 1 +resources: +acknowledgements: diff --git a/queries/Tier Zero accounts not members of Denied RODC Password Replication Group.yml b/queries/Tier Zero accounts not members of Denied RODC Password Replication Group.yml index 2831cc1..9f9e0bb 100644 --- a/queries/Tier Zero accounts not members of Denied RODC Password Replication Group.yml +++ b/queries/Tier Zero accounts not members of Denied RODC Password Replication Group.yml @@ -5,16 +5,22 @@ platforms: Active Directory category: Active Directory Hygiene description: query: |- - MATCH (n:Base) + // Get all Tier Zero accounts that are members of Denied RODC Password Replication Group + MATCH (n:Base)-[:MemberOf*1..]->(m:Group) WHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0') - AND (n:User or n:Computer) - WITH n - OPTIONAL MATCH (n)-[:MemberOf*1..]->(m:Group) - WHERE m.objectid ENDS WITH '-519' - WITH n, m - WHERE m IS NULL - RETURN n -revision: 1 + AND (n:User OR n:Computer) + AND m.objectid ENDS WITH '-519' + WITH COLLECT(n.objectid) AS MembersOfDeniedGroup + + // Get all Tier Zero accounts + MATCH (x:Base) + WHERE ((x:Tag_Tier_Zero) OR COALESCE(x.system_tags, '') CONTAINS 'admin_tier_0') + AND (x:User OR x:Computer) + + // Filter the members of Denied RODC Password Replication Group + AND NOT x.objectid IN MembersOfDeniedGroup + RETURN x +revision: 2 resources: acknowledgements: Martin Sohn Christensen, @martinsohndk diff --git a/queries/Tier Zero principals without AdminSDHolder protection.yml b/queries/Tier Zero principals without AdminSDHolder protection.yml new file mode 100644 index 0000000..7f49533 --- /dev/null +++ b/queries/Tier Zero principals without AdminSDHolder protection.yml @@ -0,0 +1,15 @@ +name: Tier Zero principals without AdminSDHolder protection +guid: 82ce5e2e-415b-489d-b891-304e8bb25998 +prebuilt: true +platforms: Active Directory +category: Active Directory Hygiene +description: +query: |- + MATCH (n:Base) + WHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0') + AND n.adminsdholderprotected = false + RETURN n + LIMIT 500 +revision: 1 +resources: +acknowledgements: diff --git a/queries/Trace ACE inheritance.yml b/queries/Trace ACE inheritance.yml deleted file mode 100644 index a8f01a6..0000000 --- a/queries/Trace ACE inheritance.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Trace ACE inheritance -guid: 8c5454df-3ae8-412c-b271-3c4c55df7141 -prebuilt: false -platforms: Active Directory -category: Domain Information -description: When BloodHound shows that an inherited ACE applies to an object it does not show the source/where it is inherited from from (OU, Container, Domain root) - the source is where it should be remediated. This query can sometimes find the source of an inherited ACE, but only works if the ACE is set to also apply to the source itself. -query: |- - // Replace INSERT_OBJECT_ID with the affected principal - // Replace 'GenericAll' with the specific edge you're tracing - WITH "INSERT_OBJECT_ID" as OID - MATCH p=()-[:GenericAll {isacl:true,isinherited:false}]->()-[:Contains*1..]->(:Base{objectid:OID}) - WHERE NONE(ou in NODES(p) WHERE ou:OU AND ou.isaclprotected IS NOT NULL) - RETURN p -revision: 1 -resources: -acknowledgements: Walter.Legowski, @SadProcessor