From b57f1c04e3d06da7d2571e8ef41e49da2941fa39 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 16 Jun 2025 21:16:52 +0200 Subject: [PATCH 01/17] Fix invalid YAML syntax --- queries/Potential GPO 'Apply' misconfiguration.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/queries/Potential GPO 'Apply' misconfiguration.yml b/queries/Potential GPO 'Apply' misconfiguration.yml index 8da3809..b2a9901 100644 --- a/queries/Potential GPO 'Apply' misconfiguration.yml +++ b/queries/Potential GPO 'Apply' misconfiguration.yml @@ -3,7 +3,7 @@ guid: f5f2455e-afdc-4708-9a34-98f539ce52d8 prebuilt: true platforms: Active Directory category: Dangerous Privileges -description: In Active Directory, GPO's are applied to objects in the Group Policy Management Console by ticking “Allow: Apply group policy”, but administrators can mistakenly tick “Allow: Write” or “Allow: Full Control” resulting in a misconfigured GPO that allows a principal to compromise other principals the GPO also applies to. Results are potential risks and must be audited for for correctness. +description: In Active Directory, GPO's are applied to objects in the Group Policy Management Console by ticking "Allow - Apply group policy", but administrators can mistakenly tick "Allow - Write" or "Allow - Full Control" resulting in a misconfigured GPO that allows a principal to compromise other principals the GPO also applies to. Results are potential risks and must be audited for for correctness. query: |- MATCH p=(n:Base)-[:GenericAll|GenericWrite]->(g:GPO) @@ -19,7 +19,7 @@ query: |- RETURN p LIMIT 1000 -revision: 1 +revision: 2 resources: acknowledgements: Martin Sohn Christensen, @martinsohndk From 608785942647c9ee82e2a5b0d5b35b0f5e3afbff Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 16 Jun 2025 21:17:02 +0200 Subject: [PATCH 02/17] Update Foreign External AZ users in Tier Zero.yaml --- queries/Foreign External AZ users in Tier Zero.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/queries/Foreign External AZ users in Tier Zero.yaml b/queries/Foreign External AZ users in Tier Zero.yaml index 9e86343..3178a10 100644 --- a/queries/Foreign External AZ users in Tier Zero.yaml +++ b/queries/Foreign External AZ users in Tier Zero.yaml @@ -8,9 +8,9 @@ query: |- MATCH (n:AZUser) WHERE n.name CONTAINS "#EXT#@" AND ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0') - RETURN p + RETURN n LIMIT 1000 -revision: 1 +revision: 2 resources: https://learn.microsoft.com/en-us/entra/external-id/user-properties#key-properties-of-the-microsoft-entra-b2b-collaboration-user acknowledgements: Martin Sohn Christensen, @martinsohndk From 782b14113911bf1c7e25730dc0d1cfbe253f9669 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 16 Jun 2025 21:23:28 +0200 Subject: [PATCH 03/17] Update logic that finds Pre-Server 2008 accounts --- .../Accounts with weak password storage encryption.yml | 9 +++------ ...service accounts without AES encryption support.yml | 10 +++++----- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/queries/Accounts with weak password storage encryption.yml b/queries/Accounts with weak password storage encryption.yml index a26fab3..5571cdf 100644 --- a/queries/Accounts with weak password storage encryption.yml +++ b/queries/Accounts with weak password storage encryption.yml @@ -3,16 +3,13 @@ guid: 8bd6fcf2-3f3c-414c-857a-4caf28e49def prebuilt: true platforms: Active Directory category: Active Directory Hygiene -description: Accounts with passwords set before Windows Server 2008 DC promotion, which therefore lack AES encryption keys. Uses the RODC group creation date to find accounts with pwdLastSet dates predating AES key generation capability. +description: Accounts with passwords set before the existence of Windows Server 2008 Domain Controller which therefore lack AES encryption keys. query: |- - MATCH (g:Group) - WHERE g.objectid ends with "-521" MATCH (n:Base) - WHERE g.domainsid = n.domainsid - AND n.pwdlastset < g.whencreated + WHERE n.pwdlastset < 1204070400 // Password Last Set before Windows Server 2008 release RETURN n LIMIT 100 -revision: 1 +revision: 2 resources: https://techcommunity.microsoft.com/blog/coreinfrastructureandsecurityblog/decrypting-the-selection-of-supported-kerberos-encryption-types/1628797 acknowledgements: Martin Sohn Christensen, @martinsohndk diff --git a/queries/Kerberos-enabled service accounts without AES encryption support.yml b/queries/Kerberos-enabled service accounts without AES encryption support.yml index dc6c2ef..b408c66 100644 --- a/queries/Kerberos-enabled service accounts without AES encryption support.yml +++ b/queries/Kerberos-enabled service accounts without AES encryption support.yml @@ -3,21 +3,21 @@ guid: cb8cf96e-21c9-422b-9439-390a13446ca6 prebuilt: false platforms: Active Directory category: Active Directory Hygiene -description: +description: Accounts without Kerberos AES encryption support, or passwords set before the existence of Windows Server 2008 Domain Controller which therefore lack AES encryption keys. query: |- MATCH (n:Base) WHERE n.hasspn = true AND (( n.supportedencryptiontypes <> ['Not defined'] - AND n.supportedencryptiontypes <> [] - AND NONE(type IN n.supportedencryptiontypes WHERE type CONTAINS 'AES128' OR type CONTAINS 'AES256') + OR n.supportedencryptiontypes <> [] + OR NONE(type IN n.supportedencryptiontypes WHERE type CONTAINS 'AES128' OR type CONTAINS 'AES256') ) - OR (n.pwdlastset < datetime('2008-02-27T00:00:00').epochseconds // Password Last Set before Windows Server 2008 + OR (n.pwdlastset < 1204070400 // Password Last Set before Windows Server 2008 AND NOT n.pwdlastset IN [-1.0, 0.0] )) RETURN n LIMIT 100 -revision: 1 +revision: 2 resources: acknowledgements: Martin Sohn Christensen, @martinsohndk From da3aab1bd6ce7806df990411022cfd0723c77386 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 16 Jun 2025 23:53:31 +0200 Subject: [PATCH 04/17] Update Non-Tier Zero account with excessive control.yml Fix buggy query, now returns correct results. --- .../Non-Tier Zero account with excessive control.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/queries/Non-Tier Zero account with excessive control.yml b/queries/Non-Tier Zero account with excessive control.yml index 851480d..8182fc7 100644 --- a/queries/Non-Tier Zero account with excessive control.yml +++ b/queries/Non-Tier Zero account with excessive control.yml @@ -3,17 +3,14 @@ guid: 944cecfe-519b-4318-b226-e8520161b454 prebuilt: false platforms: Active Directory category: Dangerous Privileges -description: +description: Returns non-Tier Zero principals with >= 1000 direct rights to other principals. This does not include rights from group memberships. query: |- - MATCH (d:Domain)-[:Contains*1..]->(u:User) - WHERE u.enabled = true - WITH d, COUNT(u) AS enabledUserCount - MATCH (d)-[:Contains*1..]->(n:Base)-[r:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|ForceChangePassword|AllExtendedRights|AddMember|AllowedToDelegate|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions]->(m:Base) + MATCH (n:Base)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation]->(m:Base) WHERE NOT ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0') - WITH n, enabledUserCount, COLLECT(DISTINCT(m)) AS endNodes + WITH n, COLLECT(DISTINCT(m)) AS endNodes WHERE SIZE(endNodes) >= 1000 RETURN n -revision: 1 +revision: 2 resources: acknowledgements: Martin Sohn Christensen, @martinsohndk From e7af413539c472567ddcd1bd2856643c9052dec9 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 17 Jun 2025 10:17:52 +0200 Subject: [PATCH 05/17] rename file to match yml name --- ... Privileges.yml => Collection health of specific computer.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename queries/{Collection health of Tier Zero Inbound Execution Privileges.yml => Collection health of specific computer.yml} (100%) diff --git a/queries/Collection health of Tier Zero Inbound Execution Privileges.yml b/queries/Collection health of specific computer.yml similarity index 100% rename from queries/Collection health of Tier Zero Inbound Execution Privileges.yml rename to queries/Collection health of specific computer.yml From 12d2a494560370b9750b11453d5c73eef282be04 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 17 Jun 2025 10:18:43 +0200 Subject: [PATCH 06/17] assessment mappings --- docs/security-assessment-mapping.json | 2329 +++++++++++++++++++++++++ docs/security-assessment-mapping.md | 65 + 2 files changed, 2394 insertions(+) create mode 100644 docs/security-assessment-mapping.json create mode 100644 docs/security-assessment-mapping.md diff --git a/docs/security-assessment-mapping.json b/docs/security-assessment-mapping.json new file mode 100644 index 0000000..f45f5ad --- /dev/null +++ b/docs/security-assessment-mapping.json @@ -0,0 +1,2329 @@ +{ + "definitions": { + "maps_to": [ + "PingCastle", + "Nessus", + "MDI" + ], + "mapping_scope": { + "partial": "Query covers the core assessment control but with different approach/scope", + "combination": "Multiple queries are combined to fully cover a single assessment control", + "superset": "Query covers everything the assessment control does plus covers additional risk", + "exact": "Query identifies the same assessment control with same scope" + } + }, + "mappings": [ + { + "bloodhound_query": { + "guid": "d263e621-7f1b-4efb-ad25-098fc7d4fb72", + "name": "Enabled computers inactive for 180 days - MSSQL Failover Cluster" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-PwdLastSet-Cluster", + "name": "[M]Check if all cluster are using regular password change practices" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "185c5010-8d4f-4f9b-b24e-831707dddfca", + "name": "Computers with passwords older than the default maximum password age" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-PwdLastSet-45", + "name": "[M]Check if all computers are using regular password change practices" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-PwdLastSet-90", + "name": "[M]Check if all computers have changed their passwords in the last 3 months" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "72550bcb-3c4f-463d-8973-91a49163dc5a", + "name": "Enabled Tier Zero / High Value principals inactive for 60 days" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "S-DC-Inactive", + "name": "[M]Check if all DC are active" + } + ] + }, + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "P-Inactive", + "name": "[M]Check for inactive administrator accounts" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "", + "id": "Dormant entities in sensitive groups", + "name": "Dormant entities in sensitive groups" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "b6d6d0bf-130e-4719-996b-adc29bba36e9", + "name": "Tier Zero computers with passwords older than the default maximum password age" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "S-PwdLastSet-DC", + "name": "[M]Check if all DC are using regular password change practices" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "Change Domain Controller computer account old password", + "name": "Change Domain Controller computer account old password" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "71972f3c-b32d-4023-a841-5cc8cc1c1867", + "name": "Enabled users inactive for 180 days" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-Inactive", + "name": "[M]Inactive account check" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "0768e810-1e1e-4319-a216-76d9c2058644", + "name": "Enabled computers inactive for 180 days" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-C-Inactive", + "name": "[M]Inactive computer check" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "5862dc4e-6f6f-4321-9474-d838968495ed", + "name": "Computers with non-default Primary Group membership" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-C-PrimaryGroup", + "name": "[M]Check for hidden group membership for computer accounts" + } + ] + }, + { + "source": "Nessus", + "controls": [ + { + "mapping_scope": "combination", + "mapping_scope_detail": "", + "id": "Primary Group ID integrity", + "name": "Primary Group ID integrity" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "combination", + "mapping_scope_detail": "", + "id": "Accounts with non-default Primary Group ID", + "name": "Accounts with non-default Primary Group ID" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "93890f88-df2c-4167-a945-a53961d08d00", + "name": "Users with non-default Primary Group membership" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-PrimaryGroup", + "name": "[M]Check for hidden group membership for user accounts" + } + ] + }, + { + "source": "Nessus", + "controls": [ + { + "mapping_scope": "combination", + "mapping_scope_detail": "", + "id": "Primary Group ID integrity", + "name": "Primary Group ID integrity" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "combination", + "mapping_scope_detail": "", + "id": "Accounts with non-default Primary Group ID", + "name": "Accounts with non-default Primary Group ID" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "ab900835-b2b8-4674-87b4-8b5141e80439", + "name": "Principals with passwords stored using reversible encryption" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-Reversible", + "name": "[T]Check for reversible password used for user accounts" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-C-Reversible", + "name": "[T]Check for reversible passwords used for computer accounts" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Unsecure account attributes: Remove Store password using reversible encryption", + "name": "Unsecure account attributes: Remove Store password using reversible encryption" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "2570e359-dec1-419d-b0dc-a204bd64ee42", + "name": "AS-REP Roastable users (DontReqPreAuth)" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-NoPreAuth", + "name": "[T]Check if all accounts require Kerberos pre-authentication" + } + ] + }, + { + "source": "Nessus", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Kerberos pre-authentication validation", + "name": "Kerberos pre-authentication validation" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Unsecure account attributes: Remove Do not require Kerberos preauthentication", + "name": "Unsecure account attributes: Remove Do not require Kerberos preauthentication" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "6d51e4dc-e1ad-477a-b6c6-324f18f03120", + "name": "AS-REP Roastable Tier Zero users (DontReqPreAuth)" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "S-NoPreAuthAdmin", + "name": "[T]Check if all admin accounts require Kerberos pre-authentication" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "23bdc2ad-6739-4b2b-85d3-258e3f424eb2", + "name": "Users which do not require password to authenticate" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-PwdNotRequired", + "name": "[M]Check that every account requires a password" + } + ] + }, + { + "source": "Nessus", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Blank passwords", + "name": "Blank passwords" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Unsecure account attributes: Remove Password not required", + "name": "Unsecure account attributes: Remove Password not required" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "212c2a98-53d9-4dfa-b177-42c601452dd1", + "name": "Users with non-expiring passwords" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-PwdNeverExpires", + "name": "[M]Check that there is no account with never-expiring passwords" + } + ] + }, + { + "source": "Nessus", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Non-expiring account password", + "name": "Non-expiring account password" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "8172d52c-a975-49bd-9180-5b6efc59c9ab", + "name": "Accounts with SID History" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-SIDHistory", + "name": "[M][T]SIDHistory check" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "3da9d14a-f1cb-4df7-b3da-8d73ff5c401b", + "name": "Domains with functional level not the latest version" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-FunctionalLevel4", + "name": "[M]Ensure that the functional level of the domain and the forest are up to date to use the latest security features (4)" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-FunctionalLevel3", + "name": "[M]Ensure that the functional level of the domain and the forest are up to date to use the latest security features (3)" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-FunctionalLevel1", + "name": "[M]Ensure that the functional level of the domain and the forest are up to date to use the latest security features (1)" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "a87b558c-5746-4a90-9f83-c86e7b924a52", + "name": "Tier Zero computers with unsupported operating systems" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "S-DC-2000", + "name": "[M]Obsolete Domain Controller (Windows 2000)" + }, + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "S-DC-2003", + "name": "[M]Obsolete Domain Controller (Windows Server 2003)" + }, + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "S-DC-2008", + "name": "[M]Obsolete Domain Controller (Windows Server 2008)" + }, + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "S-DC-2012", + "name": "[M]Obsolete Domain Controller (Windows Server 2012)" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "d06d3b14-0318-4fa9-9639-4b79ccaf3c2c", + "name": "Computers with unsupported operating systems" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-OS-W10", + "name": "[M]Obsolete OS (Windows 10 or Windows 11)" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-OS-2000", + "name": "[M]Obsolete OS (Windows 2000)" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-OS-Win7", + "name": "[M]Obsolete OS (Windows 7)" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-OS-Win8", + "name": "[M]Obsolete OS (Windows 8)" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-OS-NT", + "name": "[M]Obsolete OS (Windows NT)" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-OS-2003", + "name": "[M]Obsolete OS (Windows Server 2003)" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-OS-2008", + "name": "[M]Obsolete OS (Windows Server 2008)" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-OS-2012", + "name": "[M]Obsolete OS (Windows Server 2012)" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-OS-Vista", + "name": "[M]Obsolete OS (Windows Vista)" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-OS-XP", + "name": "[M]Obsolete OS (Windows XP)" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "cb8cf96e-21c9-422b-9439-390a13446ca6", + "name": "Kerberos-enabled service accounts without AES encryption support" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-AesNotEnabled", + "name": "[T]Check the use of Kerberos on services accounts without AES support" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Unsecure account attributes: Enable Kerberos AES encryption support", + "name": "Unsecure account attributes: Enable Kerberos AES encryption support" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "d03ea1ef-70f0-439b-b1ef-d7f94ceb2af3", + "name": "Principals with DES-only Kerberos authentication" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "combination", + "mapping_scope_detail": "", + "id": "S-DesEnabled", + "name": "[T]Check the use of Kerberos with weak encryption" + } + ] + }, + { + "source": "Nessus", + "controls": [ + { + "mapping_scope": "combination", + "mapping_scope_detail": "", + "id": "Weak Kerberos encryption", + "name": "Weak Kerberos encryption" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Unsecure account attributes: Remove Use Kerberos DES encryption types for this account", + "name": "Unsecure account attributes: Remove Use Kerberos DES encryption types for this account" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "ca329573-2157-41da-ab17-4d122c54b11d", + "name": "Principals with weak supported Kerberos encryption types" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "combination", + "mapping_scope_detail": "", + "id": "S-DesEnabled", + "name": "[T]Check the use of Kerberos with weak encryption" + } + ] + }, + { + "source": "Nessus", + "controls": [ + { + "mapping_scope": "combination", + "mapping_scope_detail": "", + "id": "Weak Kerberos encryption", + "name": "Weak Kerberos encryption" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Weak cipher usage", + "name": "Weak cipher usage" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "4b42513c-f89d-47ff-8d98-908af49d2b48", + "name": "Domain Controllers allowing NTLMv1 or LM authentication" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-OldNtlm", + "name": "[T]Ensure that the NTLMv1 and old LM protocols are banned" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "421921fa-bc0f-4659-9680-b7481adcb132", + "name": "Domains where any user can join a computer to the domain" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "partial", + "mapping_scope_detail": "Does not check the Security Policy on DCs", + "id": "S-ADRegistration", + "name": "[M]Check the process of registration of computers to the domain" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Unsecure domain configurations: ms-DS-MachineAccountQuota", + "name": "Unsecure domain configurations: ms-DS-MachineAccountQuota" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "c561c4f8-ea45-453f-85a2-3fc2e20e7f8c", + "name": "Object name conflict" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-Duplicate", + "name": "[M]Duplicate account check" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "4316eaf1-6af0-4879-8f55-ac2633a711c3", + "name": "Tier Zero accounts that can be delegated" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "P-Delegated", + "name": "[M]At least one administrator account can be delegated" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Ensure privileged accounts are not delegated", + "name": "Ensure privileged accounts are not delegated" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "e6da7800-ae06-41cb-80a6-d5421ab2143a", + "name": "Kerberoastable members of Tier Zero / High Value groups" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "P-Kerberoasting", + "name": "[T]Check if admin accounts are vulnerable to the Kerberoast attack" + } + ] + }, + { + "source": "Nessus", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "Kerberoasting", + "name": "Kerberoasting" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "Unsecure account attributes: Remove a Service Principal Name (SPN)", + "name": "Unsecure account attributes: Remove a Service Principal Name (SPN)" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "5e0d69b1-37d1-43ae-ac5d-f297f312fab5", + "name": "Tier Zero users with passwords not rotated in over 1 year" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "P-AdminPwdTooOld", + "name": "[M]Check if all admin passwords are changed on the field" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "Change password of built-in domain Administrator account", + "name": "Change password of built-in domain Administrator account" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "97fb1310-d15d-4d63-82a2-8788056250f1", + "name": "Microsoft Entra Connect accounts with passwords not rotated in over 90 days" + }, + "maps_to": [ + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Rotate password for Microsoft Entra Connect AD DS Connector account", + "name": "Rotate password for Microsoft Entra Connect AD DS Connector account" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "9e6e75b4-9ecc-45d4-a39b-b6427b813f0a", + "name": "Overprivileged Microsoft Entra Connect accounts" + }, + "maps_to": [ + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Replace Enterprise or Domain Admin account for Entra Connect AD DS Connector account", + "name": "Replace Enterprise or Domain Admin account for Entra Connect AD DS Connector account" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "7c50f724-c467-4005-8e3f-9a6ce1461db0", + "name": "Computers without Windows LAPS" + }, + "maps_to": [ + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "partial", + "mapping_scope_detail": "Query does not check for computers that haven't had their LAPS managed password changed in the last 60 days", + "id": "Microsoft LAPS usage", + "name": "Microsoft LAPS usage" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "8bd6fcf2-3f3c-414c-857a-4caf28e49def", + "name": "Accounts with weak password storage encryption" + }, + "maps_to": [ + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Unsecure account attributes: Remove Password stored with weak encryption", + "name": "Unsecure account attributes: Remove Password stored with weak encryption" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "78d59fe1-e1a0-4813-adc9-c3c96ac08b66", + "name": "Enrollment rights on published ESC15 certificate templates" + }, + "maps_to": [ + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Prevent Certificate Enrollment with arbitrary Application Policies (ESC15)", + "name": "Prevent Certificate Enrollment with arbitrary Application Policies (ESC15)" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "543eb01d-9fa3-4b8f-a936-b46bbfdaa2ae", + "name": "Tier Zero users not member of Protected Users" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "P-ProtectedUsers", + "name": "[M]Check if all privileged accounts are in the special group Protected Users" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "99d29ded-223a-442b-a0e0-f8b5694c6441", + "name": "Tier Zero computers not owned by Tier Zero" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "P-DCOwner", + "name": "[M]At least one domain controller is not owned correctly" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "05e2a94b-5ee6-47ec-b715-3982f30af01b", + "name": "Domains with List Object mode enabled" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "P-DsHeuristicsDoListObject", + "name": "[M]Check if the behavior DoListObject has been enabled" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "183fb320-f3ae-4ab3-a090-3f9a7db692e1", + "name": "All DNSAdmins" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "P-DNSAdmin", + "name": "[M]Check if the Dns Admins group is not empty" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "008792c0-4458-46a1-a10d-50cdaf95af1e", + "name": "Non-default delegation on MicrosoftDNS container" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "P-DNSDelegation", + "name": "[M]Check if there is an explicit delegation on DNS servers" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "815ff190-f6f3-4757-a516-2f4bf589b705", + "name": "Domains affected by AdPrep privilege escalation risk" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "", + "id": "P-DelegationKeyAdmin", + "name": "[M][T]Ensure that bogus Windows Server 2016 AD prep did not introduce vulnerabilities" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "f2d09c94-b6f2-4901-9a2d-f8bacd61edc7", + "name": "Domains affected by Exchange privilege escalation risk" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded source scope to all Exchange groups. Expanded target scope to Tier Zero", + "id": "P-ExchangePrivEsc", + "name": "[M]Ensure that Exchange did not introduce security vulnerabilities" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "4c1e0137-5b7f-48d8-bd09-9db7674bca61", + "name": "Non-Tier Zero principals with control of AdminSDHolder" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Non Tier Zero", + "id": "P-ExchangeAdminSDHolder", + "name": "[M]Ensure that Exchange did not modify the AdminSDHolder object to introduce vulnerabilities" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Non Tier Zero", + "id": "Admin SDHolder permissions", + "name": "Admin SDHolder permissions" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "79f8d8f9-8291-4bf7-a13a-15989018075f", + "name": "Domains exempting privileged groups from AdminSDHolder protections" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "P-DsHeuristicsAdminSDExMask", + "name": "[M]Ensure that the AdminSDHolder protection has not been disabled for some critical groups" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "35b1206f-871b-44aa-a601-c5258060dfcf", + "name": "Usage of built-in domain Administrator account" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Exclude newly created domains with the 'whencreated' property", + "id": "P-AdminLogin", + "name": "[M]Check for Native administrator usage" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "f046e95a-5f84-4e83-bcda-6e83f3d8e21a", + "name": "Domains with more than 50 Tier Zero accounts" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "P-AdminNum", + "name": "[M]Check for number of Administrator accounts above the baseline" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "3dfd0843-1ff9-4c21-aa67-feae08d109de", + "name": "All Operator groups" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "P-OperatorsEmpty", + "name": "[M]Check that the operator groups are empty" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "9654c0d4-f1e8-4393-a2d1-53a5554a9de8", + "name": "Tier Zero users with email" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero. Excluded false positives", + "id": "P-AdminEmailOn", + "name": "[M]Check if administrator accounts are email enabled" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "a334f21a-3d7f-448e-b7ea-1465a3127bce", + "name": "Large default groups with outbound control" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded technique scope to include all BloodHound traversable edges. Extended group scope to include GUESTS", + "id": "P-ControlPathIndirectEveryone", + "name": "[T]Check if there is a control path involving everyone-like groups" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded technique scope to include all BloodHound traversable edges", + "id": "GPO can be modified by unprivileged accounts", + "name": "GPO can be modified by unprivileged accounts" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "944cecfe-519b-4318-b226-e8520161b454", + "name": "Non-Tier Zero account with excessive control" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero. Expanded scope to include all BloodHound traversable edges", + "id": "P-ControlPathIndirectMany", + "name": "[T]Check if there is a control path involving too many users or computers" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "310b3626-f8e6-4ab0-832c-72df6048597f", + "name": "Large default groups with outbound control of OUs" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded technique scope to include all BloodHound traversable edges", + "id": "P-DelegationEveryone", + "name": "[M][T]A Delegation is granted to Everyone" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "4e8429f9-cba2-41e9-bac6-0c42f96b2c57", + "name": "Unresolved SID with outbound control" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded technique scope to include all BloodHound traversable edges", + "id": "P-UnkownDelegation", + "name": "[M][T]Check delegations for the recipient's existence" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "8641e593-f2f2-48ba-bd45-fbc86e9f632a", + "name": "Tier Zero computers at risk of constrained delegation" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "P-DelegationDCt2a4d", + "name": "[M][T]Check if all DC have no constrained delegation with protocol transition" + }, + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "P-DelegationDCa2d2", + "name": "[M][T]Check if all DC have no constrained delegation" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "combination", + "mapping_scope_detail": "", + "id": "Unsecure Kerberos delegation", + "name": "Unsecure Kerberos delegation" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "4dc97cf4-3c03-4fe6-8a8b-4f665c67e1e5", + "name": "Tier Zero computers at risk of resource-based constrained delegation" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "P-DelegationDCsourcedeleg", + "name": "[M][T]Check if all DC have no resource based constrained delegation" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "combination", + "mapping_scope_detail": "", + "id": "Unsecure Kerberos delegation", + "name": "Unsecure Kerberos delegation" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "e7e9a927-3f34-42c7-b921-d8bcf626011e", + "name": "Non-Tier Zero account with unconstrained delegation" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded exclusion scope to Tier Zero", + "id": "P-UnconstrainedDelegation", + "name": "[M][T]Ensure that no accounts are subject to unconstrained delegation" + } + ] + }, + { + "source": "Nessus", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded exclusion scope to Tier Zero", + "id": "Unconstrained delegation", + "name": "Unconstrained delegation" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "combination", + "mapping_scope_detail": "Expanded exclusion scope to Tier Zero", + "id": "Unsecure Kerberos delegation", + "name": "Unsecure Kerberos delegation" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "76d8e61d-7a86-40ff-8a85-fd37f1e2563f", + "name": "All Schema Admins" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "P-SchemaAdmin", + "name": "[M]Avoid unexpected schema modifications which could result in domain rebuild" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "42a856fc-257a-4142-9592-ca95fd49e579", + "name": "Kerberos-enabled service account member of built-in Admins groups" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded account scope to include accounts with SPN. Expanded group scope to include Enterprise Admins and Schema Admins", + "id": "P-ServiceDomainAdmin", + "name": "[M][T]Check if Service Accounts are domain administrators" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "19fc5acd-e30a-4038-a5b5-2e0494f93373", + "name": "Members of Allowed RODC Password Replication Group" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "P-RODCAllowedGroup", + "name": "[M]Check the Allowed RODC Password Replication Group group" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "e9613406-e346-410b-a033-690a6cf0c708", + "name": "Tier Zero accounts not members of Denied RODC Password Replication Group" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "P-RODCDeniedGroup", + "name": "[M]Check the Denied RODC Password Replication Group group" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "5cf1f354-80d4-420e-bc4b-424fabc21a56", + "name": "Cross-forest trusts with abusable configuration" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "T-SIDFiltering", + "name": "[M][T]Check for Trusts whose security is not maximum" + } + ] + }, + { + "source": "Nessus", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Dangerous trust relationship", + "name": "Dangerous trust relationship" + } + ] + }, + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "T-TGTDelegation", + "name": "[M][T]Check if Kerberos delegation can be used to take control of the forest from a trusted forest" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "275d2d58-0cad-4cad-8103-e0874cece666", + "name": "Accounts with SID History to a same-domain account" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "T-SIDHistorySameDomain", + "name": "[M][T]Check for local backdoor stored in SID History" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "f39c4953-ae92-4d67-bb50-eb1a161d4d3f", + "name": "Domain migration groups" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "S-Domain$$$", + "name": "[M]Check if a migration is in progress" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "59744dfe-9411-4daf-b342-1203dc62acd4", + "name": "Non-Tier Zero accounts with SID History of Tier Zero accounts" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "T-SIDHistoryDangerous", + "name": "[M][T]Check if dangerous SID are stored in the SIDHistory attribute" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "Unsecure SID History attributes", + "name": "Unsecure SID History attributes" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "2710401a-c4c2-4d2c-9edb-d7625045f2e8", + "name": "Accounts with SID History to a non-existent domain" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "T-SIDHistoryUnknownDomain", + "name": "[M][T]Check if the account has been migrated from a domain which doesn't exist anymore" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "8d94d3f3-3d53-4939-a206-3c0a4dd3f646", + "name": "Users with logon scripts stored in a trusted domain" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "T-ScriptOutOfDomain", + "name": "[T]Check if login scripts may be located in a trusted domain" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "1867abf8-08e3-4ea8-8f65-8366079d35c4", + "name": "Entra ID SSO accounts not rolling Kerberos decryption key" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "T-AzureADSSO", + "name": "[T]Check if password rotation is in place with AzureAD SSO" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Change password for Microsoft Entra seamless SSO account", + "name": "Change password for Microsoft Entra seamless SSO account" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "3359a295-7cfd-491f-976b-c5a68647431c", + "name": "Domains with a single-point-of-failure Domain Controller" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-NotEnoughDC", + "name": "[M]Ensure that there are enough DCs to provide basic redundancy" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "ebc77984-1ceb-4ed2-a395-ce1067847941", + "name": "Enrollment rights on published ESC2 certificate templates" + }, + "maps_to": [ + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Overly permissive certificate template with privileged EKU (ESC2)", + "name": "Overly permissive certificate template with privileged EKU (ESC2)" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "49db8edc-8421-438f-b97b-23c042959bef", + "name": "All ADCS ESC privilege escalation edges" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-CertTempCustomSubject", + "name": "[T]Check if authentication certificate templates allow users to control the subject" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-CertTempAnyone", + "name": "[T]Check if certificate templates can be edited by everyone" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-CertTempAgent", + "name": "[T]Check the permission of agent certificate templates" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-CertTempAnyPurpose", + "name": "[T]Check the purpose provided by certificate templates" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Prevent requests for certificates valid for arbitrary users (ESC1)", + "name": "Prevent requests for certificates valid for arbitrary users (ESC1)" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Misconfigured enrollment agent certificate template (ESC3)", + "name": "Misconfigured enrollment agent certificate template (ESC3)" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Misconfigured certificate templates ACL (ESC4)", + "name": "Misconfigured certificate templates ACL (ESC4)" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Misconfigured certificate templates owner (ESC4)", + "name": "Misconfigured certificate templates owner (ESC4)" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Vulnerable Certificate Authority setting (ESC6)", + "name": "Vulnerable Certificate Authority setting (ESC6)" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Insecure ADCS certificate enrollment IIS endpoints (ESC8)", + "name": "Insecure ADCS certificate enrollment IIS endpoints (ESC8)" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "96e70597-2d74-4503-a624-f1e30b642894", + "name": "Enrollment rights on certificate templates published to Enterprise CA with User Specified SAN enabled" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "Checks ESC1 scenario which is also covered by the query 49db8edc-8421-438f-b97b-23c042959bef", + "id": "A-CertTempCustomSubject", + "name": "[T]Check if authentication certificate templates allow users to control the subject" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "60881923-296c-4702-adf7-a4f059dc9bb8", + "name": "ESC8-vulnerable Enterprise CAs" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-CertEnrollChannelBinding", + "name": "[T]Check if Extended Protection is in place for certificate requests" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-CertEnrollHttp", + "name": "[T]Check if certificate enrollment can be done with HTTP" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "b7c20217-5223-4a39-b8d5-32c15fe2b1da", + "name": "Enrollment rights on published certificate templates with no security extension" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-CertTempNoSecurity", + "name": "[T]Check if authentication certificate templates disallow the tracking of the certificate requester" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "1b3ae310-ffa7-4ce5-a37f-6111aef600c8", + "name": "KRBTGT accounts with passwords not rotated in over 1 year" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to include Entra ID KRBTGT accounts", + "id": "A-Krbtgt", + "name": "[T]Mitigate golden ticket attack via a regular change of the krbtgt password" + } + ] + }, + { + "source": "Nessus", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to include Entra ID KRBTGT accounts", + "id": "Kerberos KRBTGT", + "name": "Kerberos KRBTGT" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to include Entra ID KRBTGT accounts", + "id": "Change password for krbtgt account", + "name": "Change password for krbtgt account" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "dde133d2-b4d2-4de9-a656-905f3bf066f3", + "name": "Large default group added to computer-local group" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "PingCastle only collects on GPOs. SharpHound can both collect from GPOs and computers directly", + "id": "A-MembershipEveryone", + "name": "[M]Check if a GPO assigns everyone to a local group" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "MDI only collects on GPOs. SharpHound can both collect from GPOs and computers directly", + "id": "GPO assigns unprivileged identities to local groups with elevated privileges", + "name": "GPO assigns unprivileged identities to local groups with elevated privileges" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "3f87e0b0-fc06-4986-a94c-e08781253dc8", + "name": "DCs vulnerable to NTLM relay to LDAP attacks" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-DCLdapSign", + "name": "[T]Check if signing is really required for LDAP" + }, + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-DCLdapsChannelBinding", + "name": "[T]Check if the Channel Binding is enabled for LDAPS" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Unsecure domain configurations: LDAP Signing", + "name": "Unsecure domain configurations: LDAP Signing" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "97e05e67-5961-4aba-a8e7-fe5f92334035", + "name": "Domains with smart card accounts where smart account passwords do not expire" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-SmartCardPwdRotation", + "name": "[T]Automatic password rotation for smart card is not in place" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "7e56f2e7-79c3-4f0d-aa3e-14cf3de7ab73", + "name": "Smart card accounts with passwords not rotated in over 1 year" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-SmartCardRequired", + "name": "[T]Check for accounts using smart card with unchanged password for a long time" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "8c3e0811-a31b-45b4-a29d-1dce80fa2c5f", + "name": "Domains without Protected Users group" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-ProtectedUsers", + "name": "[M]Check for presence of the Protected Users group" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "92aa81d6-b08e-4abb-ae39-ecbe5735a74c", + "name": "Computer owners who can obtain LAPS passwords" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-LAPS-Joined-Computers", + "name": "[T]Check if LAPS passwords can be retrieved from computers that have been added manually by users" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "6e9beb8a-ad14-43de-bda1-644d174a5906", + "name": "Principals with DCSync privileges" + }, + "maps_to": [ + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Remove non-admin accounts with DCSync permissions", + "name": "Remove non-admin accounts with DCSync permissions" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "f9b440b5-732c-4ed3-b6d2-83857db17e1a", + "name": "Domains without Microsoft LAPS computers" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-LAPS-Not-Installed", + "name": "[M][T]Check if the LAPS tool to handle the native local administrator passwords is installed" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "27a6f917-8ed4-4e2e-9b38-41a4b6de1b14", + "name": "Tier Zero computers with the WebClient running" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "A-DC-WebClient", + "name": "[T]Ensure that the WebClient client cannot be abused to force a DC connection to a HTTP server" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "13485477-f026-4b1f-906d-4f2e37364ba4", + "name": "Tier Zero computers not requiring inbound SMB signing" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "A-SMB2SignatureNotEnabled", + "name": "[T]Check if the file share protocol can sign its network dialog" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "e303498f-e3d4-489d-8a34-b68e187bc4e7", + "name": "Accounts with clear-text password attributes" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded attributes scope to include 'unicodepwd' and 'msSFU30Password'", + "id": "A-UnixPwd", + "name": "[T]Check if attributes unixUserPassword and userPassword are set" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "a950fdab-5934-4c69-a88b-e2e0e3da9d52", + "name": "Domains allowing unauthenticated NSPI RPC binds" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-DsHeuristicsAllowAnonNSPI", + "name": "[T]Check for access without any account to the Name Service Provider Interface (NSPI) protocol" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "ebc79aa4-e816-4be8-93fe-a0b30dbc771d", + "name": "Domains allowing unauthenticated rootDSE searches and binds" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-DsHeuristicsAnonymous", + "name": "[T]Check for access without any account via a forest wide setting" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "41a08d76-f8a5-4296-ad19-464c4c5c69fe", + "name": "Domains allowing unauthenticated domain enumeration" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-PreWin2000Anonymous", + "name": "[T]Check for Windows 2000 compatibility which allows access to the domain without any account" + } + ] + }, + { + "source": "Nessus", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Null sessions", + "name": "Null sessions" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "02202726-d86d-46c2-891c-9770c635f76f", + "name": "Domains not mitigating CVE-2021-42291" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-DsHeuristicsLDAPSecurity", + "name": "[T]Check if the mitigation for CVE-2021-42291 has been enabled" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "cb0b1591-5c3e-45f1-afb7-984e5ad865d0", + "name": "Domains not verifying UPN and SPN uniqueness" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-DsHeuristicsDoNotVerifyUniqueness", + "name": "[T]Check if the UPN and SPN uniqueness check has been disabled" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "1e1e6fdd-6973-4547-906c-a494b5fbdcba", + "name": "Domains allowing authenticated domain enumeration" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-PreWin2000AuthenticatedUsers", + "name": "[T]Check that the Pre-Windows 2000 Compatible Access group does not contain Authenticated Users" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "091995b9-7254-473a-996f-6b8368d20431", + "name": "Non-default members in Pre-Windows 2000 Compatible Access" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-PreWin2000Other", + "name": "[T]Check that the Pre-Windows 2000 Compatible Access group has not been modified from its default" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "e7f703b3-5dba-4aef-8346-4d589be2c828", + "name": "Non-Tier Zero account with 'Admin Count' flag" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "A-AdminSDHolder", + "name": "[M]Check for suspicious account(s) used in administrator activities" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "7d258d2d-a43d-4a90-85d7-71c946ae5fd7", + "name": "Domains with a minimum default password policy length less than 15 characters" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Increased minimum length to 15 based on NIST guidance", + "id": "A-MinPwdLen", + "name": "[T]Check for short password length in password policy" + } + ] + } + ] + }, + { + "bloodhound_query": { + "guid": "bb0f620d-6a55-4413-ac74-4c82905e8598", + "name": "Enabled built-in guest user accounts" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "A-Guest", + "name": "[M][T]Check if the guest account is enabled" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "exact", + "mapping_scope_detail": "", + "id": "Built-in Active Directory Guest account is enabled", + "name": "Built-in Active Directory Guest account is enabled" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/docs/security-assessment-mapping.md b/docs/security-assessment-mapping.md new file mode 100644 index 0000000..55e3aa8 --- /dev/null +++ b/docs/security-assessment-mapping.md @@ -0,0 +1,65 @@ +# BloodHound as a Comprehensive Assessment Platform +BloodHound was designed to solve the complex problem of attack paths. Beyond this primary function, users can utilize BloodHound's powerful query language to validate simpler security assessment scenarios tested by various other tools (e.g., “Which users have non-expiring passwords?”). + +To assist in these broader security assessment capabilities, BloodHound queries have been mapped to common security assessment tools, demonstrating overlap in capabilities. + +This approach enables different security teams to leverage BloodHound's comprehensive attack path data for multiple kinds of risk validation, whether they're conducting red team assessments, blue team analysis, or compliance audits. + +The BloodHound-centric mapping data is available at [security-assessment-mapping.json](/docs/security-assessmet-mapping.json) + +## Assessment Coverage Overview + +The following show which other security tools the mapping supports and the number BloodHound queries in the BloodHound Query Library that correspond to controls performed by the tools. + +| Security Tool | Total Controls | Mapped Controls | Coverage | +|---------------|-------------------|---------------|----------| +| [Netwrix PingCastle](https://www.pingcastle.com/PingCastleFiles/ad_hc_rules_list.html) | 186 | 105 | 56% | +| [Microsoft Defender for Identity: Security Posture Assessment](https://learn.microsoft.com/en-us/defender-for-identity/security-assessment) | 45 | 35 | 78% | +| [Tenable Nessus: Active Directory Starter Scan](https://www.tenable.com/blog/new-in-nessus-find-and-fix-these-10-active-directory-misconfigurations) | 10 | 10 | 100% | + +## Mapping Structure +Each mapping includes a type that describes the relationship: +- `exact` - Query identifies the same risk with same scope +- `partial` - Query covers the core risk but with different approach/scope +- `superset` - Query covers everything the other tool does plus additional risk analysis +- `combination` - Single query maps to multiple controls that together equal its functionality + +Each BloodHound query entry includes its GUID and an array of tool mappings. Tool mappings specify the security tool, specific control details, mapping type, and any relevant notes about scope differences. + +For example, the below mapping excerpt shows the BloodHound query [Tier Zero computers with passwords older than the default maximum password age](../queries/Tier%20Zero%20computers%20with%20passwords%20older%20than%20the%20default%20maximum%20password%20age.yml) maps to one PingCastle control and one MDI, while also supsesetting them - increasing risk coverage by expanding the scope to Tier Zero. + +```json +{ + "bloodhound_query": { + "guid": "b6d6d0bf-130e-4719-996b-adc29bba36e9", + "name": "Tier Zero computers with passwords older than the default maximum password age" + }, + "maps_to": [ + { + "source": "PingCastle", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "S-PwdLastSet-DC", + "name": "[M]Check if all DC are using regular password change practices" + } + ] + }, + { + "source": "MDI", + "controls": [ + { + "mapping_scope": "superset", + "mapping_scope_detail": "Expanded scope to Tier Zero", + "id": "Change Domain Controller computer account old password", + "name": "Change Domain Controller computer account old password" + } + ] + } + ] +} +``` + +> [!IMPORTANT] +> These mappings are provided "as is" with [Limitation of Liability](/LICENSE). While every effort has been made to ensure their accuracy, they have been created based on public documentation. Please contribute to the project if you identify any inaccuracies by opening an Issue or submitting a Pull Request. It is best practice to use multiple tools to ensure comprehensive risk coverage and accuracy. From 589fd9e54adf658d1db7f5458b2680f83d327951 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 17 Jun 2025 11:12:16 +0200 Subject: [PATCH 07/17] remove duplicate queries Duplicates pre-built queries: name: Foreign principals in Tier Zero / High Value targets guid: 95bec736-86ef-4017-8465-9b9b66548b17 name: Tier Zero / High Value external Entra ID users guid: 20e07417-d286-4dca-a962-568f2b262f65 --- ...eign AZ Service Principals in Tier Zero.yml | 18 ------------------ ...e Principals in control of Azure tenant.yml | 17 ----------------- ...Foreign External AZ users in Tier Zero.yaml | 16 ---------------- 3 files changed, 51 deletions(-) delete mode 100644 queries/Foreign AZ Service Principals in Tier Zero.yml delete mode 100644 queries/Foreign AZ Service Principals in control of Azure tenant.yml delete mode 100644 queries/Foreign External AZ users in Tier Zero.yaml diff --git a/queries/Foreign AZ Service Principals in Tier Zero.yml b/queries/Foreign AZ Service Principals in Tier Zero.yml deleted file mode 100644 index 274f8ff..0000000 --- a/queries/Foreign AZ Service Principals in Tier Zero.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Foreign AZ Service Principals in Tier Zero -guid: 4d567239-2e68-43e2-8f26-97655b8a37fb -prebuilt: false -platforms: Azure -category: Azure Hygiene -description: -query: |- - MATCH (sp:AZServicePrincipal) - WHERE toUpper(sp.appownerorganizationid) <> toUpper(sp.tenantid) - AND ((sp:Tag_Tier_Zero) OR COALESCE(sp.system_tags, '') CONTAINS 'admin_tier_0') - // Ensure AZServicePrincipal has a valid appownerorganizationid - AND sp.appownerorganizationid CONTAINS "-" - RETURN sp - LIMIT 1000 -revision: 1 -resources: https://posts.specterops.io/microsoft-breach-how-can-i-see-this-in-bloodhound-33c92dca4c65 -acknowledgements: Stephen Hinck - diff --git a/queries/Foreign AZ Service Principals in control of Azure tenant.yml b/queries/Foreign AZ Service Principals in control of Azure tenant.yml deleted file mode 100644 index 88a7e87..0000000 --- a/queries/Foreign AZ Service Principals in control of Azure tenant.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Foreign AZ Service Principals in control of Azure tenant -guid: c82c17f1-7253-4e3a-b5d2-3647aa388f4a -prebuilt: false -platforms: Azure -category: Dangerous Privileges -description: -query: |- - MATCH p = (sp:AZServicePrincipal)-[]->(t:AZTenant) - WHERE toUpper(sp.appownerorganizationid) <> toUpper(t.tenantid) - // Ensure AZServicePrincipal has a valid appownerorganizationid - AND sp.appownerorganizationid CONTAINS "-" - RETURN p - LIMIT 1000 -revision: 1 -resources: https://posts.specterops.io/microsoft-breach-how-can-i-see-this-in-bloodhound-33c92dca4c65 -acknowledgements: Stephen Hinck - diff --git a/queries/Foreign External AZ users in Tier Zero.yaml b/queries/Foreign External AZ users in Tier Zero.yaml deleted file mode 100644 index 3178a10..0000000 --- a/queries/Foreign External AZ users in Tier Zero.yaml +++ /dev/null @@ -1,16 +0,0 @@ -name: Foreign External AZ users in Tier Zero -guid: 3a2b7588-522f-4039-8a07-d971e0b214cb -prebuilt: false -platforms: Azure -category: Azure Hygiene -description: -query: |- - MATCH (n:AZUser) - WHERE n.name CONTAINS "#EXT#@" - AND ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0') - RETURN n - LIMIT 1000 -revision: 2 -resources: https://learn.microsoft.com/en-us/entra/external-id/user-properties#key-properties-of-the-microsoft-entra-b2b-collaboration-user -acknowledgements: Martin Sohn Christensen, @martinsohndk - From 801827f0f0ed2700e4c096cc1225bbe4feeddd55 Mon Sep 17 00:00:00 2001 From: Joey Dreijer Date: Tue, 17 Jun 2025 12:17:59 +0200 Subject: [PATCH 08/17] Added built step --- .github/workflows/syntax.yml | 32 ++++++++++++++++++++++++++++++++ tests/schema.py | 6 ++++++ utilities/python/schema.py | 6 ++++++ 3 files changed, 44 insertions(+) diff --git a/.github/workflows/syntax.yml b/.github/workflows/syntax.yml index 7db1ff7..04909bc 100644 --- a/.github/workflows/syntax.yml +++ b/.github/workflows/syntax.yml @@ -35,3 +35,35 @@ jobs: with: name: test-report path: test-report.md + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Convert queries into single json + run: | + python utilities/python/convert.py ./queries ./Queries.json + + - name: Commit if changed + run: | + git add ./Queries.json + if git diff --staged --quiet; then + echo "No changes to commit" + else + git commit -m "Update combined queries" + git push + fi diff --git a/tests/schema.py b/tests/schema.py index 0cfbbaa..8924c9e 100644 --- a/tests/schema.py +++ b/tests/schema.py @@ -19,14 +19,20 @@ class CypherQuery(BaseModel): @field_validator('platforms', mode='after') @classmethod def platforms_is_list(cls, value: str | list[str]) -> list[str]: + if value is None: + return [] return value if isinstance(value, list) else [value] @field_validator('resources', mode='after') @classmethod def resources_is_list(cls, value: str | list[str]) -> list[str]: + if value is None: + return [] return value if isinstance(value, list) else [value] @field_validator('acknowledgements', mode='after') @classmethod def acknowledgementsis_list(cls, value: str | list[str]) -> list[str]: + if value is None: + return [] return value if isinstance(value, list) else [value] diff --git a/utilities/python/schema.py b/utilities/python/schema.py index 0cfbbaa..8924c9e 100644 --- a/utilities/python/schema.py +++ b/utilities/python/schema.py @@ -19,14 +19,20 @@ class CypherQuery(BaseModel): @field_validator('platforms', mode='after') @classmethod def platforms_is_list(cls, value: str | list[str]) -> list[str]: + if value is None: + return [] return value if isinstance(value, list) else [value] @field_validator('resources', mode='after') @classmethod def resources_is_list(cls, value: str | list[str]) -> list[str]: + if value is None: + return [] return value if isinstance(value, list) else [value] @field_validator('acknowledgements', mode='after') @classmethod def acknowledgementsis_list(cls, value: str | list[str]) -> list[str]: + if value is None: + return [] return value if isinstance(value, list) else [value] From 843e5bb204fd2378da0c514cf98801a6b80f5745 Mon Sep 17 00:00:00 2001 From: Joey Dreijer Date: Tue, 17 Jun 2025 12:20:55 +0200 Subject: [PATCH 09/17] Require test to pass --- .github/workflows/syntax.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/syntax.yml b/.github/workflows/syntax.yml index 04909bc..8a8b6d2 100644 --- a/.github/workflows/syntax.yml +++ b/.github/workflows/syntax.yml @@ -6,7 +6,7 @@ on: workflow_dispatch: permissions: - contents: read + contents: write jobs: test: @@ -38,6 +38,7 @@ jobs: build: runs-on: ubuntu-latest + needs: test steps: - uses: actions/checkout@v4 - name: Set up Python 3.10 @@ -58,6 +59,12 @@ jobs: run: | python utilities/python/convert.py ./queries ./Queries.json + + - name: Configure Git + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + - name: Commit if changed run: | git add ./Queries.json From 3aec004d4f7c45b6ce6e7ce69c25369029319f59 Mon Sep 17 00:00:00 2001 From: Joey Dreijer Date: Tue, 17 Jun 2025 12:23:07 +0200 Subject: [PATCH 10/17] Require test to pass on branch --- .github/workflows/syntax.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/syntax.yml b/.github/workflows/syntax.yml index 8a8b6d2..12ae534 100644 --- a/.github/workflows/syntax.yml +++ b/.github/workflows/syntax.yml @@ -41,6 +41,8 @@ jobs: needs: test steps: - uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} - name: Set up Python 3.10 uses: actions/setup-python@v3 with: From a048782ed26c4dd80c7f55fab37bda1f7a59f2a4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 17 Jun 2025 10:23:41 +0000 Subject: [PATCH 11/17] Update combined queries --- Queries.json | 3050 ++++++++++++++++++++++++++++---------------------- 1 file changed, 1684 insertions(+), 1366 deletions(-) diff --git a/Queries.json b/Queries.json index e1994b5..4caa5c3 100644 --- a/Queries.json +++ b/Queries.json @@ -1,2381 +1,2699 @@ [ { - "name": "Computers with the outgoing NTLM setting set to Deny all", - "guid": "a9ddca74-feeb-4dbf-8b0f-de08b3cfa8a6", + "name": "Domain controllers with weak certificate binding enabled", + "guid": "a2444d99-10b5-412d-8fea-4b063cfddd2c", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "NTLM Relay Attacks", + "category": "Active Directory Certificate Services", "description": null, - "query": "MATCH (c:Computer)\nWHERE c.restrictoutboundntlm = True\nRETURN c LIMIT 1000", + "query": "MATCH p = (s:Computer)-[:DCFor]->(:Domain)\nWHERE s.strongcertificatebindingenforcementraw = 0 OR s.strongcertificatebindingenforcementraw = 1\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Shortest paths to systems trusted for unconstrained delegation", - "guid": "16a9e47b-45f8-4514-b409-771bb5186142", - "prebuilt": true, - "platform": [ + "name": "Kerberos-enabled service accounts without AES encryption support", + "guid": "cb8cf96e-21c9-422b-9439-390a13446ca6", + "prebuilt": false, + "platforms": [ "Active Directory" ], - "category": "Shortest Paths", - "description": null, - "query": "MATCH p=shortestPath((s)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|MemberOf|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation*1..]->(t:Computer))\nWHERE t.unconstraineddelegation = true AND s<>t\nRETURN p\nLIMIT 1000", - "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "category": "Active Directory Hygiene", + "description": "Accounts without Kerberos AES encryption support, or passwords set before the existence of Windows Server 2008 Domain Controller which therefore lack AES encryption keys.", + "query": "MATCH (n:Base)\nWHERE n.hasspn = true\nAND ((\n n.supportedencryptiontypes <> ['Not defined']\n OR n.supportedencryptiontypes <> []\n OR NONE(type IN n.supportedencryptiontypes WHERE type CONTAINS 'AES128' OR type CONTAINS 'AES256')\n)\nOR (n.pwdlastset < 1204070400 // Password Last Set before Windows Server 2008\nAND NOT n.pwdlastset IN [-1.0, 0.0]\n))\nRETURN n\nLIMIT 100", + "revision": 2, + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "All service principals with Microsoft Graph privilege to grant arbitrary App Roles", - "guid": "e6d6b5da-89da-4514-a409-2d6e368397da", - "prebuilt": true, - "platform": [ - "Azure" + "name": "Large default groups with outbound control of OUs", + "guid": "310b3626-f8e6-4ab0-832c-72df6048597f", + "prebuilt": false, + "platforms": [ + "Active Directory" ], - "category": "Microsoft Graph", + "category": "Dangerous Privileges", "description": null, - "query": "MATCH p=(:AZServicePrincipal)-[:AZMGGrantAppRoles]->(:AZTenant)\nRETURN p\nLIMIT 1000", + "query": "MATCH p=(n:Group)-[]->(:OU)\nWHERE n.objectid ENDS WITH \"-513\" // DOMAIN USERS\nOR n.objectid ENDS WITH \"-515\" // DOMAIN COMPUTERS\nOR n.objectid ENDS WITH \"-S-1-5-11\" // AUTHENTICATED USERS\nOR n.objectid ENDS WITH \"-S-1-1-0\" // EVERYONE\nOR n.objectid ENDS WITH \"S-1-5-32-545\" // USERS\nOR n.objectid ENDS WITH \"S-1-5-32-546\" // GUESTS\nOR n.objectid ENDS WITH \"S-1-5-7\" // ANONYMOUS\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Accounts with SID History", - "guid": "8172d52c-a975-49bd-9180-5b6efc59c9ab", + "name": "Non-Tier Zero accounts with SID History of Tier Zero accounts", + "guid": "59744dfe-9411-4daf-b342-1203dc62acd4", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Active Directory Hygiene", + "category": "Dangerous Privileges", "description": null, - "query": "MATCH p=(:Base)-[:HasSIDHistory]->(:Base)\nRETURN p", + "query": "MATCH p=(n:Base)-[:HasSIDHistory]->(m:Base)\nWHERE ((m:Tag_Tier_Zero) OR COALESCE(m.system_tags, '') CONTAINS 'admin_tier_0')\nAND NOT ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Domain controllers with weak certificate binding enabled", - "guid": "a2444d99-10b5-412d-8fea-4b063cfddd2c", + "name": "Users with passwords not rotated in over 1 year", + "guid": "be70d1bd-b7eb-40b0-971c-eefc50eca032", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Active Directory Certificate Services", + "category": "Active Directory Hygiene", "description": null, - "query": "MATCH p = (s:Computer)-[:DCFor]->(:Domain)\nWHERE s.strongcertificatebindingenforcementraw = 0 OR s.strongcertificatebindingenforcementraw = 1\nRETURN p\nLIMIT 1000", + "query": "WITH 365 as days_since_change\nMATCH (u:User)\nWHERE u.pwdlastset < (datetime().epochseconds - (days_since_change * 86400))\nAND NOT u.pwdlastset IN [-1.0, 0.0]\nRETURN u\nLIMIT 100", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Servers where Domain Users can RDP", - "guid": "b9a330ae-1d89-44d4-8f74-9ca18e93eb92", + "name": "Computers with the outgoing NTLM setting set to Deny all", + "guid": "a9ddca74-feeb-4dbf-8b0f-de08b3cfa8a6", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Dangerous Privileges", + "category": "NTLM Relay Attacks", "description": null, - "query": "MATCH p=(s:Group)-[:CanRDP]->(t:Computer)\nWHERE s.objectid ENDS WITH '-513' AND toUpper(t.operatingsystem) CONTAINS 'SERVER'\nRETURN p\nLIMIT 1000", + "query": "MATCH (c:Computer)\nWHERE c.restrictoutboundntlm = True\nRETURN c LIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Unresolved SID with outbound control", - "guid": "4e8429f9-cba2-41e9-bac6-0c42f96b2c57", + "name": "Accounts with SID History", + "guid": "8172d52c-a975-49bd-9180-5b6efc59c9ab", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", "description": null, - "query": "MATCH p=(n:Base)-[r]->(:Base)\nWHERE r.isacl\nAND n.name CONTAINS \"S-1-5-21-\" // Unresolved SID\nRETURN p\nLIMIT 1000", + "query": "MATCH p=(:Base)-[:HasSIDHistory]->(:Base)\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "All service principals with Microsoft Graph App Role assignments", - "guid": "74440269-eb41-476b-8dec-b4095569b029", + "name": "Tier Zero computers not owned by Tier Zero", + "guid": "99d29ded-223a-442b-a0e0-f8b5694c6441", + "prebuilt": false, + "platforms": [ + "Active Directory" + ], + "category": "Dangerous Privileges", + "description": null, + "query": "MATCH p=(n:Base)-[:Owns]->(:Computer)\nWHERE NOT coalesce(n.system_tags, \"\") CONTAINS \"admin_tier_0\"\nRETURN p", + "revision": 1, + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] + }, + { + "name": "Principals with DCSync privileges", + "guid": "6e9beb8a-ad14-43de-bda1-644d174a5906", "prebuilt": true, - "platform": [ - "Azure" + "platforms": [ + "Active Directory" ], - "category": "Microsoft Graph", + "category": "Dangerous Privileges", "description": null, - "query": "MATCH p=(:AZServicePrincipal)-[:AZMGAppRoleAssignment_ReadWrite_All|AZMGApplication_ReadWrite_All|AZMGDirectory_ReadWrite_All|AZMGGroupMember_ReadWrite_All|AZMGGroup_ReadWrite_All|AZMGRoleManagement_ReadWrite_Directory|AZMGServicePrincipalEndpoint_ReadWrite_All]->(:AZServicePrincipal)\nRETURN p\nLIMIT 1000", + "query": "MATCH p=(:Base)-[:DCSync|AllExtendedRights|GenericAll]->(:Domain)\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Domains not mitigating CVE-2021-42291", - "guid": "02202726-d86d-46c2-891c-9770c635f76f", + "name": "Domains with a minimum default password policy length less than 15 characters", + "guid": "7d258d2d-a43d-4a90-85d7-71c946ae5fd7", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", - "description": "Checks the AttributeAuthorizationOnLDAPAdd flag of dSHeuristics.", - "query": "MATCH (n:Domain)\nWHERE n.dsheuristics =~ \".{27}[^1].*\"\nRETURN n", + "description": null, + "query": "MATCH (n:Domain)\nWHERE n.minpwdlength < 15\nRETURN n", "revision": 1, - "note": null, - "resources": "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/e5899be4-862e-496f-9a38-33950617d2c5", - "acknowledgement": null + "resources": [ + "https://pages.nist.gov/800-63-3/sp800-63b.html" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Shortest paths from Domain Users to Tier Zero / High Value targets", - "guid": "469dc0f3-71b8-41b0-a03b-b4af7874665d", + "name": "Enrollment rights on published certificate templates with no security extension", + "guid": "0677b70c-4e04-4e89-a6a2-f5764604a6a7", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Shortest Paths", + "category": "Active Directory Certificate Services", "description": null, - "query": "MATCH p=shortestPath((s:Group)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|MemberOf|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation*1..]->(t:Base))\nWHERE s.objectid ENDS WITH '-513' AND s<>t\nAND ((t:Tag_Tier_Zero) OR COALESCE(t.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p\nLIMIT 1000", + "query": "MATCH p = (:Base)-[:Enroll|GenericAll|AllExtendedRights]->(ct:CertTemplate)-[:PublishedTo]->(:EnterpriseCA)\nWHERE ct.nosecurityextension = true\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Paths from Domain Users to Tier Zero / High Value targets", - "guid": "977bec40-565c-40b8-90c8-e3e122c291cd", + "name": "Servers where Domain Users can RDP", + "guid": "b9a330ae-1d89-44d4-8f74-9ca18e93eb92", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Dangerous Privileges", "description": null, - "query": "MATCH p=shortestPath((s:Group)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|MemberOf|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation*1..]->(t:Base))\nWHERE s.objectid ENDS WITH '-513' AND s<>t\nAND ((t:Tag_Tier_Zero) OR COALESCE(t.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p\nLIMIT 1000", + "query": "MATCH p=(s:Group)-[:CanRDP]->(t:Computer)\nWHERE s.objectid ENDS WITH '-513' AND toUpper(t.operatingsystem) CONTAINS 'SERVER'\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { "name": "All Global Administrators", "guid": "94d7d765-6837-4eb8-aa33-e1c9ef262cdc", "prebuilt": true, - "platform": [ + "platforms": [ "Azure" ], "category": "General", "description": null, "query": "MATCH p = (:AZBase)-[:AZGlobalAdmin*1..]->(:AZTenant)\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Large default group added to computer-local group", - "guid": "dde133d2-b4d2-4de9-a656-905f3bf066f3", + "name": "Computer owners who can obtain LAPS passwords", + "guid": "92aa81d6-b08e-4abb-ae39-ecbe5735a74c", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Dangerous Privileges", - "description": null, - "query": "MATCH p=(n:Group)-[:MemberOfLocalGroup]->(m:ADLocalGroup)-[:LocalToComputer]->(:Computer)\nWHERE n.objectid =~ \".*-(S-1-5-11|S-1-1-0|S-1-5-32-545|S-1-5-7|-513|-515)$\" // Authenticated Users, Everyone, Users, Anonymous, Domain Users, Domain Computers\nAND NOT m.objectid =~ \".*-(545|574|554)$\" // Users, Certificate Service DCOM Access, Pre-Windows 2000 Compatible Access\nRETURN p", + "description": "Creators of computer objects get abusable rights on the computer object. If the owner is not explicitly granted ReadLAPSPassword they can still compromise the computer with the abusable owner rights.", + "query": "MATCH p = (c:Computer)<-[:GenericAll|Owns|WriteDacl|WriteOwner|AllExtendedRights]-(n:User)\nWHERE c.haslaps = true AND c.ownersid = n.objectid\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Domains where any user can join a computer to the domain", - "guid": "421921fa-bc0f-4659-9680-b7481adcb132", - "prebuilt": true, - "platform": [ + "name": "Tier Zero computers with the WebClient running", + "guid": "27a6f917-8ed4-4e2e-9b38-41a4b6de1b14", + "prebuilt": false, + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", "description": null, - "query": "MATCH (n:Domain)\nWHERE n.machineaccountquota > 0\nRETURN n", + "query": "MATCH (c:Computer)\nWHERE c.webclientrunning = True\nAND ((c:Tag_Tier_Zero) OR COALESCE(c.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN c LIMIT 1000", "revision": 1, - "note": "Does not check the 'Add workstations to domain' URA Security Policy on DCs.", - "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" - ], - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Computers with passwords older than the default maximum password age", - "guid": "185c5010-8d4f-4f9b-b24e-831707dddfca", + "name": "ACEs across trusts", + "guid": "c902d3b4-1a75-4335-acd7-28246dab746d", "prebuilt": false, - "platform": [ - "Active Directory" - ], - "category": "Active Directory Hygiene", - "description": "Machine account passwords are regularly changed for security purposes. Starting with Windows 2000-based computers, the machine account password automatically changes every 30 days.", - "query": "WITH 60 as rotation_period\nMATCH (n:Computer)\nWHERE n.pwdlastset < (datetime().epochseconds - (rotation_period * 86400)) // password not rotated\nAND n.enabled = true // enabled computers\nAND n.whencreated < (datetime().epochseconds - (rotation_period * 86400)) // exclude recently created computers\nAND n.lastlogontimestamp > (datetime().epochseconds - (rotation_period * 86400)) // active computers (Replicated value)\nAND n.lastlogon > (datetime().epochseconds - (rotation_period * 86400)) // active computers (Non-replicated value)\nRETURN n", - "revision": 1, - "note": null, - "resources": "https://learn.microsoft.com/en-us/troubleshoot/windows-server/windows-security/disable-machine-account-password", - "acknowledgement": null - }, - { - "name": "Computers not requiring inbound SMB signing", - "guid": "6b1fcfb6-b010-41a2-9d31-f9872fe994ff", - "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "NTLM Relay Attacks", - "description": null, - "query": "MATCH (n:Computer)\nWHERE n.smbsigning = False\nRETURN n", + "category": "Domain Information", + "description": "ACEs granted across a trust, the ACEs are set on trusting objects and the rights are granted to objects from trusted domains.", + "query": "MATCH p=(trustedDomainPrincipal:Base)-[r]->(trustingDomainPrincipal:Base)\nWHERE trustedDomainPrincipal.domainsid <> trustingDomainPrincipal.domainsid\nAND r.isacl\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "On-Prem Users synced to Entra Users with Entra Admin Roles (group delegated)", - "guid": "609d648f-7fb8-42d3-ad99-626f9ce1f121", + "name": "On-Prem Users synced to Entra Users with Azure RM Roles (group delegated)", + "guid": "e4f2eada-8a89-4ba9-89eb-abbee4efbc7a", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory", "Azure" ], "category": "Cross Platform Attack Paths", "description": null, - "query": "MATCH p = (:User)-[:SyncedToEntraUser]->(:AZUser)-[:AZMemberOf]->(:AZGroup)-[:AZHasRole]->(:AZRole)\nRETURN p\nLIMIT 1000", + "query": "MATCH p = (:User)-[:SyncedToEntraUser]->(:AZUser)-[:AZMemberOf]->(:AZGroup)-[:AZOwner|AZUserAccessAdministrator|AZGetCertificates|AZGetKeys|AZGetSecrets|AZAvereContributor|AZKeyVaultContributor|AZContributor|AZVMAdminLogin|AZVMContributor|AZAKSContributor|AZAutomationContributor|AZLogicAppContributor|AZWebsiteContributor]->(:AZBase)\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Principals with passwords stored using reversible encryption", - "guid": "ab900835-b2b8-4674-87b4-8b5141e80439", - "prebuilt": true, - "platform": [ + "name": "Tier Zero computers at risk of constrained delegation", + "guid": "8641e593-f2f2-48ba-bd45-fbc86e9f632a", + "prebuilt": false, + "platforms": [ "Active Directory" ], - "category": "Active Directory Hygiene", + "category": "Dangerous Privileges", "description": null, - "query": "MATCH (n:Base)\nWHERE n.encryptedtextpwdallowed = true\nRETURN n", + "query": "MATCH p = (n:Computer)<-[:AllowedToDelegate]-(:Base)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Enrollment rights on published ESC2 certificate templates", - "guid": "ebc77984-1ceb-4ed2-a395-ce1067847941", - "prebuilt": true, - "platform": [ + "name": "Members of Allowed RODC Password Replication Group", + "guid": "19fc5acd-e30a-4038-a5b5-2e0494f93373", + "prebuilt": false, + "platforms": [ "Active Directory" ], - "category": "Active Directory Certificate Services", + "category": "Domain Information", "description": null, - "query": "MATCH p = (:Base)-[:Enroll|GenericAll|AllExtendedRights]->(c:CertTemplate)-[:PublishedTo]->(:EnterpriseCA)\nWHERE c.requiresmanagerapproval = false\nAND (c.effectiveekus = [''] OR '2.5.29.37.0' IN c.effectiveekus)\nAND (c.authorizedsignatures = 0 OR c.schemaversion = 1)\nRETURN p\nLIMIT 1000", + "query": "MATCH p=(n:Base)-[r:MemberOf]->(m:Group)\nWHERE m.objectid ENDS WITH \"-571\"\nAND (n:User or n:Computer)\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "PKI hierarchy", - "guid": "928acc23-ee4c-40a5-bde7-64c05cc1491d", + "name": "Non-default permissions on IssuancePolicy nodes", + "guid": "b2280665-c91b-448c-8c0f-97d1f38b6f59", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Certificate Services", "description": null, - "query": "MATCH p=()-[:HostsCAService|IssuedSignedBy|EnterpriseCAFor|RootCAFor|TrustedForNTAuth|NTAuthStoreFor*..]->(:Domain)\nRETURN p\nLIMIT 1000", + "query": "MATCH p = (s:Base)-[:GenericAll|GenericWrite|Owns|WriteOwner|WriteDacl]->(:IssuancePolicy)\nWHERE NOT s.objectid ENDS WITH '-512' AND NOT s.objectid ENDS WITH '-519'\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Enrollment rights on certificate templates published to Enterprise CA with User Specified SAN enabled", - "guid": "96e70597-2d74-4503-a624-f1e30b642894", - "prebuilt": true, - "platform": [ + "name": "Trace ACE inheritance", + "guid": "8c5454df-3ae8-412c-b271-3c4c55df7141", + "prebuilt": false, + "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", + "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", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Walter.Legowski, @SadProcessor" + ] }, { - "name": "Tier Zero computers not owned by Tier Zero", - "guid": "99d29ded-223a-442b-a0e0-f8b5694c6441", + "name": "Tier Zero accounts not members of Denied RODC Password Replication Group", + "guid": "e9613406-e346-410b-a033-690a6cf0c708", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Dangerous Privileges", + "category": "Active Directory Hygiene", "description": null, - "query": "MATCH p=(n:Base)-[:Owns]->(:Computer)\nWHERE NOT coalesce(n.system_tags, \"\") CONTAINS \"admin_tier_0\"\nRETURN p", + "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, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Shortest paths to Azure Subscriptions", - "guid": "4785b305-c101-461c-80fc-3fb3ff67a8ce", - "prebuilt": true, - "platform": [ - "Azure" + "name": "Users with logon scripts stored in a trusted domain", + "guid": "8d94d3f3-3d53-4939-a206-3c0a4dd3f646", + "prebuilt": false, + "platforms": [ + "Active Directory" ], - "category": "Shortest Paths", + "category": "Active Directory Hygiene", "description": null, - "query": "MATCH p=shortestPath((s:AZBase)-[:AZAvereContributor|AZContributor|AZGetCertificates|AZGetKeys|AZGetSecrets|AZHasRole|AZMemberOf|AZOwner|AZRunsAs|AZVMContributor|AZAutomationContributor|AZKeyVaultContributor|AZVMAdminLogin|AZAddMembers|AZAddSecret|AZExecuteCommand|AZGlobalAdmin|AZPrivilegedAuthAdmin|AZGrant|AZGrantSelf|AZPrivilegedRoleAdmin|AZResetPassword|AZUserAccessAdministrator|AZOwns|AZCloudAppAdmin|AZAppAdmin|AZAddOwner|AZManagedIdentity|AZAKSContributor|AZNodeResourceGroup|AZWebsiteContributor|AZLogicAppContributor|AZMGAddMember|AZMGAddOwner|AZMGAddSecret|AZMGGrantAppRoles|AZMGGrantRole|SyncedToADUser|AZRoleEligible|AZContains*1..]->(t:AZSubscription))\nWHERE s<>t\nRETURN p\nLIMIT 1000", + "query": "MATCH (n:User)\nWHERE n.logonscript IS NOT NULL\nMATCH (d:Domain)-[:TrustedBy]->(:Domain)-[:Contains*1..]->(n)\nWITH n,last(split(d.name, '@')) AS domain\nWHERE toUpper(n.logonscript) STARTS WITH (\"\\\\\\\\\" + domain + \"\\\\\")\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Kerberoastable users with most admin privileges", - "guid": "9907b208-494c-4ba6-846d-485e6de14e17", - "prebuilt": true, - "platform": [ + "name": "Domains not verifying UPN and SPN uniqueness", + "guid": "cb0b1591-5c3e-45f1-afb7-984e5ad865d0", + "prebuilt": false, + "platforms": [ "Active Directory" ], - "category": "Kerberos Interaction", - "description": null, - "query": "MATCH (u:User)\nWHERE u.hasspn = true\n AND u.enabled = true\n AND NOT u.objectid ENDS WITH '-502'\n AND NOT COALESCE(u.gmsa, false) = true\n AND NOT COALESCE(u.msa, false) = true\nMATCH (u)-[:MemberOf|AdminTo*1..]->(c:Computer)\nWITH DISTINCT u, COUNT(c) AS adminCount\nRETURN u\nORDER BY adminCount DESC\nLIMIT 100", + "category": "Active Directory Hygiene", + "description": "Checks the DoNotVerifyUPNAndOrSPNUniqueness flag of dSHeuristics.", + "query": "MATCH (n:Domain)\nWHERE n.dsheuristics =~ \".{20}[^0].*\"\nRETURN n", "revision": 1, - "note": null, - "resources": "https://attack.mitre.org/techniques/T1558/003/", - "acknowledgement": null - }, - { - "name": "On-Prem Users synced to Entra Users that Own Entra Objects", - "guid": "4baf1026-e64c-4e31-afeb-2090b8090130", - "prebuilt": true, - "platform": [ - "Active Directory", - "Azure" + "resources": [ + "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/e5899be4-862e-496f-9a38-33950617d2c5" ], - "category": "Cross Platform Attack Paths", - "description": null, - "query": "MATCH p = (:User)-[:SyncedToEntraUser]->(:AZUser)-[:AZOwns]->(:AZBase)\nRETURN p\nLIMIT 1000", - "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Domains allowing unauthenticated NSPI RPC binds", - "guid": "a950fdab-5934-4c69-a88b-e2e0e3da9d52", + "name": "Non-Tier Zero account with 'Admin Count' flag", + "guid": "e7f703b3-5dba-4aef-8346-4d589be2c828", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", - "description": "Checks the fAllowAnonNSPI flag of dSHeuristics.", - "query": "MATCH (n:Domain)\nWHERE n.dsheuristics =~ \".{7}[^0].*\"\nRETURN n", + "description": "Users who were a member of one of AD's built-in administrative groups but are not currently Tier Zero.", + "query": "MATCH (n:User)\nWHERE NOT ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nAND n.admincount = true\nRETURN n", "revision": 1, - "note": null, - "resources": "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/e5899be4-862e-496f-9a38-33950617d2c5", - "acknowledgement": null + "resources": [ + "https://learn.microsoft.com/en-us/windows/win32/adschema/a-admincount" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "All incoming and local paths for a specific computer", - "guid": "1f67e538-19d4-4020-89c8-5b39b31571bd", + "name": "Tier Zero computers with unsupported operating systems", + "guid": "a87b558c-5746-4a90-9f83-c86e7b924a52", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "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", + "category": "Active Directory Hygiene", + "description": null, + "query": "MATCH (c:Computer)\nWHERE c.operatingsystem =~ '(?i).*Windows.* (2000|2003|2008|2012|xp|vista|7|8|me|nt).*'\nAND ((c:Tag_Tier_Zero) OR COALESCE(c.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN c\nLIMIT 100", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Tier Zero computers with the WebClient running", - "guid": "27a6f917-8ed4-4e2e-9b38-41a4b6de1b14", + "name": "Large default group added to computer-local group", + "guid": "dde133d2-b4d2-4de9-a656-905f3bf066f3", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Active Directory Hygiene", + "category": "Dangerous Privileges", "description": null, - "query": "MATCH (c:Computer)\nWHERE c.webclientrunning = True\nAND ((c:Tag_Tier_Zero) OR COALESCE(c.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN c LIMIT 1000", + "query": "MATCH p=(n:Group)-[:MemberOfLocalGroup]->(m:ADLocalGroup)-[:LocalToComputer]->(:Computer)\nWHERE n.objectid =~ \".*-(S-1-5-11|S-1-1-0|S-1-5-32-545|S-1-5-7|-513|-515)$\" // Authenticated Users, Everyone, Users, Anonymous, Domain Users, Domain Computers\nAND NOT m.objectid =~ \".*-(545|574|554)$\" // Users, Certificate Service DCOM Access, Pre-Windows 2000 Compatible Access\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Non-Tier Zero account with excessive control", - "guid": "944cecfe-519b-4318-b226-e8520161b454", + "name": "Domains without Microsoft LAPS computers", + "guid": "f9b440b5-732c-4ed3-b6d2-83857db17e1a", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Dangerous Privileges", + "category": "Domain Information", "description": null, - "query": "MATCH (d:Domain)-[:Contains*1..]->(u:User)\nWHERE u.enabled = true\nWITH d, COUNT(u) AS enabledUserCount\nMATCH (d)-[:Contains*1..]->(n:Base)-[r:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|ForceChangePassword|AllExtendedRights|AddMember|AllowedToDelegate|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions]->(m:Base)\nWHERE NOT ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nWITH n, enabledUserCount, COLLECT(DISTINCT(m)) AS endNodes\nWHERE SIZE(endNodes) >= 1000\nRETURN n", + "query": "MATCH (d:Domain)\nOPTIONAL MATCH (c:Computer)\nWHERE c.domainsid = d.objectid AND c.haslaps = true\nWITH d, COLLECT(c) AS computers\nWHERE SIZE(computers) = 0\nRETURN d", "revision": 1, - "note": "Finds Non-Tier Zero principals with control of >1000 Non-Tier Zero principals", - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Devices with unsupported operating systems", - "guid": "e3f2b53a-7ce6-4e52-9c74-68b69338288b", + "name": "Shortest paths to Azure Subscriptions", + "guid": "4785b305-c101-461c-80fc-3fb3ff67a8ce", "prebuilt": true, - "platform": [ + "platforms": [ "Azure" ], - "category": "Azure Hygiene", + "category": "Shortest Paths", "description": null, - "query": "MATCH (n:AZDevice)\nWHERE n.operatingsystem CONTAINS 'WINDOWS'\nAND n.operatingsystemversion =~ '(10.0.19044|10.0.22000|10.0.19043|10.0.19042|10.0.19041|10.0.18363|10.0.18362|10.0.17763|10.0.17134|10.0.16299|10.0.15063|10.0.14393|10.0.10586|10.0.10240|6.3.9600|6.2.9200|6.1.7601|6.0.6200|5.1.2600|6.0.6003|5.2.3790|5.0.2195).?.*'\nRETURN n\nLIMIT 100", + "query": "MATCH p=shortestPath((s:AZBase)-[:AZAvereContributor|AZContributor|AZGetCertificates|AZGetKeys|AZGetSecrets|AZHasRole|AZMemberOf|AZOwner|AZRunsAs|AZVMContributor|AZAutomationContributor|AZKeyVaultContributor|AZVMAdminLogin|AZAddMembers|AZAddSecret|AZExecuteCommand|AZGlobalAdmin|AZPrivilegedAuthAdmin|AZGrant|AZGrantSelf|AZPrivilegedRoleAdmin|AZResetPassword|AZUserAccessAdministrator|AZOwns|AZCloudAppAdmin|AZAppAdmin|AZAddOwner|AZManagedIdentity|AZAKSContributor|AZNodeResourceGroup|AZWebsiteContributor|AZLogicAppContributor|AZMGAddMember|AZMGAddOwner|AZMGAddSecret|AZMGGrantAppRoles|AZMGGrantRole|SyncedToADUser|AZRoleEligible|AZContains*1..]->(t:AZSubscription))\nWHERE s<>t\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Non-default delegation on MicrosoftDNS container", - "guid": "008792c0-4458-46a1-a10d-50cdaf95af1e", - "prebuilt": false, - "platform": [ + "name": "Principals with weak supported Kerberos encryption types", + "guid": "ca329573-2157-41da-ab17-4d122c54b11d", + "prebuilt": true, + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", "description": null, - "query": "MATCH p=(n:Base)-[r]->(m:Container)\nWHERE m.distinguishedname STARTS WITH \"CN=MICROSOFTDNS,CN=SYSTEM,DC=\"\nAND NOT n.name STARTS WITH \"DNSADMINS@\"\nAND NOT n.objectid =~ \"-(512|544|519|9)$\"\nAND r.isacl\nRETURN p", + "query": "MATCH (u:Base)\nWHERE 'DES-CBC-CRC' IN u.supportedencryptiontypes\nOR 'DES-CBC-MD5' IN u.supportedencryptiontypes\nOR 'RC4-HMAC-MD5' IN u.supportedencryptiontypes\nRETURN u", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Object name conflict", - "guid": "c561c4f8-ea45-453f-85a2-3fc2e20e7f8c", + "name": "Enabled computers inactive for 180 days - MSSQL Failover Cluster", + "guid": "d263e621-7f1b-4efb-ad25-098fc7d4fb72", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", - "description": "When two objects are created with the same Relative Distinguished Name (RDN) in the same parent Organizational Unit or container, the conflict is recognized by the system when one of the new objects replicates to another domain controller. When this happens, one of the objects is renamed with 'CNF'", - "query": "MATCH (n:Base)\nWHERE n.distinguishedname CONTAINS 'CNF:'\nRETURN n", - "revision": 1, - "note": null, - "resources": "https://learn.microsoft.com/en-us/archive/technet-wiki/15435.active-directory-duplicate-object-name-resolution", - "acknowledgement": null - }, - { - "name": "Shortest paths from Azure Applications to Tier Zero / High Value targets", - "guid": "60ff7c58-a98e-4bc1-9e32-8378d2db0c43", - "prebuilt": true, - "platform": [ - "Azure" - ], - "category": "Shortest Paths", "description": null, - "query": "MATCH p=shortestPath((s:AZApp)-[:AZAvereContributor|AZContributor|AZGetCertificates|AZGetKeys|AZGetSecrets|AZHasRole|AZMemberOf|AZOwner|AZRunsAs|AZVMContributor|AZAutomationContributor|AZKeyVaultContributor|AZVMAdminLogin|AZAddMembers|AZAddSecret|AZExecuteCommand|AZGlobalAdmin|AZPrivilegedAuthAdmin|AZGrant|AZGrantSelf|AZPrivilegedRoleAdmin|AZResetPassword|AZUserAccessAdministrator|AZOwns|AZCloudAppAdmin|AZAppAdmin|AZAddOwner|AZManagedIdentity|AZAKSContributor|AZNodeResourceGroup|AZWebsiteContributor|AZLogicAppContributor|AZMGAddMember|AZMGAddOwner|AZMGAddSecret|AZMGGrantAppRoles|AZMGGrantRole|SyncedToADUser|AZRoleEligible|AZContains*1..]->(t:AZBase))\nWHERE ((t:Tag_Tier_Zero) OR COALESCE(t.system_tags, '') CONTAINS 'admin_tier_0') AND s<>t\nRETURN p\nLIMIT 1000", + "query": "WITH 180 as inactive_days\nMATCH (n:Computer)\nWHERE n.enabled = true\nAND n.lastlogontimestamp < (datetime().epochseconds - (inactive_days * 86400)) // Replicated value\nAND n.lastlogon < (datetime().epochseconds - (inactive_days * 86400)) // Non-replicated value\nAND n.whencreated < (datetime().epochseconds - (inactive_days * 86400)) // Exclude recently created principals\nAND ANY(type IN n.serviceprincipalnames WHERE \n toLower(type) CONTAINS 'mssqlservercluster' OR \n toLower(type) CONTAINS 'mssqlserverclustermgmtapi' OR \n toLower(type) CONTAINS 'msclustervirtualserver')\nRETURN n\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://learn.microsoft.com/en-us/troubleshoot/windows-server/high-availability/troubleshoot-issues-accounts-used-failover-clusters#troubleshoot-password-issues-with-the-cluster-name-account" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Domains without Protected Users group", - "guid": "8c3e0811-a31b-45b4-a29d-1dce80fa2c5f", + "name": "Users with non-default Primary Group membership", + "guid": "93890f88-df2c-4167-a945-a53961d08d00", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Domain Information", + "category": "Active Directory Hygiene", "description": null, - "query": "MATCH (n:Domain)\nWHERE n.collected = true\nOPTIONAL MATCH (m:Group)\nWHERE m.name ENDS WITH n.name\nAND m.objectid ENDS WITH '-525'\nWITH n, m\nWHERE m IS NULL\nRETURN n", + "query": "MATCH p=(n:User)-[r:MemberOf]->(g:Group)\nWHERE NOT g.objectid ENDS WITH \"-513\" // Domain Users\nAND r.isprimarygroup = true\nAND NOT n.objectid ENDS WITH \"-501\" // Guests account, as it has primaryGroup to Guests\nAND (n.gmsa IS NULL OR n.gmsa = false) // Not gMSA, as it has primaryGroup to Domain Computers\nAND (n.msa IS NULL OR n.msa = false) // Not MSA, as it has primaryGroup to Domain Computers\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "KRBTGT accounts with passwords not rotated in over 1 year", - "guid": "1b3ae310-ffa7-4ce5-a37f-6111aef600c8", + "name": "Large default groups with outbound control", + "guid": "a334f21a-3d7f-448e-b7ea-1465a3127bce", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Active Directory Hygiene", + "category": "Dangerous Privileges", "description": null, - "query": "MATCH (n:User)\nWHERE (n.objectid ENDS WITH '-502'\nOR n.name STARTS WITH 'AZUREADKERBEROS.'\nOR n.name STARTS WITH 'KRBTGT_AZUREAD@')\nAND n.pwdlastset < (datetime().epochseconds - (365 * 86400))\nRETURN n", + "query": "MATCH p=(n:Group)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|ForceChangePassword|AllExtendedRights|AddMember|AllowedToDelegate|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC5|ADCSESC6a|ADCSESC6b|ADCSESC7|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13]->(:Base)\nWHERE n.objectid ENDS WITH \"-513\" // DOMAIN USERS\nOR n.objectid ENDS WITH \"-515\" // DOMAIN COMPUTERS\nOR n.objectid ENDS WITH \"-S-1-5-11\" // AUTHENTICATED USERS\nOR n.objectid ENDS WITH \"-S-1-1-0\" // EVERYONE\nOR n.objectid ENDS WITH \"S-1-5-32-545\" // USERS\nOR n.objectid ENDS WITH \"S-1-5-32-546\" // GUESTS\nOR n.objectid ENDS WITH \"S-1-5-7\" // ANONYMOUS\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Users with non-expiring passwords", - "guid": "212c2a98-53d9-4dfa-b177-42c601452dd1", + "name": "Tier Zero users not member of Protected Users", + "guid": "543eb01d-9fa3-4b8f-a936-b46bbfdaa2ae", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", "description": null, - "query": "MATCH (u:User)\nWHERE u.enabled = true\nAND u.pwdneverexpires = true\nRETURN u\nLIMIT 100", + "query": "MATCH (m:User)\nWHERE ((m:Tag_Tier_Zero) OR COALESCE(m.system_tags, '') CONTAINS 'admin_tier_0')\nOPTIONAL MATCH (g:Group)<-[:MemberOf*1..]-(n:Base)\nWHERE g.objectid ENDS WITH '-525'\nWITH m, COLLECT(n) AS matchingNs\nWHERE NONE(n IN matchingNs WHERE n.objectid = m.objectid)\nRETURN m", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Domain migration groups", - "guid": "f39c4953-ae92-4d67-bb50-eb1a161d4d3f", - "prebuilt": false, - "platform": [ + "name": "Disabled Tier Zero / High Value principals", + "guid": "d65a801f-d3ef-4b7e-8030-99ebfd6dad12", + "prebuilt": true, + "platforms": [ "Active Directory" ], - "category": "Domain Information", + "category": "Active Directory Hygiene", "description": null, - "query": "MATCH (n:Group)\nWHERE n.name CONTAINS \"$$$@\"\nRETURN n", + "query": "MATCH (n:Base)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nAND n.enabled = false\nAND NOT n.objectid ENDS WITH '-502' // Removes false positive, KRBTGT\nAND NOT n.objectid ENDS WITH '-500' // Removes false positive, built-in Administrator\nRETURN n\nLIMIT 100", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Tier Zero / High Value external Entra ID users", - "guid": "20e07417-d286-4dca-a962-568f2b262f65", + "name": "Shortest paths to Domain Admins", + "guid": "f40cb34b-5ec7-44bc-9aa8-a200a4a41f22", "prebuilt": true, - "platform": [ - "Azure" + "platforms": [ + "Active Directory" ], - "category": "Azure Hygiene", + "category": "Shortest Paths", "description": null, - "query": "MATCH (n:AZUser)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nAND n.name CONTAINS '#EXT#@'\nRETURN n\nLIMIT 100", + "query": "MATCH p=shortestPath((t:Group)<-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|MemberOf|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation*1..]-(s:Base))\nWHERE t.objectid ENDS WITH '-512' AND s<>t\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Shortest paths to privileged roles", - "guid": "3dc73dd8-4873-4aeb-a88f-56a58c77f512", - "prebuilt": true, - "platform": [ - "Azure" + "name": "Non-Tier Zero principals with control of AdminSDHolder", + "guid": "4c1e0137-5b7f-48d8-bd09-9db7674bca61", + "prebuilt": false, + "platforms": [ + "Active Directory" ], - "category": "Shortest Paths", + "category": "Dangerous Privileges", "description": null, - "query": "MATCH p=shortestPath((s:AZBase)-[:AZAvereContributor|AZContributor|AZGetCertificates|AZGetKeys|AZGetSecrets|AZHasRole|AZMemberOf|AZOwner|AZRunsAs|AZVMContributor|AZAutomationContributor|AZKeyVaultContributor|AZVMAdminLogin|AZAddMembers|AZAddSecret|AZExecuteCommand|AZGlobalAdmin|AZPrivilegedAuthAdmin|AZGrant|AZGrantSelf|AZPrivilegedRoleAdmin|AZResetPassword|AZUserAccessAdministrator|AZOwns|AZCloudAppAdmin|AZAppAdmin|AZAddOwner|AZManagedIdentity|AZAKSContributor|AZNodeResourceGroup|AZWebsiteContributor|AZLogicAppContributor|AZMGAddMember|AZMGAddOwner|AZMGAddSecret|AZMGGrantAppRoles|AZMGGrantRole|SyncedToADUser|AZRoleEligible|AZContains*1..]->(t:AZRole))\nWHERE t.name =~ '(?i)Global Administrator|User Administrator|Cloud Application Administrator|Authentication Policy Administrator|Exchange Administrator|Helpdesk Administrator|Privileged Authentication Administrator' AND s<>t\nRETURN p\nLIMIT 1000", + "query": "MATCH p=(n:Group)-[r:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|ForceChangePassword|AllExtendedRights|AddMember|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteOwnerLimitedRights|OwnsLimitedRights]->(m:Container)\nWHERE NOT ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nAND m.name STARTS WITH \"ADMINSDHOLDER@\"\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/plan/security-best-practices/appendix-c--protected-accounts-and-groups-in-active-directory#adminsdholder" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Public Key Services container", - "guid": "07e94492-71aa-4665-ab8c-e7aec25906cd", + "name": "Shortest paths from Domain Users to Tier Zero / High Value targets", + "guid": "469dc0f3-71b8-41b0-a03b-b4af7874665d", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Active Directory Certificate Services", + "category": "Shortest Paths", "description": null, - "query": "MATCH p = (c:Container)-[:Contains*..]->(:Base)\nWHERE c.distinguishedname starts with 'CN=PUBLIC KEY SERVICES,CN=SERVICES,CN=CONFIGURATION,DC='\nRETURN p\nLIMIT 1000", + "query": "MATCH p=shortestPath((s:Group)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|MemberOf|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation*1..]->(t:Base))\nWHERE s.objectid ENDS WITH '-513' AND s<>t\nAND ((t:Tag_Tier_Zero) OR COALESCE(t.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Shortest paths from Entra Users to Tier Zero / High Value targets", - "guid": "58089b28-54e0-4fd2-bf66-3db480b00e2f", - "prebuilt": true, - "platform": [ + "name": "Microsoft Entra Connect accounts with passwords not rotated in over 90 days", + "guid": "97fb1310-d15d-4d63-82a2-8788056250f1", + "prebuilt": false, + "platforms": [ + "Active Directory", "Azure" ], - "category": "Shortest Paths", - "description": null, - "query": "MATCH p=shortestPath((s:AZUser)-[:AZAvereContributor|AZContributor|AZGetCertificates|AZGetKeys|AZGetSecrets|AZHasRole|AZMemberOf|AZOwner|AZRunsAs|AZVMContributor|AZAutomationContributor|AZKeyVaultContributor|AZVMAdminLogin|AZAddMembers|AZAddSecret|AZExecuteCommand|AZGlobalAdmin|AZPrivilegedAuthAdmin|AZGrant|AZGrantSelf|AZPrivilegedRoleAdmin|AZResetPassword|AZUserAccessAdministrator|AZOwns|AZCloudAppAdmin|AZAppAdmin|AZAddOwner|AZManagedIdentity|AZAKSContributor|AZNodeResourceGroup|AZWebsiteContributor|AZLogicAppContributor|AZMGAddMember|AZMGAddOwner|AZMGAddSecret|AZMGGrantAppRoles|AZMGGrantRole|SyncedToADUser|AZRoleEligible|AZContains*1..]->(t:AZBase))\nWHERE (t:AZBase) AND t.name =~ '(?i)Global Administrator|User Administrator|Cloud Application Administrator|Authentication Policy Administrator|Exchange Administrator|Helpdesk Administrator|Privileged Authentication Administrator' AND s<>t\nAND ((t:Tag_Tier_Zero) OR COALESCE(t.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p\nLIMIT 1000", + "category": "Active Directory Hygiene", + "description": "Micosoft recommends to change the password of MSOL accounts every 90 days to prevent attackers from allowing use of the high privileges", + "query": "WITH 90 as days_since_change\nMATCH (u:User)\nWHERE u.name STARTS WITH \"MSOL_\"\nAND u.pwdlastset < (datetime().epochseconds - (days_since_change * 86400))\nAND NOT u.pwdlastset IN [-1.0, 0.0]\nRETURN u", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://learn.microsoft.com/en-us/defender-for-identity/rotate-password-microsoft-entra-connect" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Computers with membership in Protected Users", - "guid": "a26372f4-2e92-49f6-8993-6657fbc1569a", - "prebuilt": true, - "platform": [ + "name": "Domain Controllers allowing NTLMv1 or LM authentication", + "guid": "4b42513c-f89d-47ff-8d98-908af49d2b48", + "prebuilt": false, + "platforms": [ "Active Directory" ], "category": "NTLM Relay Attacks", "description": null, - "query": "MATCH p = (:Base)-[:MemberOf*1..]->(g:Group)\nWHERE g.objectid ENDS WITH \"-525\"\nRETURN p LIMIT 1000", + "query": "MATCH (dc:Computer)\nWHERE dc.isdc = true\nAND (dc.lmcompatibilitylevel IS NOT NULL AND NOT dc.lmcompatibilitylevel = 5)\nRETURN dc", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Accounts with SID History to a non-existent domain", - "guid": "2710401a-c4c2-4d2c-9edb-d7625045f2e8", - "prebuilt": false, - "platform": [ + "name": "Enabled Tier Zero / High Value principals inactive for 60 days", + "guid": "72550bcb-3c4f-463d-8973-91a49163dc5a", + "prebuilt": true, + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", "description": null, - "query": "MATCH (d:Domain)\nWITH collect(d.objectid) AS domainSIDs\nMATCH p=(n:Base)-[:HasSIDHistory]->(m:Base)\nWHERE NOT n.domainsid IN domainSIDs\nRETURN p", + "query": "WITH 60 as inactive_days\nMATCH (n:Base)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nAND n.enabled = true\nAND n.lastlogontimestamp < (datetime().epochseconds - (inactive_days * 86400)) // Replicated value\nAND n.lastlogon < (datetime().epochseconds - (inactive_days * 86400)) // Non-replicated value\nAND n.whencreated < (datetime().epochseconds - (inactive_days * 86400)) // Exclude recently created principals\nAND NOT n.name STARTS WITH 'AZUREADKERBEROS.' // Removes false positive, Azure KRBTGT\nAND NOT n.objectid ENDS WITH '-500' // Removes false positive, built-in Administrator\nAND NOT n.name STARTS WITH 'AZUREADSSOACC.' // Removes false positive, Entra Seamless SSO\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Tier Zero computers with unsupported operating systems", - "guid": "a87b558c-5746-4a90-9f83-c86e7b924a52", - "prebuilt": false, - "platform": [ - "Active Directory" + "name": "Tier Zero / High Value external Entra ID users", + "guid": "20e07417-d286-4dca-a962-568f2b262f65", + "prebuilt": true, + "platforms": [ + "Azure" ], - "category": "Active Directory Hygiene", + "category": "Azure Hygiene", "description": null, - "query": "MATCH (c:Computer)\nWHERE c.operatingsystem =~ '(?i).*Windows.* (2000|2003|2008|2012|xp|vista|7|8|me|nt).*'\nAND ((c:Tag_Tier_Zero) OR COALESCE(c.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN c\nLIMIT 100", + "query": "MATCH (n:AZUser)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nAND n.name CONTAINS '#EXT#@'\nRETURN n\nLIMIT 100", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Map OU structure", - "guid": "8f14084b-5065-43d8-865a-a6ac52da25d1", - "prebuilt": true, - "platform": [ + "name": "Enabled users inactive for 180 days", + "guid": "71972f3c-b32d-4023-a841-5cc8cc1c1867", + "prebuilt": false, + "platforms": [ "Active Directory" ], - "category": "Domain Information", + "category": "Active Directory Hygiene", "description": null, - "query": "MATCH p = (:Domain)-[:Contains*1..]->(:OU)\nRETURN p\nLIMIT 1000", + "query": "WITH 180 as inactive_days\nMATCH (n:User)\nWHERE n.enabled = true\nAND n.lastlogontimestamp < (datetime().epochseconds - (inactive_days * 86400)) // Replicated value\nAND n.lastlogon < (datetime().epochseconds - (inactive_days * 86400)) // Non-replicated value\nAND n.whencreated < (datetime().epochseconds - (inactive_days * 86400)) // Exclude recently created principals\nAND NOT n.objectid ENDS WITH '-500' // Removes false positive, built-in Administrator\nRETURN n\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "ESC8-vulnerable Enterprise CAs", - "guid": "60881923-296c-4702-adf7-a4f059dc9bb8", - "prebuilt": true, - "platform": [ + "name": "Domains with a single-point-of-failure Domain Controller", + "guid": "3359a295-7cfd-491f-976b-c5a68647431c", + "prebuilt": false, + "platforms": [ "Active Directory" ], - "category": "NTLM Relay Attacks", + "category": "Active Directory Hygiene", "description": null, - "query": "MATCH (n:EnterpriseCA)\nWHERE n.hasvulnerableendpoint=true\nRETURN n", + "query": "MATCH (n:Group)<-[:MemberOf]-(:Computer)\nWHERE n.objectid ENDS WITH '-516'\nWITH n, COUNT(n) AS dcCount\nWHERE dcCount = 1\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "ACEs across trusts", - "guid": "c902d3b4-1a75-4335-acd7-28246dab746d", + "name": "Principal with SPN keyword", + "guid": "38a9c4c9-3d70-453f-a017-cbfd35ed9917", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Domain Information", - "description": "ACEs granted across a trust, the ACEs are set on trusting objects and the rights are granted to objects from trusted domains.", - "query": "MATCH p=(trustedDomainPrincipal:Base)-[r]->(trustingDomainPrincipal:Base)\nWHERE trustedDomainPrincipal.domainsid <> trustingDomainPrincipal.domainsid\nAND r.isacl\nRETURN p\nLIMIT 1000", + "category": "Kerberos Interaction", + "description": "Finds service accounts used with a specific Kerberos-enabled service or all service accounts running on a Kerberos-enabled service on a specific server.", + "query": "// Replace keyword with a service type or server name (not FQDN)\nWITH \"KEYWORD\" as SPNKeyword\nMATCH (n:User)\nWHERE ANY(keyword IN n.serviceprincipalnames WHERE toUpper(keyword) CONTAINS toUpper(SPNKeyword))\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://adsecurity.org/?page_id=183" + ], + "acknowledgements": [ + "Ryan, @haus3c" + ] }, { - "name": "Domain Controllers allowing NTLMv1 or LM authentication", - "guid": "4b42513c-f89d-47ff-8d98-908af49d2b48", + "name": "Domains allowing authenticated domain enumeration", + "guid": "1e1e6fdd-6973-4547-906c-a494b5fbdcba", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "NTLM Relay Attacks", + "category": "Active Directory Hygiene", "description": null, - "query": "MATCH (dc:Computer)\nWHERE dc.isdc = true\nAND (dc.lmcompatibilitylevel IS NOT NULL AND NOT dc.lmcompatibilitylevel = 5)\nRETURN dc", + "query": "MATCH p=(n:Group)-[:MemberOf]->(m:Group)\nWHERE n.objectid ENDS WITH \"S-1-5-11\" // Authenticated Users\nAND m.objectid ENDS WITH \"S-1-5-32-554\" // Pre-Windows 2000 Compatible Access\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Computers with the WebClient running", - "guid": "51107ad1-f0bc-43d3-a561-5cee471ca196", + "name": "Public Key Services container", + "guid": "07e94492-71aa-4665-ab8c-e7aec25906cd", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "NTLM Relay Attacks", + "category": "Active Directory Certificate Services", "description": null, - "query": "MATCH (c:Computer)\nWHERE c.webclientrunning = True\nRETURN c LIMIT 1000", + "query": "MATCH p = (c:Container)-[:Contains*..]->(:Base)\nWHERE c.distinguishedname starts with 'CN=PUBLIC KEY SERVICES,CN=SERVICES,CN=CONFIGURATION,DC='\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Workstations where Domain Users can RDP", - "guid": "9486e0e6-2617-4595-b969-cf57ca21fc86", + "name": "All service principals with Microsoft Graph App Role assignments", + "guid": "74440269-eb41-476b-8dec-b4095569b029", "prebuilt": true, - "platform": [ - "Active Directory" + "platforms": [ + "Azure" ], - "category": "Dangerous Privileges", + "category": "Microsoft Graph", "description": null, - "query": "MATCH p=(s:Group)-[:CanRDP]->(t:Computer)\nWHERE s.objectid ENDS WITH '-513' AND NOT toUpper(t.operatingsystem) CONTAINS 'SERVER'\nRETURN p\nLIMIT 1000", + "query": "MATCH p=(:AZServicePrincipal)-[:AZMGAppRoleAssignment_ReadWrite_All|AZMGApplication_ReadWrite_All|AZMGDirectory_ReadWrite_All|AZMGGroupMember_ReadWrite_All|AZMGGroup_ReadWrite_All|AZMGRoleManagement_ReadWrite_Directory|AZMGServicePrincipalEndpoint_ReadWrite_All]->(:AZServicePrincipal)\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "AdminSDHolder protected Accounts and Groups", - "guid": "5ee2f40e-a55c-4140-ab8a-91746ba3752b", + "name": "Direct Principal Rights Assignment", + "guid": "1d9c6ae3-38fc-4089-b5ad-fc3be0fa8eec", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Domain Information", - "description": "Objects whose permissions are set by SDProp to the template AdminSDHolder object as per MS-ADTS 3.1.1.6.1.2 Protected Objects. Does not exclude objects if specified in dSHeuristics dwAdminSDExMask", - "query": "MATCH (n:Base)-[:MemberOf*0..]->(m:Group)\nWHERE (\n n.objectid =~ \".*-(S-1-5-32-544|S-1-5-32-548|S-1-5-32-549|S-1-5-32-550|S-1-5-32-551|S-1-5-32-552|518|512|519)$\" // Groups\n OR m.objectid =~ \".*-(S-1-5-32-544|S-1-5-32-548|S-1-5-32-549|S-1-5-32-550|S-1-5-32-551|S-1-5-32-552|518|512|519)$\" // Members of groups\n OR n.objectid =~ \".*-(500|502|516|521)$\" // Direct objects\n)\nRETURN n", + "category": "Active Directory Hygiene", + "description": "This query identifies rights assigned directly to users or computers instead of groups. Active Directory best practice requires granting rights to groups, then adding users as group members. This role-based access control (RBAC) approach ensures permissions are easily auditable and manageable. Results include inherited rights, which must be modified at the parent container level.", + "query": "MATCH p=(n:Base)-[r:GenericAll|GenericWrite|WriteOwner|WriteDacl|ForceChangePassword|AllExtendedRights|AddMember|AllowedToDelegate|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13]->(:Base)\nWHERE (n:User OR n:Computer) \nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, "resources": [ - "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/a0d0b4fa-2895-4c64-b182-ba64ad0f84b8", - "https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/plan/security-best-practices/appendix-c--protected-accounts-and-groups-in-active-directory" + "https://softwareengineering.stackexchange.com/questions/11856/whats-wrong-with-circular-references" ], - "acknowledgement": null + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Tier Zero AD principals synchronized with Entra ID", - "guid": "a8b6ec67-21aa-4dd2-8906-47bb81bf5262", + "name": "Enrollment rights on certificate templates published to Enterprise CA with User Specified SAN enabled", + "guid": "96e70597-2d74-4503-a624-f1e30b642894", "prebuilt": true, - "platform": [ - "Azure" + "platforms": [ + "Active Directory" ], - "category": "Azure Hygiene", + "category": "Active Directory Certificate Services", "description": null, - "query": "MATCH (ENTRA:AZBase)\nMATCH (AD:Base)\nWHERE ((AD:Tag_Tier_Zero) OR COALESCE(AD.system_tags, '') CONTAINS 'admin_tier_0')\nAND ENTRA.onpremsyncenabled = true\nAND ENTRA.onpremid = AD.objectid\nRETURN ENTRA\n// Replace 'RETURN ENTRA' with 'RETURN AD' to see the corresponding AD principals\nLIMIT 100", + "query": "MATCH p = (:Base)-[:Enroll|GenericAll|AllExtendedRights]->(ct:CertTemplate)-[:PublishedTo]->(eca:EnterpriseCA)\nWHERE eca.isuserspecifiessanenabled = True\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Cross-forest trusts with abusable configuration", - "guid": "5cf1f354-80d4-420e-bc4b-424fabc21a56", + "name": "All Kerberoastable users", + "guid": "14ab4eaa-b73b-49c4-b2d1-1e020757c995", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Active Directory Hygiene", + "category": "Kerberos Interaction", "description": null, - "query": "MATCH p=(n:Domain)-[:CrossForestTrust|SpoofSIDHistory|AbuseTGTDelegation]-(m:Domain)\nWHERE (n)-[:SpoofSIDHistory|AbuseTGTDelegation]-(m)\nRETURN p", + "query": "MATCH (u:User)\nWHERE 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, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://attack.mitre.org/techniques/T1558/003/" + ], + "acknowledgements": [] }, { - "name": "DCs vulnerable to NTLM relay to LDAP attacks", - "guid": "3f87e0b0-fc06-4986-a94c-e08781253dc8", - "prebuilt": true, - "platform": [ + "name": "Domains allowing unauthenticated rootDSE searches and binds", + "guid": "ebc79aa4-e816-4be8-93fe-a0b30dbc771d", + "prebuilt": false, + "platforms": [ "Active Directory" ], - "category": "NTLM Relay Attacks", - "description": null, - "query": "MATCH p = (dc:Computer)-[:DCFor]->(:Domain)\nWHERE (dc.ldapavailable = True AND dc.ldapsigning = False)\nOR (dc.ldapsavailable = True AND dc.ldapsepa = False)\nOR (dc.ldapavailable = True AND dc.ldapsavailable = True AND dc.ldapsigning = False and dc.ldapsepa = True)\nRETURN p", + "category": "Active Directory Hygiene", + "description": "Checks the fLDAPBlockAnonOps flag of dSHeuristics.", + "query": "MATCH (n:Domain)\nWHERE n.dsheuristics =~ \".{6}[^2].*\"\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/e5899be4-862e-496f-9a38-33950617d2c5" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Tier Zero / High Value enabled users not requiring smart card authentication", - "guid": "867f9f17-c149-4c4b-ad84-9a807622ff8c", + "name": "All Domain Admins", + "guid": "0596dba7-9180-49a0-aa54-00243240037c", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Active Directory Hygiene", + "category": "Domain Information", "description": null, - "query": "MATCH (u:User)\nWHERE ((u:Tag_Tier_Zero) OR COALESCE(u.system_tags, '') CONTAINS 'admin_tier_0')\nAND u.enabled = true\nAND u.smartcardrequired = false\nAND NOT u.name STARTS WITH 'MSOL_' // Removes false positive, Entra sync\nAND NOT u.name STARTS WITH 'PROVAGENTGMSA' // Removes false positive, Entra sync\nAND NOT u.name STARTS WITH 'ADSYNCMSA_' // Removes false positive, Entra sync\nRETURN u", + "query": "MATCH p = (t:Group)<-[:MemberOf*1..]-(a)\nWHERE (a:User or a:Computer) and t.objectid ENDS WITH '-512'\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Entra Users synced from On-Prem Users added to Domain Admins group", - "guid": "62722d5f-bd93-4d11-beeb-9be261827e4e", - "prebuilt": true, - "platform": [ - "Active Directory", - "Azure" + "name": "Non-default delegation on MicrosoftDNS container", + "guid": "008792c0-4458-46a1-a10d-50cdaf95af1e", + "prebuilt": false, + "platforms": [ + "Active Directory" ], - "category": "Cross Platform Attack Paths", + "category": "Active Directory Hygiene", "description": null, - "query": "MATCH p = (:AZUser)-[:SyncedToADUser]->(:User)-[:MemberOf]->(t:Group)\nWHERE t.objectid ENDS WITH '-512'\nRETURN p\nLIMIT 1000", + "query": "MATCH p=(n:Base)-[r]->(m:Container)\nWHERE m.distinguishedname STARTS WITH \"CN=MICROSOFTDNS,CN=SYSTEM,DC=\"\nAND NOT n.name STARTS WITH \"DNSADMINS@\"\nAND NOT n.objectid =~ \"-(512|544|519|9)$\"\nAND r.isacl\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { "name": "Computers with unsupported operating systems", "guid": "d06d3b14-0318-4fa9-9639-4b79ccaf3c2c", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", "description": null, "query": "MATCH (c:Computer)\nWHERE c.operatingsystem =~ '(?i).*Windows.* (2000|2003|2008|2012|xp|vista|7|8|me|nt).*'\nRETURN c\nLIMIT 100", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null - }, - { - "name": "Non-Tier Zero account with 'Admin Count' flag", - "guid": "e7f703b3-5dba-4aef-8346-4d589be2c828", - "prebuilt": false, - "platform": [ - "Active Directory" - ], - "category": "Active Directory Hygiene", - "description": "Users who were a member of one of AD's built-in administrative groups but are not currently Tier Zero.", - "query": "MATCH (n:User)\nWHERE NOT ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nAND n.admincount = true\nRETURN n", - "revision": 1, - "note": null, - "resources": "https://learn.microsoft.com/en-us/windows/win32/adschema/a-admincount", - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Tier Zero computers with passwords older than the default maximum password age", - "guid": "b6d6d0bf-130e-4719-996b-adc29bba36e9", + "name": "Non-default members in Pre-Windows 2000 Compatible Access", + "guid": "091995b9-7254-473a-996f-6b8368d20431", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", "description": null, - "query": "MATCH (n:Computer)\nWHERE n.enabled = true\nAND n.whencreated < (datetime().epochseconds - (60 * 3 * 86400))\nAND n.pwdlastset < (datetime().epochseconds - (60 * 3 * 86400))\nAND coalesce(n.system_tags, \"\") CONTAINS \"admin_tier_0\"\nRETURN n", + "query": "MATCH p=(n:Group)-[:MemberOf]->(m:Group)\nWHERE NOT n.objectid ENDS WITH \"S-1-5-11\" // Authenticated Users\nAND NOT (n.objectid ENDS WITH \"S-1-5-7\" // Anonymous\nAND NOT n.objectid ENDS WITH \"S-1-1-0\") // Everyone\nAND m.objectid ENDS WITH \"S-1-5-32-554\" // Pre-Windows 2000 Compatible Access\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Computers with non-default Primary Group membership", - "guid": "5862dc4e-6f6f-4321-9474-d838968495ed", + "name": "Domains with List Object mode enabled", + "guid": "05e2a94b-5ee6-47ec-b715-3982f30af01b", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "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", + "category": "Domain Information", + "description": "Checks the fDoListObject flag of dSHeuristics.", + "query": "MATCH (n:Domain)\nWHERE n.dsheuristics =~ \".{2}[^0].*\"\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/e5899be4-862e-496f-9a38-33950617d2c5" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Principals with weak supported Kerberos encryption types", - "guid": "ca329573-2157-41da-ab17-4d122c54b11d", - "prebuilt": true, - "platform": [ + "name": "Accounts with clear-text password attributes", + "guid": "e303498f-e3d4-489d-8a34-b68e187bc4e7", + "prebuilt": false, + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", "description": null, - "query": "MATCH (u:Base)\nWHERE 'DES-CBC-CRC' IN u.supportedencryptiontypes\nOR 'DES-CBC-MD5' IN u.supportedencryptiontypes\nOR 'RC4-HMAC-MD5' IN u.supportedencryptiontypes\nRETURN u", + "query": "MATCH (n:Base)\nWHERE n.userpassword IS NOT NULL\nOR n.unixpassword IS NOT NULL\nOR n.unicodepwd IS NOT NULL\nOR n.msSFU30Password IS NOT NULL\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Tier Zero accounts that can be delegated", - "guid": "4316eaf1-6af0-4879-8f55-ac2633a711c3", + "name": "Collection health of CA Registry Data", + "guid": "c8dd3479-8063-450a-9456-557bc5f39e10", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Kerberos Interaction", - "description": null, - "query": "MATCH (m:Base)\nWHERE ((m:Tag_Tier_Zero) OR COALESCE(m.system_tags, '') CONTAINS 'admin_tier_0')\nAND m.enabled = true\nAND m.sensitive = false\nOPTIONAL MATCH (g:Group)<-[:MemberOf*1..]-(n:Base)\nWHERE g.objectid ENDS WITH '-525'\nWITH m, COLLECT(n) AS matchingNs\nWHERE NONE(n IN matchingNs WHERE n.objectid = m.objectid)\nRETURN m", + "category": "Domain Information", + "description": "BloodHound's ADCS analysis requires collecting CA registry data to increase accuracy/enable more edges. Collection by default requires SharpHound has Administrators membership. Requires SharpHound v2.3.5 or above. It only requires one misconfigured CA to potentially a full forest compromise by any principal. CAs returned by this query have not been collected.", + "query": "MATCH p=(eca:EnterpriseCA)<-[:HostsCAService]-(c:Computer)\nWHERE (\n eca.isuserspecifiessanenabledcollected = false\n OR eca.casecuritycollected = false\n OR eca.enrollmentagentrestrictionscollected = false\n OR eca.roleseparationenabledcollected = false\n)\n// Exclude inactive CAs\nAND c.enabled = true\nAND c.lastlogontimestamp > (datetime().epochseconds - (30 * 86400))\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://bloodhound.specterops.io/collect-data/enterprise-collection/permissions#ca-registry" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Smart card accounts with passwords not rotated in over 1 year", - "guid": "7e56f2e7-79c3-4f0d-aa3e-14cf3de7ab73", + "name": "Kerberos-enabled service account member of built-in Admins groups", + "guid": "42a856fc-257a-4142-9592-ca95fd49e579", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", "description": null, - "query": "MATCH (n:Base)\nWHERE n.pwdlastset < (datetime().epochseconds - (365 * 86400))\nAND n.enabled = true\nAND n.smartcardrequired = true\nRETURN n", + "query": "MATCH p=(n:Base)-[:MemberOf*1..]->(g:Group)\nWHERE (\n g.objectid ENDS WITH '-512' // Domain Admins\n OR g.objectid ENDS WITH '-519' // Enterprise Admins\n OR g.objectid ENDS WITH '-518' // Schema Admins\n)\nAND n.hasspn = true\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Domain controllers with UPN certificate mapping enabled", - "guid": "799ea3ce-572b-4594-98c4-041aa2ae6176", + "name": "PKI hierarchy", + "guid": "928acc23-ee4c-40a5-bde7-64c05cc1491d", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Certificate Services", "description": null, - "query": "MATCH p = (s:Computer)-[:DCFor]->(:Domain)\nWHERE s.certificatemappingmethodsraw IN [4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31]\nRETURN p\nLIMIT 1000", + "query": "MATCH p=()-[:HostsCAService|IssuedSignedBy|EnterpriseCAFor|RootCAFor|TrustedForNTAuth|NTAuthStoreFor*..]->(:Domain)\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": [ - "https://support.microsoft.com/en-us/topic/kb5014754-certificate-based-authentication-changes-on-windows-domain-controllers-ad2c23b0-15d8-4340-a468-4d4f3b188f16", - "https://specterops.io/blog/2024/02/28/adcs-esc14-abuse-technique/" - ], - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Users with logon scripts stored in a trusted domain", - "guid": "8d94d3f3-3d53-4939-a206-3c0a4dd3f646", + "name": "Foreign Service Principals With any Abusable MS Graph App Role Assignment", + "guid": "d7a180c8-5624-4fc1-a407-deeb2ad3054c", "prebuilt": false, - "platform": [ - "Active Directory" + "platforms": [ + "Azure" ], - "category": "Active Directory Hygiene", - "description": null, - "query": "MATCH (n:User)\nWHERE n.logonscript IS NOT NULL\nMATCH (d:Domain)-[:TrustedBy]->(:Domain)-[:Contains*1..]->(n)\nWITH n,last(split(d.name, '@')) AS domain\nWHERE toUpper(n.logonscript) STARTS WITH (\"\\\\\\\\\" + domain + \"\\\\\")\nRETURN n", + "category": "Dangerous Privileges", + "description": "MS Graph app role assignments provide significant power within an Entra ID tenant, similar to an Admin role.", + "query": "MATCH p = (sp1:AZServicePrincipal)-[r:AZMGGroupMember_ReadWrite_All|AZMGServicePrincipalEndpoint_ReadWrite_All|AZMGAppRoleAssignment_ReadWrite_All|AZMGGroup_ReadWrite_All|AZMGDirectory_ReadWrite_All|AZMGRoleManagement_ReadWrite_Directory]->(sp2:AZServicePrincipal)\nWHERE toUpper(sp1.appownerorganizationid) <> toUpper(sp1.tenantid)\n// Ensure AZServicePrincipal has a valid appownerorganizationid\nAND sp1.appownerorganizationid CONTAINS \"-\"\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://posts.specterops.io/microsoft-breach-how-can-i-see-this-in-bloodhound-33c92dca4c65" + ], + "acknowledgements": [ + "Stephen Hinck" + ] }, { - "name": "Enrollment rights on published certificate templates", - "guid": "a4ae2e54-aad3-4bfd-a12d-90cb8a9cbc86", + "name": "Computers with membership in Protected Users", + "guid": "a26372f4-2e92-49f6-8993-6657fbc1569a", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Active Directory Certificate Services", + "category": "NTLM Relay Attacks", "description": null, - "query": "MATCH p = (:Base)-[:Enroll|GenericAll|AllExtendedRights]->(:CertTemplate)-[:PublishedTo]->(:EnterpriseCA)\nRETURN p\nLIMIT 1000", + "query": "MATCH p = (:Base)-[:MemberOf*1..]->(g:Group)\nWHERE g.objectid ENDS WITH \"-525\"\nRETURN p LIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Computers where Domain Users are local administrators", - "guid": "d43a7bdc-33c6-4a39-a3bb-24115749e595", + "name": "Kerberoastable members of Tier Zero / High Value groups", + "guid": "e6da7800-ae06-41cb-80a6-d5421ab2143a", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Dangerous Privileges", + "category": "Kerberos Interaction", "description": null, - "query": "MATCH p=(s:Group)-[:AdminTo]->(:Computer)\nWHERE s.objectid ENDS WITH '-513'\nRETURN p\nLIMIT 1000", + "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, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://attack.mitre.org/techniques/T1558/003/" + ], + "acknowledgements": [] }, { - "name": "All members of high privileged roles", - "guid": "3df24d92-dd12-4125-811b-e696b098f60e", + "name": "Computers not requiring inbound SMB signing", + "guid": "6b1fcfb6-b010-41a2-9d31-f9872fe994ff", "prebuilt": true, - "platform": [ - "Azure" + "platforms": [ + "Active Directory" ], - "category": "General", + "category": "NTLM Relay Attacks", "description": null, - "query": "MATCH p=(t:AZRole)<-[:AZHasRole|AZMemberOf*1..2]-(:AZBase)\nWHERE t.name =~ '(?i)Global Administrator|User Administrator|Cloud Application Administrator|Authentication Policy Administrator|Exchange Administrator|Helpdesk Administrator|Privileged Authentication Administrator'\nRETURN p\nLIMIT 1000", + "query": "MATCH (n:Computer)\nWHERE n.smbsigning = False\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Tier Zero users with passwords not rotated in over 1 year", - "guid": "5e0d69b1-37d1-43ae-ac5d-f297f312fab5", + "name": "Unresolved SID with outbound control", + "guid": "4e8429f9-cba2-41e9-bac6-0c42f96b2c57", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", "description": null, - "query": "WITH 365 as days_since_change\nMATCH (u:User)\nWHERE ((u:Tag_Tier_Zero) OR COALESCE(u.system_tags, '') CONTAINS 'admin_tier_0')\nAND u.pwdlastset < (datetime().epochseconds - (days_since_change * 86400))\nAND NOT u.pwdlastset IN [-1.0, 0.0]\nRETURN u\nLIMIT 100", + "query": "MATCH p=(n:Base)-[r]->(:Base)\nWHERE r.isacl\nAND n.name CONTAINS \"S-1-5-21-\" // Unresolved SID\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Non-default members in Pre-Windows 2000 Compatible Access", - "guid": "091995b9-7254-473a-996f-6b8368d20431", - "prebuilt": false, - "platform": [ + "name": "Principals with passwords stored using reversible encryption", + "guid": "ab900835-b2b8-4674-87b4-8b5141e80439", + "prebuilt": true, + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", "description": null, - "query": "MATCH p=(n:Group)-[:MemberOf]->(m:Group)\nWHERE NOT n.objectid ENDS WITH \"S-1-5-11\" // Authenticated Users\nAND NOT (n.objectid ENDS WITH \"S-1-5-7\" // Anonymous\nAND NOT n.objectid ENDS WITH \"S-1-1-0\") // Everyone\nAND m.objectid ENDS WITH \"S-1-5-32-554\" // Pre-Windows 2000 Compatible Access\nRETURN p", + "query": "MATCH (n:Base)\nWHERE n.encryptedtextpwdallowed = true\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Principals with foreign domain group membership", - "guid": "8fb3214a-5a75-4ecd-b293-c121abd94b4b", - "prebuilt": true, - "platform": [ + "name": "Domains without Protected Users group", + "guid": "8c3e0811-a31b-45b4-a29d-1dce80fa2c5f", + "prebuilt": false, + "platforms": [ "Active Directory" ], - "category": "Dangerous Privileges", + "category": "Domain Information", "description": null, - "query": "MATCH p=(s:Base)-[:MemberOf]->(t:Group)\nWHERE s.domainsid<>t.domainsid\nRETURN p\nLIMIT 1000", + "query": "MATCH (n:Domain)\nWHERE n.collected = true\nOPTIONAL MATCH (m:Group)\nWHERE m.name ENDS WITH n.name\nAND m.objectid ENDS WITH '-525'\nWITH n, m\nWHERE m IS NULL\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Disabled Tier Zero / High Value principals", - "guid": "860d5c2d-84fe-4c85-80de-e0a9badbd0e7", + "name": "Tier Zero AD principals synchronized with Entra ID", + "guid": "a8b6ec67-21aa-4dd2-8906-47bb81bf5262", "prebuilt": true, - "platform": [ + "platforms": [ "Azure" ], "category": "Azure Hygiene", "description": null, - "query": "MATCH (n:AZBase)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nAND n.enabled = false\nRETURN n\nLIMIT 100", - "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null - }, - { - "name": "Map Azure Management structure", - "guid": "c1bb109e-e6a4-4c91-864f-f78e1e42615e", - "prebuilt": false, - "platform": [ - "Azure" - ], - "category": "Kerberos Interaction", - "description": "Maps the structure of Azure Management", - "query": "MATCH p = (:AZTenant)-[:AZContains*1..]->(:AZResourceGroup)\nRETURN p\nLIMIT 1000", + "query": "MATCH (ENTRA:AZBase)\nMATCH (AD:Base)\nWHERE ((AD:Tag_Tier_Zero) OR COALESCE(AD.system_tags, '') CONTAINS 'admin_tier_0')\nAND ENTRA.onpremsyncenabled = true\nAND ENTRA.onpremid = AD.objectid\nRETURN ENTRA\n// Replace 'RETURN ENTRA' with 'RETURN AD' to see the corresponding AD principals\nLIMIT 100", "revision": 1, - "note": null, - "resources": "https://learn.microsoft.com/en-us/azure/governance/management-groups/overview", - "acknowledgement": "Martin Sohn Christensen, @martinsohndk" + "resources": [], + "acknowledgements": [] }, { - "name": "Kerberos-enabled service account member of built-in Admins groups", - "guid": "42a856fc-257a-4142-9592-ca95fd49e579", - "prebuilt": false, - "platform": [ + "name": "Enrollment rights on published certificate templates", + "guid": "a4ae2e54-aad3-4bfd-a12d-90cb8a9cbc86", + "prebuilt": true, + "platforms": [ "Active Directory" ], - "category": "Active Directory Hygiene", + "category": "Active Directory Certificate Services", "description": null, - "query": "MATCH p=(n:Base)-[:MemberOf*1..]->(g:Group)\nWHERE (\n g.objectid ENDS WITH '-512' // Domain Admins\n OR g.objectid ENDS WITH '-519' // Enterprise Admins\n OR g.objectid ENDS WITH '-518' // Schema Admins\n)\nAND n.hasspn = true\nRETURN p", + "query": "MATCH p = (:Base)-[:Enroll|GenericAll|AllExtendedRights]->(:CertTemplate)-[:PublishedTo]->(:EnterpriseCA)\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Entra ID SSO accounts not rolling Kerberos decryption key", - "guid": "1867abf8-08e3-4ea8-8f65-8366079d35c4", + "name": "Collection health of DC Registry Data", + "guid": "3f0fa2f3-fbdf-42c0-9e7d-97e689009161", "prebuilt": false, - "platform": [ - "Active Directory", - "Azure" + "platforms": [ + "Active Directory" ], - "category": "Configuration Weakness", - "description": "Microsoft highly recommends that you roll over the Entra ID SSO Kerberos decryption key at least every 30 days.", - "query": "MATCH (n:Computer)\nWHERE n.name STARTS WITH \"AZUREADSSOACC.\"\nAND n.pwdlastset < (datetime().epochseconds - (30 * 86400))\nRETURN n", + "category": "Domain Information", + "description": "BloodHound's ADCS analysis requires collecting CA registry data to increase accuracy/enable more edges. Collection by default requires SharpHound has Administrators membership. Requires SharpHound v2.3.5 or above. It only requires one misconfigured DC to potentially a full forest compromise by any principal. DCs returned by this query have not been collected.", + "query": "MATCH p=(:Domain)<-[:DCFor]-(c:Computer)\nWHERE c.strongcertificatebindingenforcementraw IS NULL\n// Exclude inactive DCs\nAND c.enabled = true\nAND c.lastlogontimestamp > (datetime().epochseconds - (30 * 86400))\nRETURN p", "revision": 1, - "note": null, - "resources": "https://learn.microsoft.com/en-us/entra/identity/hybrid/connect/how-to-connect-sso-faq#how-can-i-roll-over-the-kerberos-decryption-key-of-the--azureadsso--computer-account-", - "acknowledgement": null + "resources": [ + "https://bloodhound.specterops.io/collect-data/enterprise-collection/permissions#dc-registry" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Domain Admins logons to non-Domain Controllers", - "guid": "e2f3fd0a-1df2-4089-b0a4-272ad6e369a9", + "name": "Kerberoastable users with most admin privileges", + "guid": "9907b208-494c-4ba6-846d-485e6de14e17", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Dangerous Privileges", + "category": "Kerberos Interaction", "description": null, - "query": "MATCH (s)-[:MemberOf*0..]->(g:Group)\nWHERE g.objectid ENDS WITH '-516'\nWITH COLLECT(s) AS exclude\nMATCH p = (c:Computer)-[:HasSession]->(:User)-[:MemberOf*1..]->(g:Group)\nWHERE g.objectid ENDS WITH '-512' AND NOT c IN exclude\nRETURN p\nLIMIT 1000", + "query": "MATCH (u:User)\nWHERE u.hasspn = true\n AND u.enabled = true\n AND NOT u.objectid ENDS WITH '-502'\n AND NOT COALESCE(u.gmsa, false) = true\n AND NOT COALESCE(u.msa, false) = true\nMATCH (u)-[:MemberOf|AdminTo*1..]->(c:Computer)\nWITH DISTINCT u, COUNT(c) AS adminCount\nRETURN u\nORDER BY adminCount DESC\nLIMIT 100", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://attack.mitre.org/techniques/T1558/003/" + ], + "acknowledgements": [] }, { - "name": "Map domain trusts", - "guid": "268d3d26-5bc2-4820-a6ed-09d20f3d5413", + "name": "Enrollment rights on published ESC2 certificate templates", + "guid": "ebc77984-1ceb-4ed2-a395-ce1067847941", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Domain Information", + "category": "Active Directory Certificate Services", "description": null, - "query": "MATCH p = (:Domain)-[:SameForestTrust|CrossForestTrust]->(:Domain)\nRETURN p\nLIMIT 1000", + "query": "MATCH p = (:Base)-[:Enroll|GenericAll|AllExtendedRights]->(c:CertTemplate)-[:PublishedTo]->(:EnterpriseCA)\nWHERE c.requiresmanagerapproval = false\nAND (c.effectiveekus = [''] OR '2.5.29.37.0' IN c.effectiveekus)\nAND (c.authorizedsignatures = 0 OR c.schemaversion = 1)\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Tier Zero users with email", - "guid": "9654c0d4-f1e8-4393-a2d1-53a5554a9de8", + "name": "AS-REP Roastable Tier Zero users (DontReqPreAuth)", + "guid": "6d51e4dc-e1ad-477a-b6c6-324f18f03120", "prebuilt": false, - "platform": [ - "Active Directory" - ], - "category": "Active Directory Hygiene", - "description": "Tier Zero accounts with email access have an increased attack surface.", - "query": "MATCH (n)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nAND n.email <> \"\"\nAND n.enabled = true\nAND NOT toUpper(n.email) ENDS WITH \".ONMICROSOFT.COM\"\nAND NOT (\n (toUpper(n.email) STARTS WITH \"HEALTHMAILBOX\"\n OR toUpper(n.email) STARTS WITH \"MSEXCHDISCOVERYMAILBOX\"\n OR toUpper(n.email) STARTS WITH \"MSEXCHDISCOVERY\"\n OR toUpper(n.email) STARTS WITH \"MSEXCHAPPROVAL\"\n OR toUpper(n.email) STARTS WITH \"FEDERATEDEMAIL\"\n OR toUpper(n.email) STARTS WITH \"SYSTEMMAILBOX\"\n OR toUpper(n.email) STARTS WITH \"MIGRATION.\")\n AND\n (n.name STARTS WITH \"SM_\"\n OR n.name STARTS WITH \"HEALTHMAILBOX\")\n)\nRETURN n", - "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null - }, - { - "name": "Domains with smart card accounts where smart account passwords do not expire", - "guid": "97e05e67-5961-4aba-a8e7-fe5f92334035", - "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", "description": null, - "query": "MATCH (s:Domain)-[:Contains*1..]->(t:Base)\nWHERE s.expirepasswordsonsmartcardonlyaccounts = false\nAND t.enabled = true\nAND t.smartcardrequired = true\nRETURN s", + "query": "MATCH (n:Base)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nAND n.dontreqpreauth = true\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://attack.mitre.org/techniques/T1558/004/" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Domains with more than 50 Tier Zero accounts", - "guid": "f046e95a-5f84-4e83-bcda-6e83f3d8e21a", + "name": "Tier Zero omputers not requiring inbound SMB signing", + "guid": "13485477-f026-4b1f-906d-4f2e37364ba4", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Active Directory Hygiene", + "category": "NTLM Relay Attacks", "description": null, - "query": "MATCH (d:Domain)-[:Contains*1..]->(n:Base)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nWITH d, COUNT(n) AS adminCount\nWHERE adminCount > 50\nRETURN d", + "query": "MATCH (n:Computer)\nWHERE n.smbsigning = False\nAND ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Principals with DCSync privileges", - "guid": "6e9beb8a-ad14-43de-bda1-644d174a5906", + "name": "All members of high privileged roles", + "guid": "3df24d92-dd12-4125-811b-e696b098f60e", "prebuilt": true, - "platform": [ - "Active Directory" + "platforms": [ + "Azure" ], - "category": "Dangerous Privileges", + "category": "General", "description": null, - "query": "MATCH p=(:Base)-[:DCSync|AllExtendedRights|GenericAll]->(:Domain)\nRETURN p\nLIMIT 1000", + "query": "MATCH p=(t:AZRole)<-[:AZHasRole|AZMemberOf*1..2]-(:AZBase)\nWHERE t.name =~ '(?i)Global Administrator|User Administrator|Cloud Application Administrator|Authentication Policy Administrator|Exchange Administrator|Helpdesk Administrator|Privileged Authentication Administrator'\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Enabled computers inactive for 180 days - MSSQL Failover Cluster", - "guid": "d263e621-7f1b-4efb-ad25-098fc7d4fb72", - "prebuilt": false, - "platform": [ - "Active Directory" + "name": "Disabled Tier Zero / High Value principals", + "guid": "860d5c2d-84fe-4c85-80de-e0a9badbd0e7", + "prebuilt": true, + "platforms": [ + "Azure" ], - "category": "Active Directory Hygiene", + "category": "Azure Hygiene", "description": null, - "query": "WITH 180 as inactive_days\nMATCH (n:Computer)\nWHERE n.enabled = true\nAND n.lastlogontimestamp < (datetime().epochseconds - (inactive_days * 86400)) // Replicated value\nAND n.lastlogon < (datetime().epochseconds - (inactive_days * 86400)) // Non-replicated value\nAND n.whencreated < (datetime().epochseconds - (inactive_days * 86400)) // Exclude recently created principals\nAND ANY(type IN n.serviceprincipalnames WHERE \n toLower(type) CONTAINS 'mssqlservercluster' OR \n toLower(type) CONTAINS 'mssqlserverclustermgmtapi' OR \n toLower(type) CONTAINS 'msclustervirtualserver')\nRETURN n\nLIMIT 1000", + "query": "MATCH (n:AZBase)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nAND n.enabled = false\nRETURN n\nLIMIT 100", "revision": 1, - "note": null, - "resources": "https://learn.microsoft.com/en-us/troubleshoot/windows-server/high-availability/troubleshoot-issues-accounts-used-failover-clusters#troubleshoot-password-issues-with-the-cluster-name-account", - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Enrollment rights on published certificate templates with no security extension", - "guid": "0677b70c-4e04-4e89-a6a2-f5764604a6a7", + "name": "AS-REP Roastable users (DontReqPreAuth)", + "guid": "2570e359-dec1-419d-b0dc-a204bd64ee42", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Active Directory Certificate Services", + "category": "Kerberos Interaction", "description": null, - "query": "MATCH p = (:Base)-[:Enroll|GenericAll|AllExtendedRights]->(ct:CertTemplate)-[:PublishedTo]->(:EnterpriseCA)\nWHERE ct.nosecurityextension = true\nRETURN p\nLIMIT 1000", + "query": "MATCH (u:User)\nWHERE u.dontreqpreauth = true\nAND u.enabled = true\nRETURN u\nLIMIT 100", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://attack.mitre.org/techniques/T1558/004/" + ], + "acknowledgements": [] }, { - "name": "Computers where Domain Users can read LAPS passwords", - "guid": "aa4bfa95-e7b9-4d56-8f35-f34f04d7b6f4", + "name": "Potential GPO 'Apply' misconfiguration", + "guid": "f5f2455e-afdc-4708-9a34-98f539ce52d8", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Dangerous Privileges", + "description": "In Active Directory, GPO's are applied to objects in the Group Policy Management Console by ticking \"Allow - Apply group policy\", but administrators can mistakenly tick \"Allow - Write\" or \"Allow - Full Control\" resulting in a misconfigured GPO that allows a principal to compromise other principals the GPO also applies to. Results are potential risks and must be audited for for correctness.", + "query": "MATCH p=(n:Base)-[:GenericAll|GenericWrite]->(g:GPO)\n\n// Exclude Enterprise Admins and Domain Admins\nWHERE NOT n.objectid =~ \"-(519|512)$\"\n\n// Exclude unresolved SIDs\nAND NOT (n.distinguishedname IS NULL)\n\n// Asset description may reveal if it's a delegation group (false-positive) or a filter group (true-positive)\n//AND n.description is not null\n//AND n.description =~ \"(?i)apply\"\n\nRETURN p\nLIMIT 1000", + "revision": 2, + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] + }, + { + "name": "Enrollment rights on published ESC1 certificate templates", + "guid": "2af855bc-f48f-4b22-9839-627d8231e425", + "prebuilt": true, + "platforms": [ + "Active Directory" + ], + "category": "Active Directory Certificate Services", "description": null, - "query": "MATCH p=(s:Group)-[:AllExtendedRights|ReadLAPSPassword]->(:Computer)\nWHERE s.objectid ENDS WITH '-513'\nRETURN p\nLIMIT 1000", + "query": "MATCH p = (:Base)-[:Enroll|GenericAll|AllExtendedRights]->(ct:CertTemplate)-[:PublishedTo]->(:EnterpriseCA)\nWHERE ct.enrolleesuppliessubject = True\nAND ct.authenticationenabled = True\nAND ct.requiresmanagerapproval = False\nAND (ct.authorizedsignatures = 0 OR ct.schemaversion = 1)\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Domains with smart card accounts where smart account passwords do not expire", - "guid": "97e05e67-5961-4aba-a8e7-fe5f92334035", + "name": "Entra ID SSO accounts not rolling Kerberos decryption key", + "guid": "1867abf8-08e3-4ea8-8f65-8366079d35c4", "prebuilt": false, - "platform": [ - "Active Directory" + "platforms": [ + "Active Directory", + "Azure" ], - "category": "Active Directory Hygiene", - "description": null, - "query": "MATCH (s:Domain)-[:Contains*1..]->(t:Base)\nWHERE s.expirepasswordsonsmartcardonlyaccounts = false\nAND t.enabled = true\nAND t.smartcardrequired = true\nRETURN s", + "category": "Configuration Weakness", + "description": "Microsoft highly recommends that you roll over the Entra ID SSO Kerberos decryption key at least every 30 days.", + "query": "MATCH (n:Computer)\nWHERE n.name STARTS WITH \"AZUREADSSOACC.\"\nAND n.pwdlastset < (datetime().epochseconds - (30 * 86400))\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://learn.microsoft.com/en-us/entra/identity/hybrid/connect/how-to-connect-sso-faq#how-can-i-roll-over-the-kerberos-decryption-key-of-the--azureadsso--computer-account-" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "All Domain Admins", - "guid": "0596dba7-9180-49a0-aa54-00243240037c", + "name": "DCs vulnerable to NTLM relay to LDAP attacks", + "guid": "3f87e0b0-fc06-4986-a94c-e08781253dc8", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Domain Information", + "category": "NTLM Relay Attacks", "description": null, - "query": "MATCH p = (t:Group)<-[:MemberOf*1..]-(a)\nWHERE (a:User or a:Computer) and t.objectid ENDS WITH '-512'\nRETURN p\nLIMIT 1000", + "query": "MATCH p = (dc:Computer)-[:DCFor]->(:Domain)\nWHERE (dc.ldapavailable = True AND dc.ldapsigning = False)\nOR (dc.ldapsavailable = True AND dc.ldapsepa = False)\nOR (dc.ldapavailable = True AND dc.ldapsavailable = True AND dc.ldapsigning = False and dc.ldapsepa = True)\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Domains allowing unauthenticated domain enumeration", - "guid": "41a08d76-f8a5-4296-ad19-464c4c5c69fe", + "name": "Domains with functional level not the latest version", + "guid": "3da9d14a-f1cb-4df7-b3da-8d73ff5c401b", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", "description": null, - "query": "MATCH p=(n:Group)-[:MemberOf]->(m:Group)\nWHERE (n.objectid ENDS WITH \"S-1-5-7\" // Anonymous\nOR n.objectid ENDS WITH \"S-1-1-0\") // Everyone\nAND m.objectid ENDS WITH \"S-1-5-32-554\" // Pre-Windows 2000 Compatible Access\nRETURN p", + "query": "MATCH (n:Domain)\nWHERE toString(n.functionallevel) IN ['2008','2003','2003 Interim','2000 Mixed/Native']\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "All coerce and NTLM relay edges", - "guid": "15c5ff3b-856c-44d1-a731-a8cb72512dd1", + "name": "Tier Zero computers at risk of resource-based constrained delegation", + "guid": "4dc97cf4-3c03-4fe6-8a8b-4f665c67e1e5", + "prebuilt": false, + "platforms": [ + "Active Directory" + ], + "category": "Dangerous Privileges", + "description": null, + "query": "MATCH p = (n:Computer)<-[:AllowedToAct]-(:Base)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p", + "revision": 1, + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] + }, + { + "name": "Tier Zero / High Value users with non-expiring passwords", + "guid": "4eca1b69-00a2-48a0-abb3-b94ea647cf6b", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "NTLM Relay Attacks", + "category": "Active Directory Hygiene", "description": null, - "query": "MATCH p = (n:Base)-[:CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|CoerceAndRelayNTLMToADCS|CoerceAndRelayNTLMToSMB]->(:Base)\nRETURN p LIMIT 500", + "query": "MATCH (u:User)\nWHERE ((u:Tag_Tier_Zero) OR COALESCE(u.system_tags, '') CONTAINS 'admin_tier_0') AND u.enabled = true\nAND u.pwdneverexpires = true\nRETURN u\nLIMIT 100", "revision": 1, - "note": null, - "resources": "https://specterops.io/blog/2025/04/08/the-renaissance-of-ntlm-relay-attacks-everything-you-need-to-know/", - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Accounts with clear-text password attributes", - "guid": "e303498f-e3d4-489d-8a34-b68e187bc4e7", + "name": "Tier Zero users with passwords not rotated in over 1 year", + "guid": "5e0d69b1-37d1-43ae-ac5d-f297f312fab5", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", "description": null, - "query": "MATCH (n:Base)\nWHERE n.userpassword IS NOT NULL\nOR n.unixpassword IS NOT NULL\nOR n.unicodepwd IS NOT NULL\nOR n.msSFU30Password IS NOT NULL\nRETURN n", + "query": "WITH 365 as days_since_change\nMATCH (u:User)\nWHERE ((u:Tag_Tier_Zero) OR COALESCE(u.system_tags, '') CONTAINS 'admin_tier_0')\nAND u.pwdlastset < (datetime().epochseconds - (days_since_change * 86400))\nAND NOT u.pwdlastset IN [-1.0, 0.0]\nRETURN u\nLIMIT 100", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "CA administrators and CA managers", - "guid": "fd35e3d8-0c74-4b5a-a847-c0dd1f1c9f19", + "name": "Workstations where Domain Users can RDP", + "guid": "9486e0e6-2617-4595-b969-cf57ca21fc86", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Active Directory Certificate Services", + "category": "Dangerous Privileges", "description": null, - "query": "MATCH p = (:Base)-[:ManageCertificates|ManageCA]->(:EnterpriseCA)\nRETURN p\nLIMIT 1000", + "query": "MATCH p=(s:Group)-[:CanRDP]->(t:Computer)\nWHERE s.objectid ENDS WITH '-513' AND NOT toUpper(t.operatingsystem) CONTAINS 'SERVER'\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Sessions across trusts", - "guid": "aea7ac64-1f51-407b-b0ee-19fd30075794", - "prebuilt": false, - "platform": [ + "name": "Domain Admins logons to non-Domain Controllers", + "guid": "e2f3fd0a-1df2-4089-b0a4-272ad6e369a9", + "prebuilt": true, + "platforms": [ "Active Directory" ], - "category": "Domain Information", - "description": "Users logging on across a trust, the users originate from trusted domains.", - "query": "MATCH p=(trustedDomainPrincipal:Computer)-[r:HasSession]->(trustingDomainPrincipal:User)\nWHERE trustedDomainPrincipal.domainsid <> trustingDomainPrincipal.domainsid\nRETURN p\nLIMIT 1000", + "category": "Dangerous Privileges", + "description": null, + "query": "MATCH (s)-[:MemberOf*0..]->(g:Group)\nWHERE g.objectid ENDS WITH '-516'\nWITH COLLECT(s) AS exclude\nMATCH p = (c:Computer)-[:HasSession]->(:User)-[:MemberOf*1..]->(g:Group)\nWHERE g.objectid ENDS WITH '-512' AND NOT c IN exclude\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Enrollment rights on published ESC1 certificate templates", - "guid": "2af855bc-f48f-4b22-9839-627d8231e425", - "prebuilt": true, - "platform": [ + "name": "All ADCS ESC privilege escalation edges", + "guid": "49db8edc-8421-438f-b97b-23c042959bef", + "prebuilt": false, + "platforms": [ "Active Directory" ], "category": "Active Directory Certificate Services", "description": null, - "query": "MATCH p = (:Base)-[:Enroll|GenericAll|AllExtendedRights]->(ct:CertTemplate)-[:PublishedTo]->(:EnterpriseCA)\nWHERE ct.enrolleesuppliessubject = True\nAND ct.authenticationenabled = True\nAND ct.requiresmanagerapproval = False\nAND (ct.authorizedsignatures = 0 OR ct.schemaversion = 1)\nRETURN p\nLIMIT 1000", + "query": "MATCH p=(:Base)-[:ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|GoldenCert|CoerceAndRelayNTLMToADCS]->(:Base)\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://posts.specterops.io/certified-pre-owned-d95910965cd2", + "https://posts.specterops.io/adcs-attack-paths-in-bloodhound-part-1-799f3d3b03cf", + "https://posts.specterops.io/adcs-attack-paths-in-bloodhound-part-2-ac7f925d1547", + "https://posts.specterops.io/adcs-attack-paths-in-bloodhound-part-3-33efb00856ac", + "https://posts.specterops.io/adcs-esc13-abuse-technique-fda4272fbd53", + "https://specterops.io/blog/2025/04/08/the-renaissance-of-ntlm-relay-attacks-everything-you-need-to-know/#:~:text=Introducing%20the%20CoerceAndRelayNTLMToADCS%20Edge" + ], + "acknowledgements": [ + "Jonas B\u00fclow Knudsen, @Jonas_B_K" + ] }, { - "name": "Foreign principals in Tier Zero / High Value targets", - "guid": "95bec736-86ef-4017-8465-9b9b66548b17", + "name": "On-Prem Users synced to Entra Users with Entra Admin Roles (direct)", + "guid": "de717635-d31f-4fbd-930b-b4dac0f22118", "prebuilt": true, - "platform": [ + "platforms": [ + "Active Directory", "Azure" ], - "category": "Azure Hygiene", + "category": "Cross Platform Attack Paths", "description": null, - "query": "MATCH (n:AZServicePrincipal)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nAND NOT toUpper(n.appownerorganizationid) = toUpper(n.tenantid)\nAND n.appownerorganizationid CONTAINS '-'\nRETURN n\nLIMIT 100", + "query": "MATCH p = (:User)-[:SyncedToEntraUser]->(:AZUser)-[:AZHasRole]->(:AZRole)\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Non-Tier Zero accounts with SID History of Tier Zero accounts", - "guid": "59744dfe-9411-4daf-b342-1203dc62acd4", + "name": "Map Azure Management structure", + "guid": "c1bb109e-e6a4-4c91-864f-f78e1e42615e", "prebuilt": false, - "platform": [ - "Active Directory" + "platforms": [ + "Azure" ], - "category": "Dangerous Privileges", - "description": null, - "query": "MATCH p=(n:Base)-[:HasSIDHistory]->(m:Base)\nWHERE ((m:Tag_Tier_Zero) OR COALESCE(m.system_tags, '') CONTAINS 'admin_tier_0')\nAND NOT ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p", + "category": "Kerberos Interaction", + "description": "Maps the structure of Azure Management", + "query": "MATCH p = (:AZTenant)-[:AZContains*1..]->(:AZResourceGroup)\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://learn.microsoft.com/en-us/azure/governance/management-groups/overview" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Tier Zero computers at risk of resource-based constrained delegation", - "guid": "4dc97cf4-3c03-4fe6-8a8b-4f665c67e1e5", + "name": "Computers with passwords older than the default maximum password age", + "guid": "185c5010-8d4f-4f9b-b24e-831707dddfca", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Dangerous Privileges", - "description": null, - "query": "MATCH p = (n:Computer)<-[:AllowedToAct]-(:Base)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p", + "category": "Active Directory Hygiene", + "description": "Machine account passwords are regularly changed for security purposes. Starting with Windows 2000-based computers, the machine account password automatically changes every 30 days.", + "query": "WITH 60 as rotation_period\nMATCH (n:Computer)\nWHERE n.pwdlastset < (datetime().epochseconds - (rotation_period * 86400)) // password not rotated\nAND n.enabled = true // enabled computers\nAND n.whencreated < (datetime().epochseconds - (rotation_period * 86400)) // exclude recently created computers\nAND n.lastlogontimestamp > (datetime().epochseconds - (rotation_period * 86400)) // active computers (Replicated value)\nAND n.lastlogon > (datetime().epochseconds - (rotation_period * 86400)) // active computers (Non-replicated value)\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://learn.microsoft.com/en-us/troubleshoot/windows-server/windows-security/disable-machine-account-password" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Domains with a minimum default password policy length less than 15 characters", - "guid": "7d258d2d-a43d-4a90-85d7-71c946ae5fd7", - "prebuilt": false, - "platform": [ + "name": "CA administrators and CA managers", + "guid": "fd35e3d8-0c74-4b5a-a847-c0dd1f1c9f19", + "prebuilt": true, + "platforms": [ "Active Directory" ], - "category": "Active Directory Hygiene", + "category": "Active Directory Certificate Services", "description": null, - "query": "MATCH (n:Domain)\nWHERE n.minpwdlength < 15\nRETURN n", + "query": "MATCH p = (:Base)-[:ManageCertificates|ManageCA]->(:EnterpriseCA)\nRETURN p\nLIMIT 1000", "revision": 1, - "note": "NIST recommends 15 characters.", - "resources": "https://pages.nist.gov/800-63-3/sp800-63b.html", - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Domains affected by AdPrep privilege escalation risk", - "guid": "815ff190-f6f3-4757-a516-2f4bf589b705", - "prebuilt": false, - "platform": [ + "name": "Tier Zero / High Value enabled users not requiring smart card authentication", + "guid": "867f9f17-c149-4c4b-ad84-9a807622ff8c", + "prebuilt": true, + "platforms": [ "Active Directory" ], - "category": "Dangerous Privileges", + "category": "Active Directory Hygiene", "description": null, - "query": "MATCH p=(n:Group)-[r:GenericAll]->(m:Domain)\nWHERE n.objectid ENDS WITH \"-527\" // Enterprise Key Admins\nAND NOT ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p", + "query": "MATCH (u:User)\nWHERE ((u:Tag_Tier_Zero) OR COALESCE(u.system_tags, '') CONTAINS 'admin_tier_0')\nAND u.enabled = true\nAND u.smartcardrequired = false\nAND NOT u.name STARTS WITH 'MSOL_' // Removes false positive, Entra sync\nAND NOT u.name STARTS WITH 'PROVAGENTGMSA' // Removes false positive, Entra sync\nAND NOT u.name STARTS WITH 'ADSYNCMSA_' // Removes false positive, Entra sync\nRETURN u", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "On-Prem Users synced to Entra Users with Azure RM Roles (group delegated)", - "guid": "e4f2eada-8a89-4ba9-89eb-abbee4efbc7a", + "name": "On-Prem Users synced to Entra Users that Own Entra Objects", + "guid": "4baf1026-e64c-4e31-afeb-2090b8090130", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory", "Azure" ], "category": "Cross Platform Attack Paths", "description": null, - "query": "MATCH p = (:User)-[:SyncedToEntraUser]->(:AZUser)-[:AZMemberOf]->(:AZGroup)-[:AZOwner|AZUserAccessAdministrator|AZGetCertificates|AZGetKeys|AZGetSecrets|AZAvereContributor|AZKeyVaultContributor|AZContributor|AZVMAdminLogin|AZVMContributor|AZAKSContributor|AZAutomationContributor|AZLogicAppContributor|AZWebsiteContributor]->(:AZBase)\nRETURN p\nLIMIT 1000", + "query": "MATCH p = (:User)-[:SyncedToEntraUser]->(:AZUser)-[:AZOwns]->(:AZBase)\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Enabled computers inactive for 180 days", - "guid": "0768e810-1e1e-4319-a216-76d9c2058644", + "name": "Circular AD group memberships", + "guid": "fcaa5ffc-3d22-481f-a2a2-18a4eec30058", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", - "description": null, - "query": "WITH 180 as inactive_days\nMATCH (n:Computer)\nWHERE n.enabled = true\nAND n.lastlogontimestamp < (datetime().epochseconds - (inactive_days * 86400)) // Replicated value\nAND n.lastlogon < (datetime().epochseconds - (inactive_days * 86400)) // Non-replicated value\nAND n.whencreated < (datetime().epochseconds - (inactive_days * 86400)) // Exclude recently created principals\nAND NOT n.name STARTS WITH 'AZUREADKERBEROS.' // Removes false positive, Azure KRBTGT\nAND NOT n.name STARTS WITH 'AZUREADSSOACC.' // Removes false positive, Entra Seamless SSO\nRETURN n\nLIMIT 1000", + "description": "Detects circular group membership chains where groups are members of themselves through one or more intermediate groups. This causes an administrative complexity.", + "query": "MATCH p=(x:Group)-[:MemberOf*2..]->(y:Group)\nWHERE x.objectid=y.objectid\nRETURN p\nLIMIT 100", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://softwareengineering.stackexchange.com/questions/11856/whats-wrong-with-circular-references" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Disabled Tier Zero / High Value principals", - "guid": "d65a801f-d3ef-4b7e-8030-99ebfd6dad12", - "prebuilt": true, - "platform": [ + "name": "Smart card accounts with passwords not rotated in over 1 year", + "guid": "7e56f2e7-79c3-4f0d-aa3e-14cf3de7ab73", + "prebuilt": false, + "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.enabled = false\nAND NOT n.objectid ENDS WITH '-502' // Removes false positive, KRBTGT\nAND NOT n.objectid ENDS WITH '-500' // Removes false positive, built-in Administrator\nRETURN n\nLIMIT 100", + "query": "MATCH (n:Base)\nWHERE n.pwdlastset < (datetime().epochseconds - (365 * 86400))\nAND n.enabled = true\nAND n.smartcardrequired = true\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Kerberos-enabled service accounts without AES encryption support", - "guid": "cb8cf96e-21c9-422b-9439-390a13446ca6", + "name": "Domains exempting privileged groups from AdminSDHolder protections", + "guid": "79f8d8f9-8291-4bf7-a13a-15989018075f", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", + "description": "Checks the dwAdminSDExMask flag of dSHeuristics.", + "query": "MATCH (n:Domain)\nWHERE n.dsheuristics =~ \".{15}[^0].*\"\nRETURN n", + "revision": 1, + "resources": [ + "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/e5899be4-862e-496f-9a38-33950617d2c5" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] + }, + { + "name": "All Operators", + "guid": "3dfd0843-1ff9-4c21-aa67-feae08d109de", + "prebuilt": false, + "platforms": [ + "Active Directory" + ], + "category": "Domain Information", "description": null, - "query": "MATCH (n:Base)\nWHERE n.hasspn = true\nAND ((\n n.supportedencryptiontypes <> ['Not defined']\n AND n.supportedencryptiontypes <> []\n AND NONE(type IN n.supportedencryptiontypes WHERE type CONTAINS 'AES128' OR type CONTAINS 'AES256')\n)\nOR (n.pwdlastset < datetime('2008-02-27T00:00:00').epochseconds // Password Last Set before Windows Server 2008\nAND NOT n.pwdlastset IN [-1.0, 0.0]\n))\nRETURN n\nLIMIT 100", + "query": "MATCH p=(:Base)-[:MemberOf]->(n:Group)\nWHERE (\n n.objectid ENDS WITH 'S-1-5-32-551' OR // Backup Operators\n n.objectid ENDS WITH 'S-1-5-32-556' OR // Network Configuration Operators\n n.objectid ENDS WITH 'S-1-5-32-549' OR // Server Operators\n n.objectid ENDS WITH 'S-1-5-32-579' OR // Access Control Assistance Operators\n n.objectid ENDS WITH 'S-1-5-32-548' OR // Account Operators\n n.objectid ENDS WITH 'S-1-5-32-569' OR // Cryptographic Operators\n n.objectid ENDS WITH 'S-1-5-32-550' // Print Operators\n)\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Shortest paths from Owned objects", - "guid": "e370a01d-c129-4f19-b88d-9479cbe00028", + "name": "Shortest paths from Azure Applications to Tier Zero / High Value targets", + "guid": "60ff7c58-a98e-4bc1-9e32-8378d2db0c43", "prebuilt": true, - "platform": [ - "Active Directory" + "platforms": [ + "Azure" ], "category": "Shortest Paths", "description": null, - "query": "MATCH p=shortestPath((s:Base)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|MemberOf|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation*1..]->(t:Base))\nWHERE (s:Tag_Owned)\nAND s<>t\nRETURN p\nLIMIT 1000", + "query": "MATCH p=shortestPath((s:AZApp)-[:AZAvereContributor|AZContributor|AZGetCertificates|AZGetKeys|AZGetSecrets|AZHasRole|AZMemberOf|AZOwner|AZRunsAs|AZVMContributor|AZAutomationContributor|AZKeyVaultContributor|AZVMAdminLogin|AZAddMembers|AZAddSecret|AZExecuteCommand|AZGlobalAdmin|AZPrivilegedAuthAdmin|AZGrant|AZGrantSelf|AZPrivilegedRoleAdmin|AZResetPassword|AZUserAccessAdministrator|AZOwns|AZCloudAppAdmin|AZAppAdmin|AZAddOwner|AZManagedIdentity|AZAKSContributor|AZNodeResourceGroup|AZWebsiteContributor|AZLogicAppContributor|AZMGAddMember|AZMGAddOwner|AZMGAddSecret|AZMGGrantAppRoles|AZMGGrantRole|SyncedToADUser|AZRoleEligible|AZContains*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": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Kerberoastable members of Tier Zero / High Value groups", - "guid": "e6da7800-ae06-41cb-80a6-d5421ab2143a", + "name": "Computers where Domain Users can read LAPS passwords", + "guid": "aa4bfa95-e7b9-4d56-8f35-f34f04d7b6f4", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Kerberos Interaction", + "category": "Dangerous Privileges", "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", + "query": "MATCH p=(s:Group)-[:AllExtendedRights|ReadLAPSPassword]->(:Computer)\nWHERE s.objectid ENDS WITH '-513'\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": "https://attack.mitre.org/techniques/T1558/003/", - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Users with non-default Primary Group membership", - "guid": "93890f88-df2c-4167-a945-a53961d08d00", + "name": "Non-Tier Zero principals with BadSuccessor rights (with prerequisites check)", + "guid": "74daaebe-6040-4f7a-9c9a-416faf73dcc3", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Active Directory Hygiene", - "description": null, - "query": "MATCH p=(n:User)-[r:MemberOf]->(g:Group)\nWHERE NOT g.objectid ENDS WITH \"-513\" // Domain Users\nAND r.isprimarygroup = true\nAND NOT n.objectid ENDS WITH \"-501\" // Guests account, as it has primaryGroup to Guests\nAND (n.gmsa IS NULL OR n.gmsa = false) // Not gMSA, as it has primaryGroup to Domain Computers\nAND (n.msa IS NULL OR n.msa = false) // Not MSA, as it has primaryGroup to Domain Computers\nRETURN p", + "category": "Dangerous Privileges", + "description": "Finds non-Tier Zero principals with BadSuccessor rights after checking prerequisites check (DC2025 & KDC key).", + "query": "// Find 2025 DCs\nMATCH (dc:Computer)\nWHERE dc.isdc = true AND dc.operatingsystem CONTAINS '2025'\n// Find gMSAs\nMATCH (m:User)\nWHERE m.gmsa = true\n// Find OU control\nMATCH p = (ou:OU)<-[:WriteDacl|Owns|GenericAll|WriteOwner]-(n:Base)\n// Confirm domain has a 2025 DC\nWHERE ou.domain = dc.domain\n// Confirm domain KDC key\nAND ou.domain = m.domain\n// Exclude Tier Zero\nAND NOT ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p LIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://bsky.app/profile/specterops.io/post/3lpua65qeu22l" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Enabled Tier Zero / High Value principals inactive for 60 days", - "guid": "72550bcb-3c4f-463d-8973-91a49163dc5a", + "name": "Entra Users synced from On-Prem Users added to Domain Admins group", + "guid": "62722d5f-bd93-4d11-beeb-9be261827e4e", "prebuilt": true, - "platform": [ - "Active Directory" + "platforms": [ + "Active Directory", + "Azure" ], - "category": "Active Directory Hygiene", + "category": "Cross Platform Attack Paths", "description": null, - "query": "WITH 60 as inactive_days\nMATCH (n:Base)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nAND n.enabled = true\nAND n.lastlogontimestamp < (datetime().epochseconds - (inactive_days * 86400)) // Replicated value\nAND n.lastlogon < (datetime().epochseconds - (inactive_days * 86400)) // Non-replicated value\nAND n.whencreated < (datetime().epochseconds - (inactive_days * 86400)) // Exclude recently created principals\nAND NOT n.name STARTS WITH 'AZUREADKERBEROS.' // Removes false positive, Azure KRBTGT\nAND NOT n.objectid ENDS WITH '-500' // Removes false positive, built-in Administrator\nAND NOT n.name STARTS WITH 'AZUREADSSOACC.' // Removes false positive, Entra Seamless SSO\nRETURN n", + "query": "MATCH p = (:AZUser)-[:SyncedToADUser]->(:User)-[:MemberOf]->(t:Group)\nWHERE t.objectid ENDS WITH '-512'\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Shortest paths to Domain Admins", - "guid": "f40cb34b-5ec7-44bc-9aa8-a200a4a41f22", + "name": "Foreign principals in Tier Zero / High Value targets", + "guid": "95bec736-86ef-4017-8465-9b9b66548b17", "prebuilt": true, - "platform": [ - "Active Directory" + "platforms": [ + "Azure" ], - "category": "Shortest Paths", + "category": "Azure Hygiene", "description": null, - "query": "MATCH p=shortestPath((t:Group)<-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|MemberOf|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation*1..]-(s:Base))\nWHERE t.objectid ENDS WITH '-512' AND s<>t\nRETURN p\nLIMIT 1000", + "query": "MATCH (n:AZServicePrincipal)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nAND NOT toUpper(n.appownerorganizationid) = toUpper(n.tenantid)\nAND n.appownerorganizationid CONTAINS '-'\nRETURN n\nLIMIT 100", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Enrollment rights on published enrollment agent certificate templates", - "guid": "8483bf5b-89f1-4723-abb2-c48295f6393e", + "name": "On-Prem Users synced to Entra Users with Azure RM Roles (direct)", + "guid": "8569113b-e42e-49b0-a968-53bcf0ccd970", "prebuilt": true, - "platform": [ - "Active Directory" + "platforms": [ + "Active Directory", + "Azure" ], - "category": "Active Directory Certificate Services", + "category": "Cross Platform Attack Paths", "description": null, - "query": "MATCH p = (:Base)-[:Enroll|GenericAll|AllExtendedRights]->(ct:CertTemplate)-[:PublishedTo]->(:EnterpriseCA)\nWHERE '1.3.6.1.4.1.311.20.2.1' IN ct.effectiveekus\nOR '2.5.29.37.0' IN ct.effectiveekus\nOR SIZE(ct.effectiveekus) = 0\nRETURN p\nLIMIT 1000", + "query": "MATCH p = (:User)-[:SyncedToEntraUser]->(:AZUser)-[:AZOwner|AZUserAccessAdministrator|AZGetCertificates|AZGetKeys|AZGetSecrets|AZAvereContributor|AZKeyVaultContributor|AZContributor|AZVMAdminLogin|AZVMContributor|AZAKSContributor|AZAutomationContributor|AZLogicAppContributor|AZWebsiteContributor]->(:AZBase)\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Locations of Tier Zero / High Value objects", - "guid": "18a83a17-b451-4343-acfe-7620516e2968", + "name": "Dangerous privileges for Domain Users groups", + "guid": "9b8b9c18-f8c6-4c54-a20f-de0f7a7edbe0", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Domain Information", + "category": "Dangerous Privileges", "description": null, - "query": "MATCH p = (t:Base)<-[:Contains*1..]-(:Domain)\nWHERE ((t:Tag_Tier_Zero) OR COALESCE(t.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p\nLIMIT 1000", + "query": "MATCH p=(s:Group)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|MemberOf|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation]->(:Base)\nWHERE s.objectid ENDS WITH '-513'\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Domains with a single-point-of-failure Domain Controller", - "guid": "3359a295-7cfd-491f-976b-c5a68647431c", - "prebuilt": false, - "platform": [ + "name": "On-Prem Users synced to Entra Users with Entra Admin Roles (group delegated)", + "guid": "609d648f-7fb8-42d3-ad99-626f9ce1f121", + "prebuilt": true, + "platforms": [ + "Active Directory", + "Azure" + ], + "category": "Cross Platform Attack Paths", + "description": null, + "query": "MATCH p = (:User)-[:SyncedToEntraUser]->(:AZUser)-[:AZMemberOf]->(:AZGroup)-[:AZHasRole]->(:AZRole)\nRETURN p\nLIMIT 1000", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, + { + "name": "Cross-forest trusts with abusable configuration", + "guid": "5cf1f354-80d4-420e-bc4b-424fabc21a56", + "prebuilt": true, + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", "description": null, - "query": "MATCH (n:Group)<-[:MemberOf]-(:Computer)\nWHERE n.objectid ENDS WITH '-516'\nWITH n, COUNT(n) AS dcCount\nWHERE dcCount = 1\nRETURN n", + "query": "MATCH p=(n:Domain)-[:CrossForestTrust|SpoofSIDHistory|AbuseTGTDelegation]-(m:Domain)\nWHERE (n)-[:SpoofSIDHistory|AbuseTGTDelegation]-(m)\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "AS-REP Roastable Tier Zero users (DontReqPreAuth)", - "guid": "6d51e4dc-e1ad-477a-b6c6-324f18f03120", + "name": "KRBTGT accounts with passwords not rotated in over 1 year", + "guid": "1b3ae310-ffa7-4ce5-a37f-6111aef600c8", "prebuilt": false, - "platform": [ + "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.dontreqpreauth = true\nRETURN n", + "query": "MATCH (n:User)\nWHERE (n.objectid ENDS WITH '-502'\nOR n.name STARTS WITH 'AZUREADKERBEROS.'\nOR n.name STARTS WITH 'KRBTGT_AZUREAD@')\nAND n.pwdlastset < (datetime().epochseconds - (365 * 86400))\nRETURN n", "revision": 1, - "note": null, - "resources": "https://attack.mitre.org/techniques/T1558/004/", - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Large default groups with outbound control", - "guid": "a334f21a-3d7f-448e-b7ea-1465a3127bce", - "prebuilt": false, - "platform": [ + "name": "Locations of Tier Zero / High Value objects", + "guid": "18a83a17-b451-4343-acfe-7620516e2968", + "prebuilt": true, + "platforms": [ "Active Directory" ], - "category": "Dangerous Privileges", + "category": "Domain Information", "description": null, - "query": "MATCH p=(n:Group)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|ForceChangePassword|AllExtendedRights|AddMember|AllowedToDelegate|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC5|ADCSESC6a|ADCSESC6b|ADCSESC7|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13]->(:Base)\nWHERE n.objectid ENDS WITH \"-513\" // DOMAIN USERS\nOR n.objectid ENDS WITH \"-515\" // DOMAIN COMPUTERS\nOR n.objectid ENDS WITH \"-S-1-5-11\" // AUTHENTICATED USERS\nOR n.objectid ENDS WITH \"-S-1-1-0\" // EVERYONE\nOR n.objectid ENDS WITH \"S-1-5-32-545\" // USERS\nOR n.objectid ENDS WITH \"S-1-5-32-546\" // GUESTS\nOR n.objectid ENDS WITH \"S-1-5-7\" // ANONYMOUS\nRETURN p", + "query": "MATCH p = (t:Base)<-[:Contains*1..]-(:Domain)\nWHERE ((t:Tag_Tier_Zero) OR COALESCE(t.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "On-Prem Users synced to Entra Users with Entra Group Membership", - "guid": "edb575df-2048-4ef0-a0e4-168544a496e9", + "name": "Shortest paths to systems trusted for unconstrained delegation", + "guid": "16a9e47b-45f8-4514-b409-771bb5186142", "prebuilt": true, - "platform": [ - "Active Directory", - "Azure" + "platforms": [ + "Active Directory" ], - "category": "Cross Platform Attack Paths", + "category": "Shortest Paths", "description": null, - "query": "MATCH p = (:User)-[:SyncedToEntraUser]->(:AZUser)-[:AZMemberOf]->(:AZGroup)\nRETURN p\nLIMIT 1000", + "query": "MATCH p=shortestPath((s)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|MemberOf|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation*1..]->(t:Computer))\nWHERE t.unconstraineddelegation = true AND s<>t\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Tier Zero accounts not members of Denied RODC Password Replication Group", - "guid": "e9613406-e346-410b-a033-690a6cf0c708", + "name": "Accounts with SID History to a non-existent domain", + "guid": "2710401a-c4c2-4d2c-9edb-d7625045f2e8", "prebuilt": false, - "platform": [ + "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: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", + "query": "MATCH (d:Domain)\nWITH collect(d.objectid) AS domainSIDs\nMATCH p=(n:Base)-[:HasSIDHistory]->(m:Base)\nWHERE NOT n.domainsid IN domainSIDs\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Domains without Microsoft LAPS computers", - "guid": "f9b440b5-732c-4ed3-b6d2-83857db17e1a", + "name": "Circular AD group memberships", + "guid": "018c2b45-e30f-47d8-a751-22419c3d0736", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Domain Information", - "description": null, - "query": "MATCH (d:Domain)\nOPTIONAL MATCH (c:Computer)\nWHERE c.domainsid = d.objectid AND c.haslaps = true\nWITH d, COLLECT(c) AS computers\nWHERE SIZE(computers) = 0\nRETURN d", + "category": "Active Directory Hygiene", + "description": "BloodHound typically identifies risk on Active Directory objects stored in OUs, however behind the scenes; Active Directory has a hieracy of containers e.g. CN=SYSTEM and CN=CONFIGURATION, on which control can lead to risk. Results are prone to false-positives but can assist auditing containers permissions.", + "query": "MATCH p=(:Domain)-[:Contains*1..]->(c:Container)<-[r]-(n:Base)\n\n// Exclude Tier Zero\nWHERE NOT ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\n\n// Scope edges to ACLs\nAND r.isacl\n\n// Exclude CN=Users and CN=Computers containers\nAND NOT c.distinguishedname STARTS WITH \"CN=COMPUTERS,DC=\"\nAND NOT c.distinguishedname STARTS WITH \"CN=USERS,DC=\"\n\n// Exclude same-domain unresolved SIDs\nAND NOT (n.distinguishedname IS NULL AND n.domainsid = c.domainsid)\n\n// Exclude default: Cert Publishers\nAND NOT (c.distinguishedname CONTAINS \",CN=PUBLIC KEY SERVICES,CN=SERVICES,CN=CONFIGURATION,DC=\" AND n.objectid ENDS WITH \"-517\")\n\n// Exclude default: RAS and IAS Servers\nAND NOT (c.distinguishedname CONTAINS \"CN=RAS AND IAS SERVERS ACCESS CHECK,CN=SYSTEM,DC=\" AND n.objectid ENDS WITH \"-553\")\n\n// Exclude default: DNS\nAND NOT (c.distinguishedname CONTAINS \"CN=MICROSOFTDNS,CN=SYSTEM,DC=\" AND n.name STARTS WITH \"DNSADMINS@\")\n\n// Exclude default: ConfigMgr\nAND NOT (c.distinguishedname STARTS WITH \"CN=SYSTEM MANAGEMENT,CN=SYSTEM,DC=\" AND n.samaccountname ENDS WITH \"$\")\n\n// Exclude default: Exchange pt1\nAND NOT (c.distinguishedname CONTAINS \"CN=MICROSOFT EXCHANGE,CN=SERVICES,CN=CONFIGURATION,DC=\" AND (n.name STARTS WITH \"EXCHANGE TRUSTED SUBSYSTEM@\" OR n.name STARTS WITH \"ORGANIZATION MANAGEMENT@\" OR n.name STARTS WITH \"EXCHANGE SERVICES@\"))\n\n// Exclude default: Exchange pt2\nAND NOT ((c.distinguishedname CONTAINS \"CN=MONITORING MAILBOXES,CN=MICROSOFT EXCHANGE SYSTEM OBJECTS,DC=\" OR c.distinguishedname CONTAINS \"CN=MICROSOFT EXCHANGE SYSTEM OBJECTS,DC=\") AND n.name STARTS WITH \"EXCHANGE ENTERPRISE SERVERS@\")\n\n// Exclude default: Exchange pt3\nAND NOT ((c.distinguishedname CONTAINS \"CN=ACTIVE DIRECTORY CONNECTIONS,CN=MICROSOFT EXCHANGE,CN=SERVICES,CN=CONFIGURATION,DC=\" OR c.distinguishedname CONTAINS \"CN=MICROSOFT EXCHANGE SYSTEM OBJECTS,DC=\" OR c.distinguishedname =~ \"CN=RECIPIENT UPDATE SERVICES,CN=ADDRESS LISTS CONTAINER,CN=.*,CN=MICROSOFT EXCHANGE,CN=SERVICES,CN=CONFIGURATION,DC=\") AND n.name STARTS WITH \"EXCHANGE DOMAIN SERVERS@\")\n\nRETURN p\nLIMIT 2000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Nested groups within Tier Zero / High Value", - "guid": "8e541e75-df1d-423f-b429-4bbf0403a338", - "prebuilt": true, - "platform": [ + "name": "Object name conflict", + "guid": "c561c4f8-ea45-453f-85a2-3fc2e20e7f8c", + "prebuilt": false, + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", - "description": null, - "query": "MATCH p=(t:Group)<-[:MemberOf*..]-(s:Group)\nWHERE ((t:Tag_Tier_Zero) OR COALESCE(t.system_tags, '') CONTAINS 'admin_tier_0')\nAND NOT s.objectid ENDS WITH '-512' // Domain Admins\nAND NOT s.objectid ENDS WITH '-519' // Enterprise Admins\nRETURN p\nLIMIT 1000", + "description": "When two objects are created with the same Relative Distinguished Name (RDN) in the same parent Organizational Unit or container, the conflict is recognized by the system when one of the new objects replicates to another domain controller. When this happens, one of the objects is renamed with 'CNF'", + "query": "MATCH (n:Base)\nWHERE n.distinguishedname CONTAINS 'CNF:'\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://learn.microsoft.com/en-us/archive/technet-wiki/15435.active-directory-duplicate-object-name-resolution" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Users which do not require password to authenticate", - "guid": "23bdc2ad-6739-4b2b-85d3-258e3f424eb2", + "name": "Shortest paths from Entra Users to Tier Zero / High Value targets", + "guid": "58089b28-54e0-4fd2-bf66-3db480b00e2f", "prebuilt": true, - "platform": [ - "Active Directory" + "platforms": [ + "Azure" ], - "category": "Active Directory Hygiene", + "category": "Shortest Paths", "description": null, - "query": "MATCH (u:User)\nWHERE u.passwordnotreqd = true\nRETURN u\nLIMIT 100", + "query": "MATCH p=shortestPath((s:AZUser)-[:AZAvereContributor|AZContributor|AZGetCertificates|AZGetKeys|AZGetSecrets|AZHasRole|AZMemberOf|AZOwner|AZRunsAs|AZVMContributor|AZAutomationContributor|AZKeyVaultContributor|AZVMAdminLogin|AZAddMembers|AZAddSecret|AZExecuteCommand|AZGlobalAdmin|AZPrivilegedAuthAdmin|AZGrant|AZGrantSelf|AZPrivilegedRoleAdmin|AZResetPassword|AZUserAccessAdministrator|AZOwns|AZCloudAppAdmin|AZAppAdmin|AZAddOwner|AZManagedIdentity|AZAKSContributor|AZNodeResourceGroup|AZWebsiteContributor|AZLogicAppContributor|AZMGAddMember|AZMGAddOwner|AZMGAddSecret|AZMGGrantAppRoles|AZMGGrantRole|SyncedToADUser|AZRoleEligible|AZContains*1..]->(t:AZBase))\nWHERE (t:AZBase) AND t.name =~ '(?i)Global Administrator|User Administrator|Cloud Application Administrator|Authentication Policy Administrator|Exchange Administrator|Helpdesk Administrator|Privileged Authentication Administrator' AND s<>t\nAND ((t:Tag_Tier_Zero) OR COALESCE(t.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Computer owners who can obtain LAPS passwords", - "guid": "92aa81d6-b08e-4abb-ae39-ecbe5735a74c", - "prebuilt": false, - "platform": [ + "name": "Shortest paths from Owned objects to Tier Zero", + "guid": "dfaa8e8f-2c79-4e92-a291-b1347f6e83b0", + "prebuilt": true, + "platforms": [ "Active Directory" ], - "category": "Dangerous Privileges", - "description": "Creators of computer objects get abusable rights on the computer object. If the owner is not explicitly granted ReadLAPSPassword they can still compromise the computer with the abusable owner rights.", - "query": "MATCH p = (c:Computer)<-[:GenericAll|Owns|WriteDacl|WriteOwner|AllExtendedRights]-(n:User)\nWHERE c.haslaps = true AND c.ownersid = n.objectid\nRETURN p", + "category": "Shortest Paths", + "description": null, + "query": "// MANY TO MANY SHORTEST PATH QUERIES USE EXCESSIVE SYSTEM RESOURCES AND TYPICALLY WILL NOT COMPLETE\nMATCH p=shortestPath((s:Tag_Owned)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|MemberOf|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation*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": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { "name": "Enrollment rights on CertTemplates with OIDGroupLink", "guid": "140a68eb-d21c-4b75-971f-309225fb2d75", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Certificate Services", "description": null, "query": "MATCH p = (:Base)-[:Enroll|GenericAll|AllExtendedRights]->(:CertTemplate)-[:ExtendedByPolicy]->(:IssuancePolicy)-[:OIDGroupLink]->(:Group)\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Trace ACE inheritance", - "guid": "8c5454df-3ae8-412c-b271-3c4c55df7141", + "name": "Accounts with SID History to a same-domain account", + "guid": "275d2d58-0cad-4cad-8103-e0874cece666", "prebuilt": false, - "platform": [ + "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\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": "Dangerous Privileges", + "description": null, + "query": "MATCH p=(n:Base)-[:HasSIDHistory]->(m:Base)\nWHERE n.domainsid = m.domainsid\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": "Walter.Legowski, @SadProcessor" + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "All DNSAdmins", - "guid": "183fb320-f3ae-4ab3-a090-3f9a7db692e1", + "name": "Users with non-expiring passwords", + "guid": "212c2a98-53d9-4dfa-b177-42c601452dd1", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Domain Information", + "category": "Active Directory Hygiene", "description": null, - "query": "MATCH p=(n:Base)-[:MemberOf]->(g:Group) \nWHERE n.name STARTS WITH \"DNSADMINS@\"\nRETURN p", + "query": "MATCH (u:User)\nWHERE u.enabled = true\nAND u.pwdneverexpires = true\nRETURN u\nLIMIT 100", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Domains allowing unauthenticated rootDSE searches and binds", - "guid": "ebc79aa4-e816-4be8-93fe-a0b30dbc771d", + "name": "Computers with non-default Primary Group membership", + "guid": "5862dc4e-6f6f-4321-9474-d838968495ed", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", - "description": "Checks the fLDAPBlockAnonOps flag of dSHeuristics.", - "query": "MATCH (n:Domain)\nWHERE n.dsheuristics =~ \".{6}[^2].*\"\nRETURN n", + "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, - "note": null, - "resources": "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/e5899be4-862e-496f-9a38-33950617d2c5", - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Users with passwords not rotated in over 1 year", - "guid": "be70d1bd-b7eb-40b0-971c-eefc50eca032", - "prebuilt": true, - "platform": [ + "name": "Domains allowing unauthenticated domain enumeration", + "guid": "41a08d76-f8a5-4296-ad19-464c4c5c69fe", + "prebuilt": false, + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", "description": null, - "query": "WITH 365 as days_since_change\nMATCH (u:User)\nWHERE u.pwdlastset < (datetime().epochseconds - (days_since_change * 86400))\nAND NOT u.pwdlastset IN [-1.0, 0.0]\nRETURN u\nLIMIT 100", + "query": "MATCH p=(n:Group)-[:MemberOf]->(m:Group)\nWHERE (n.objectid ENDS WITH \"S-1-5-7\" // Anonymous\nOR n.objectid ENDS WITH \"S-1-1-0\") // Everyone\nAND m.objectid ENDS WITH \"S-1-5-32-554\" // Pre-Windows 2000 Compatible Access\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "On-Prem Users synced to Entra Users with Azure RM Roles (direct)", - "guid": "8569113b-e42e-49b0-a968-53bcf0ccd970", - "prebuilt": true, - "platform": [ - "Active Directory", - "Azure" + "name": "Enrollment rights on published ESC15 certificate templates", + "guid": "78d59fe1-e1a0-4813-adc9-c3c96ac08b66", + "prebuilt": false, + "platforms": [ + "Active Directory" ], - "category": "Cross Platform Attack Paths", - "description": null, - "query": "MATCH p = (:User)-[:SyncedToEntraUser]->(:AZUser)-[:AZOwner|AZUserAccessAdministrator|AZGetCertificates|AZGetKeys|AZGetSecrets|AZAvereContributor|AZKeyVaultContributor|AZContributor|AZVMAdminLogin|AZVMContributor|AZAKSContributor|AZAutomationContributor|AZLogicAppContributor|AZWebsiteContributor]->(:AZBase)\nRETURN p\nLIMIT 1000", + "category": "Active Directory Certificate Services", + "description": "Enrollment rights on certificate templates that meet the requirements for the ADCS ESC15 (EKUwu) attack.", + "query": "MATCH p=(:Base)-[:Enroll|AllExtendedRights]->(ct:CertTemplate)-[:PublishedTo]->(:EnterpriseCA)-[:TrustedForNTAuth]->(:NTAuthStore)-[:NTAuthStoreFor]->(:Domain)\nWHERE ct.enrolleesuppliessubject = True\nAND ct.authenticationenabled = False\nAND ct.requiresmanagerapproval = False\nAND ct.schemaversion = 1\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://x.com/SpecterOps/status/1844800558151901639", + "https://msrc.microsoft.com/update-guide/en-US/advisory/CVE-2024-49019" + ], + "acknowledgements": [ + "Jonas B\u00fclow Knudsen, @Jonas_B_K" + ] }, { - "name": "Tier Zero / High Value users with non-expiring passwords", - "guid": "4eca1b69-00a2-48a0-abb3-b94ea647cf6b", + "name": "Shortest paths to Domain Admins from Kerberoastable users", + "guid": "bd163361-1e05-47c7-908b-962aef251535", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Active Directory Hygiene", + "category": "Shortest Paths", "description": null, - "query": "MATCH (u:User)\nWHERE ((u:Tag_Tier_Zero) OR COALESCE(u.system_tags, '') CONTAINS 'admin_tier_0') AND u.enabled = true\nAND u.pwdneverexpires = true\nRETURN u\nLIMIT 100", + "query": "MATCH p=shortestPath((s:User)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|MemberOf|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation*1..]->(t:Group))\nWHERE s.hasspn=true\nAND s.enabled = true\nAND NOT s.objectid ENDS WITH '-502'\nAND NOT COALESCE(s.gmsa, false) = true\nAND NOT COALESCE(s.msa, false) = true\nAND t.objectid ENDS WITH '-512'\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Domains exempting privileged groups from AdminSDHolder protections", - "guid": "79f8d8f9-8291-4bf7-a13a-15989018075f", - "prebuilt": false, - "platform": [ - "Active Directory" + "name": "All service principals with Microsoft Graph privilege to grant arbitrary App Roles", + "guid": "e6d6b5da-89da-4514-a409-2d6e368397da", + "prebuilt": true, + "platforms": [ + "Azure" ], - "category": "Active Directory Hygiene", - "description": "Checks the dwAdminSDExMask flag of dSHeuristics.", - "query": "MATCH (n:Domain)\nWHERE n.dsheuristics =~ \".{15}[^0].*\"\nRETURN n", + "category": "Microsoft Graph", + "description": null, + "query": "MATCH p=(:AZServicePrincipal)-[:AZMGGrantAppRoles]->(:AZTenant)\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/e5899be4-862e-496f-9a38-33950617d2c5", - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Domains allowing authenticated domain enumeration", - "guid": "1e1e6fdd-6973-4547-906c-a494b5fbdcba", + "name": "Domains with more than 50 Tier Zero accounts", + "guid": "f046e95a-5f84-4e83-bcda-6e83f3d8e21a", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", "description": null, - "query": "MATCH p=(n:Group)-[:MemberOf]->(m:Group)\nWHERE n.objectid ENDS WITH \"S-1-5-11\" // Authenticated Users\nAND m.objectid ENDS WITH \"S-1-5-32-554\" // Pre-Windows 2000 Compatible Access\nRETURN p", + "query": "MATCH (d:Domain)-[:Contains*1..]->(n:Base)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nWITH d, COUNT(n) AS adminCount\nWHERE adminCount > 50\nRETURN d", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Domains affected by Exchange privilege escalation risk", - "guid": "f2d09c94-b6f2-4901-9a2d-f8bacd61edc7", - "prebuilt": false, - "platform": [ + "name": "Computers where Domain Users are local administrators", + "guid": "d43a7bdc-33c6-4a39-a3bb-24115749e595", + "prebuilt": true, + "platforms": [ "Active Directory" ], "category": "Dangerous Privileges", "description": null, - "query": "MATCH p=(n:Group)-[r:WriteDacl|ForceChangePassword|AddMember]->(m:Base)\nWHERE n.name STARTS WITH \"EXCHANGE \"\nAND ((m:Tag_Tier_Zero) OR COALESCE(m.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p", + "query": "MATCH p=(s:Group)-[:AdminTo]->(:Computer)\nWHERE s.objectid ENDS WITH '-513'\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Principals with DES-only Kerberos authentication", - "guid": "d03ea1ef-70f0-439b-b1ef-d7f94ceb2af3", + "name": "Nested groups within Tier Zero / High Value", + "guid": "8e541e75-df1d-423f-b429-4bbf0403a338", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", "description": null, - "query": "MATCH (n:Base)\nWHERE n.enabled = true\nAND n.usedeskeyonly = true\nRETURN n", + "query": "MATCH p=(t:Group)<-[:MemberOf*..]-(s:Group)\nWHERE ((t:Tag_Tier_Zero) OR COALESCE(t.system_tags, '') CONTAINS 'admin_tier_0')\nAND NOT s.objectid ENDS WITH '-512' // Domain Admins\nAND NOT s.objectid ENDS WITH '-519' // Enterprise Admins\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Non-Tier Zero principals with BadSuccessor rights (no prerequisites check)", - "guid": "2b9fb71e-73ad-4061-a2df-40c7132b044d", - "prebuilt": false, - "platform": [ - "Active Directory" + "name": "Devices with unsupported operating systems", + "guid": "e3f2b53a-7ce6-4e52-9c74-68b69338288b", + "prebuilt": true, + "platforms": [ + "Azure" ], - "category": "Dangerous Privileges", - "description": "Finds non-Tier Zero principals with BadSuccessor rights with no prerequisites check (DC2025 & KDC key).", - "query": "// Find OU control\nMATCH p = (ou:OU)<-[:WriteDacl|Owns|GenericAll|WriteOwner]-(n:Base)\n// Exclude Tier Zero\nWHERE NOT ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p LIMIT 1000", + "category": "Azure Hygiene", + "description": null, + "query": "MATCH (n:AZDevice)\nWHERE n.operatingsystem CONTAINS 'WINDOWS'\nAND n.operatingsystemversion =~ '(10.0.19044|10.0.22000|10.0.19043|10.0.19042|10.0.19041|10.0.18363|10.0.18362|10.0.17763|10.0.17134|10.0.16299|10.0.15063|10.0.14393|10.0.10586|10.0.10240|6.3.9600|6.2.9200|6.1.7601|6.0.6200|5.1.2600|6.0.6003|5.2.3790|5.0.2195).?.*'\nRETURN n\nLIMIT 100", "revision": 1, - "note": null, - "resources": "https://bsky.app/profile/specterops.io/post/3lpua65qeu22l", - "acknowledgement": "Martin Sohn Christensen, @martinsohndk" + "resources": [], + "acknowledgements": [] }, { - "name": "Non-Tier Zero principals with control of AdminSDHolder", - "guid": "4c1e0137-5b7f-48d8-bd09-9db7674bca61", + "name": "Domains affected by Exchange privilege escalation risk", + "guid": "f2d09c94-b6f2-4901-9a2d-f8bacd61edc7", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Dangerous Privileges", "description": null, - "query": "MATCH p=(n:Group)-[r:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|ForceChangePassword|AllExtendedRights|AddMember|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteOwnerLimitedRights|OwnsLimitedRights]->(m:Container)\nWHERE NOT ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nAND m.name STARTS WITH \"ADMINSDHOLDER@\"\nRETURN p", + "query": "MATCH p=(n:Group)-[r:WriteDacl|ForceChangePassword|AddMember]->(m:Base)\nWHERE n.name STARTS WITH \"EXCHANGE \"\nAND ((m:Tag_Tier_Zero) OR COALESCE(m.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p", "revision": 1, - "note": null, - "resources": "https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/plan/security-best-practices/appendix-c--protected-accounts-and-groups-in-active-directory#adminsdholder", - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Dangerous privileges for Domain Users groups", - "guid": "9b8b9c18-f8c6-4c54-a20f-de0f7a7edbe0", - "prebuilt": true, - "platform": [ - "Active Directory" + "name": "Foreign Service Principals With Group Memberships", + "guid": "327ef6a5-bfa8-4c92-b35a-d3df85264a24", + "prebuilt": false, + "platforms": [ + "Azure" ], - "category": "Dangerous Privileges", - "description": null, - "query": "MATCH p=(s:Group)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|MemberOf|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation]->(:Base)\nWHERE s.objectid ENDS WITH '-513'\nRETURN p\nLIMIT 1000", + "category": "Azure Hygiene", + "description": "Review each to validate whether their presence is expected and whether the assigned group memberships are appropriate for the foreign service principal.", + "query": "MATCH p = (sp:AZServicePrincipal)-[:AZMemberOf]->(g:AZGroup)\nWHERE toUpper(sp.appownerorganizationid) <> toUpper(g.tenantid)\n// Ensure AZServicePrincipal has a valid appownerorganizationid\nAND sp.appownerorganizationid CONTAINS \"-\"\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://posts.specterops.io/microsoft-breach-how-can-i-see-this-in-bloodhound-33c92dca4c65" + ], + "acknowledgements": [ + "Stephen Hinck" + ] }, { - "name": "Shortest paths to Tier Zero / High Value targets", - "guid": "237aac58-8641-4703-a9f7-001d69546fd8", - "prebuilt": true, - "platform": [ + "name": "All Schema Admins", + "guid": "76d8e61d-7a86-40ff-8a85-fd37f1e2563f", + "prebuilt": false, + "platforms": [ "Active Directory" ], - "category": "Shortest Paths", + "category": "Domain Information", "description": null, - "query": "MATCH p=shortestPath((s)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|MemberOf|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation*1..]->(t:Tag_Tier_Zero))\nWHERE s<>t\nRETURN p\nLIMIT 1000", + "query": "MATCH p=(n:Base)-[:MemberOf*1..]->(m:Group)\nWHERE (n:User OR n:Computer)\nAND m.objectid ENDS WITH \"-518\" // Schema Admins\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Members of Allowed RODC Password Replication Group", - "guid": "19fc5acd-e30a-4038-a5b5-2e0494f93373", + "name": "Collection health of specific computer", + "guid": "bb95c9c5-984c-4057-a430-000d684c069a", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Domain Information", - "description": null, - "query": "MATCH p=(n:Base)-[r:MemberOf]->(m:Group)\nWHERE m.objectid ENDS WITH \"-571\"\nAND (n:User or n:Computer)\nRETURN p", + "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, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Shortest paths from Owned objects to Tier Zero", - "guid": "dfaa8e8f-2c79-4e92-a291-b1347f6e83b0", - "prebuilt": true, - "platform": [ + "name": "All paths crossing a specific trust", + "guid": "251fc893-7a6b-4a0a-8650-9d5408d38c3c", + "prebuilt": false, + "platforms": [ "Active Directory" ], - "category": "Shortest Paths", - "description": null, - "query": "// MANY TO MANY SHORTEST PATH QUERIES USE EXCESSIVE SYSTEM RESOURCES AND TYPICALLY WILL NOT COMPLETE\nMATCH p=shortestPath((s:Tag_Owned)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|MemberOf|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation*1..]->(t:Base))\nWHERE s<>t\nAND ((t:Tag_Tier_Zero) OR COALESCE(t.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p\nLIMIT 1000", + "category": "Domain Information", + "description": "All paths crossing a specific trust from a trusted to a trusting domain.", + "query": "// Replace the TRUSTED domain SID\n// Replace the TRUSTING domain SID\nMATCH p=(Trusted:Base)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|MemberOf|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation]->(Trusting:Base)\nWHERE Trusted.domainsid = 'S-1-5-21-1111111111-1111111111-1111111111'\nAND Trusting.domainsid = 'S-1-5-21-2222222222-2222222222-2222222222'\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Domains not verifying UPN and SPN uniqueness", - "guid": "cb0b1591-5c3e-45f1-afb7-984e5ad865d0", + "name": "Non-Tier Zero account with excessive control", + "guid": "944cecfe-519b-4318-b226-e8520161b454", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Active Directory Hygiene", - "description": "Checks the DoNotVerifyUPNAndOrSPNUniqueness flag of dSHeuristics.", - "query": "MATCH (n:Domain)\nWHERE n.dsheuristics =~ \".{20}[^0].*\"\nRETURN n", - "revision": 1, - "note": null, - "resources": "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/e5899be4-862e-496f-9a38-33950617d2c5", - "acknowledgement": null + "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)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation]->(m:Base)\nWHERE 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": 2, + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Domains with functional level not the latest version", - "guid": "3da9d14a-f1cb-4df7-b3da-8d73ff5c401b", + "name": "Tier Zero accounts that can be delegated", + "guid": "4316eaf1-6af0-4879-8f55-ac2633a711c3", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Active Directory Hygiene", + "category": "Kerberos Interaction", "description": null, - "query": "MATCH (n:Domain)\nWHERE toString(n.functionallevel) IN ['2008','2003','2003 Interim','2000 Mixed/Native']\nRETURN n", + "query": "MATCH (m:Base)\nWHERE ((m:Tag_Tier_Zero) OR COALESCE(m.system_tags, '') CONTAINS 'admin_tier_0')\nAND m.enabled = true\nAND m.sensitive = false\nOPTIONAL MATCH (g:Group)<-[:MemberOf*1..]-(n:Base)\nWHERE g.objectid ENDS WITH '-525'\nWITH m, COLLECT(n) AS matchingNs\nWHERE NONE(n IN matchingNs WHERE n.objectid = m.objectid)\nRETURN m", "revision": 1, - "note": "Functional level <4", - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Enabled users inactive for 180 days", - "guid": "71972f3c-b32d-4023-a841-5cc8cc1c1867", + "name": "Non-Tier Zero account with unconstrained delegation", + "guid": "e7e9a927-3f34-42c7-b921-d8bcf626011e", "prebuilt": false, - "platform": [ + "platforms": [ + "Active Directory" + ], + "category": "Dangerous Privileges", + "description": null, + "query": "MATCH (n:Base)\nWHERE n.unconstraineddelegation = true\nAND NOT ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN n", + "revision": 1, + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] + }, + { + "name": "Computers with the WebClient running", + "guid": "51107ad1-f0bc-43d3-a561-5cee471ca196", + "prebuilt": true, + "platforms": [ + "Active Directory" + ], + "category": "NTLM Relay Attacks", + "description": null, + "query": "MATCH (c:Computer)\nWHERE c.webclientrunning = True\nRETURN c LIMIT 1000", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, + { + "name": "Computers without Windows LAPS", + "guid": "7c50f724-c467-4005-8e3f-9a6ce1461db0", + "prebuilt": false, + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", "description": null, - "query": "WITH 180 as inactive_days\nMATCH (n:User)\nWHERE n.enabled = true\nAND n.lastlogontimestamp < (datetime().epochseconds - (inactive_days * 86400)) // Replicated value\nAND n.lastlogon < (datetime().epochseconds - (inactive_days * 86400)) // Non-replicated value\nAND n.whencreated < (datetime().epochseconds - (inactive_days * 86400)) // Exclude recently created principals\nAND NOT n.objectid ENDS WITH '-500' // Removes false positive, built-in Administrator\nRETURN n\nLIMIT 1000", + "query": "MATCH (c:Computer)\nWHERE c.operatingsystem =~ '(?i).*WINDOWS (SERVER)? ?(10|11|2019|2022|2025).*'\nAND c.haslaps = false\nAND c.enabled = true\nRETURN c\nLIMIT 100", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://learn.microsoft.com/en-us/windows-server/identity/laps/laps-overview" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "AS-REP Roastable users (DontReqPreAuth)", - "guid": "2570e359-dec1-419d-b0dc-a204bd64ee42", + "name": "Map domain trusts", + "guid": "268d3d26-5bc2-4820-a6ed-09d20f3d5413", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Kerberos Interaction", + "category": "Domain Information", "description": null, - "query": "MATCH (u:User)\nWHERE u.dontreqpreauth = true\nAND u.enabled = true\nRETURN u\nLIMIT 100", + "query": "MATCH p = (:Domain)-[:SameForestTrust|CrossForestTrust]->(:Domain)\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": "https://attack.mitre.org/techniques/T1558/004/", - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "All ADCS ESC privilege escalation edges", - "guid": "49db8edc-8421-438f-b97b-23c042959bef", + "name": "Overprivileged Microsoft Entra Connect accounts", + "guid": "9e6e75b4-9ecc-45d4-a39b-b6427b813f0a", "prebuilt": false, - "platform": [ + "platforms": [ + "Active Directory", + "Azure" + ], + "category": "Active Directory Hygiene", + "description": "Legacy MSOL accounts were by default deployed with Domain Admins or Enterprise Admins membership.", + "query": "MATCH p=(n:User)-[:MemberOf*1..]->(g:Group)\nWHERE n.name STARTS WITH \"MSOL_\"\nAND (g.objectid ENDS WITH \"-512\" // Domain Admins\nOR g.objectid ENDS WITH \"-519\") // Entterprise Admins\nRETURN p", + "revision": 1, + "resources": [ + "https://learn.microsoft.com/en-us/entra/identity/hybrid/connect/reference-connect-accounts-permissions" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] + }, + { + "name": "Domains allowing unauthenticated NSPI RPC binds", + "guid": "a950fdab-5934-4c69-a88b-e2e0e3da9d52", + "prebuilt": false, + "platforms": [ "Active Directory" ], - "category": "Active Directory Certificate Services", + "category": "Active Directory Hygiene", + "description": "Checks the fAllowAnonNSPI flag of dSHeuristics.", + "query": "MATCH (n:Domain)\nWHERE n.dsheuristics =~ \".{7}[^0].*\"\nRETURN n", + "revision": 1, + "resources": [ + "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/e5899be4-862e-496f-9a38-33950617d2c5" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] + }, + { + "name": "ESC8-vulnerable Enterprise CAs", + "guid": "60881923-296c-4702-adf7-a4f059dc9bb8", + "prebuilt": true, + "platforms": [ + "Active Directory" + ], + "category": "NTLM Relay Attacks", "description": null, - "query": "MATCH p=(:Base)-[:ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|GoldenCert|CoerceAndRelayNTLMToADCS]->(:Base)\nRETURN p", + "query": "MATCH (n:EnterpriseCA)\nWHERE n.hasvulnerableendpoint=true\nRETURN n", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, + { + "name": "Circular AZ group memberships", + "guid": "b005669c-d8af-47ae-a0f1-4f36cd5334ab", + "prebuilt": false, + "platforms": [ + "Azure" + ], + "category": "Azure Hygiene", + "description": "Detects circular group membership chains where groups are members of themselves through one or more intermediate groups. This causes an administrative complexity.", + "query": "MATCH p=(x:AZGroup)-[:AZMemberOf*2..]->(y:AZGroup)\nWHERE x.objectid=y.objectid\nRETURN p\nLIMIT 100", "revision": 1, - "note": null, "resources": [ - "https://posts.specterops.io/certified-pre-owned-d95910965cd2", - "https://posts.specterops.io/adcs-attack-paths-in-bloodhound-part-1-799f3d3b03cf", - "https://posts.specterops.io/adcs-attack-paths-in-bloodhound-part-2-ac7f925d1547", - "https://posts.specterops.io/adcs-attack-paths-in-bloodhound-part-3-33efb00856ac", - "https://posts.specterops.io/adcs-esc13-abuse-technique-fda4272fbd53", - "https://specterops.io/blog/2025/04/08/the-renaissance-of-ntlm-relay-attacks-everything-you-need-to-know/#:~:text=Introducing%20the%20CoerceAndRelayNTLMToADCS%20Edge" + "https://softwareengineering.stackexchange.com/questions/11856/whats-wrong-with-circular-references" ], - "acknowledgement": null + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "All Schema Admins", - "guid": "76d8e61d-7a86-40ff-8a85-fd37f1e2563f", + "name": "Enabled computers inactive for 180 days", + "guid": "0768e810-1e1e-4319-a216-76d9c2058644", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Domain Information", + "category": "Active Directory Hygiene", "description": null, - "query": "MATCH p=(n:Base)-[:MemberOf*1..]->(m:Group)\nWHERE (n:User OR n:Computer)\nAND m.objectid ENDS WITH \"-518\" // Schema Admins\nRETURN p", + "query": "WITH 180 as inactive_days\nMATCH (n:Computer)\nWHERE n.enabled = true\nAND n.lastlogontimestamp < (datetime().epochseconds - (inactive_days * 86400)) // Replicated value\nAND n.lastlogon < (datetime().epochseconds - (inactive_days * 86400)) // Non-replicated value\nAND n.whencreated < (datetime().epochseconds - (inactive_days * 86400)) // Exclude recently created principals\nAND NOT n.name STARTS WITH 'AZUREADKERBEROS.' // Removes false positive, Azure KRBTGT\nAND NOT n.name STARTS WITH 'AZUREADSSOACC.' // Removes false positive, Entra Seamless SSO\nRETURN n\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Domains with List Object mode enabled", - "guid": "05e2a94b-5ee6-47ec-b715-3982f30af01b", + "name": "Enrollment rights on published enrollment agent certificate templates", + "guid": "8483bf5b-89f1-4723-abb2-c48295f6393e", + "prebuilt": true, + "platforms": [ + "Active Directory" + ], + "category": "Active Directory Certificate Services", + "description": null, + "query": "MATCH p = (:Base)-[:Enroll|GenericAll|AllExtendedRights]->(ct:CertTemplate)-[:PublishedTo]->(:EnterpriseCA)\nWHERE '1.3.6.1.4.1.311.20.2.1' IN ct.effectiveekus\nOR '2.5.29.37.0' IN ct.effectiveekus\nOR SIZE(ct.effectiveekus) = 0\nRETURN p\nLIMIT 1000", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, + { + "name": "AdminSDHolder protected Accounts and Groups", + "guid": "5ee2f40e-a55c-4140-ab8a-91746ba3752b", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Domain Information", - "description": "Checks the fDoListObject flag of dSHeuristics.", - "query": "MATCH (n:Domain)\nWHERE n.dsheuristics =~ \".{2}[^0].*\"\nRETURN n", + "description": "Objects whose permissions are set by SDProp to the template AdminSDHolder object as per MS-ADTS 3.1.1.6.1.2 Protected Objects. Does not exclude objects if specified in dSHeuristics dwAdminSDExMask", + "query": "MATCH (n:Base)-[:MemberOf*0..]->(m:Group)\nWHERE (\n n.objectid =~ \".*-(S-1-5-32-544|S-1-5-32-548|S-1-5-32-549|S-1-5-32-550|S-1-5-32-551|S-1-5-32-552|518|512|519)$\" // Groups\n OR m.objectid =~ \".*-(S-1-5-32-544|S-1-5-32-548|S-1-5-32-549|S-1-5-32-550|S-1-5-32-551|S-1-5-32-552|518|512|519)$\" // Members of groups\n OR n.objectid =~ \".*-(500|502|516|521)$\" // Direct objects\n)\nRETURN n", "revision": 1, - "note": null, - "resources": "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/e5899be4-862e-496f-9a38-33950617d2c5", - "acknowledgement": null + "resources": [ + "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/a0d0b4fa-2895-4c64-b182-ba64ad0f84b8", + "https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/plan/security-best-practices/appendix-c--protected-accounts-and-groups-in-active-directory" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "All paths crossing a specific trust", - "guid": "251fc893-7a6b-4a0a-8650-9d5408d38c3c", + "name": "All DNSAdmins", + "guid": "183fb320-f3ae-4ab3-a090-3f9a7db692e1", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Domain Information", - "description": "All paths crossing a specific trust from a trusted to a trusting domain.", - "query": "// Replace the TRUSTED domain SID\n// Replace the TRUSTING domain SID\nMATCH p=(Trusted:Base)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|MemberOf|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation]->(Trusting:Base)\nWHERE Trusted.domainsid = 'S-1-5-21-1111111111-1111111111-1111111111'\nAND Trusting.domainsid = 'S-1-5-21-2222222222-2222222222-2222222222'\nRETURN p\nLIMIT 1000", + "description": null, + "query": "MATCH p=(n:Base)-[:MemberOf]->(g:Group) \nWHERE n.name STARTS WITH \"DNSADMINS@\"\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Shortest paths to Domain Admins from Kerberoastable users", - "guid": "bd163361-1e05-47c7-908b-962aef251535", + "name": "Map OU structure", + "guid": "8f14084b-5065-43d8-865a-a6ac52da25d1", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Shortest Paths", + "category": "Domain Information", "description": null, - "query": "MATCH p=shortestPath((s:User)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|MemberOf|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation*1..]->(t:Group))\nWHERE s.hasspn=true\nAND s.enabled = true\nAND NOT s.objectid ENDS WITH '-502'\nAND NOT COALESCE(s.gmsa, false) = true\nAND NOT COALESCE(s.msa, false) = true\nAND t.objectid ENDS WITH '-512'\nRETURN p\nLIMIT 1000", + "query": "MATCH p = (:Domain)-[:Contains*1..]->(:OU)\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Non-Tier Zero principals with BadSuccessor rights (with prerequisites check)", - "guid": "74daaebe-6040-4f7a-9c9a-416faf73dcc3", - "prebuilt": false, - "platform": [ + "name": "Principals with foreign domain group membership", + "guid": "8fb3214a-5a75-4ecd-b293-c121abd94b4b", + "prebuilt": true, + "platforms": [ "Active Directory" ], "category": "Dangerous Privileges", - "description": "Finds non-Tier Zero principals with BadSuccessor rights after checking prerequisites check (DC2025 & KDC key).", - "query": "// Find 2025 DCs\nMATCH (dc:Computer)\nWHERE dc.isdc = true AND dc.operatingsystem CONTAINS '2025'\n// Find gMSAs\nMATCH (m:User)\nWHERE m.gmsa = true\n// Find OU control\nMATCH p = (ou:OU)<-[:WriteDacl|Owns|GenericAll|WriteOwner]-(n:Base)\n// Confirm domain has a 2025 DC\nWHERE ou.domain = dc.domain\n// Confirm domain KDC key\nAND ou.domain = m.domain\n// Exclude Tier Zero\nAND NOT ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p LIMIT 1000", + "description": null, + "query": "MATCH p=(s:Base)-[:MemberOf]->(t:Group)\nWHERE s.domainsid<>t.domainsid\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": "https://bsky.app/profile/specterops.io/post/3lpua65qeu22l", - "acknowledgement": "Martin Sohn Christensen, @martinsohndk" + "resources": [], + "acknowledgements": [] }, { - "name": "Non-default permissions on IssuancePolicy nodes", - "guid": "b2280665-c91b-448c-8c0f-97d1f38b6f59", + "name": "Paths from Domain Users to Tier Zero / High Value targets", + "guid": "977bec40-565c-40b8-90c8-e3e122c291cd", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Active Directory Certificate Services", + "category": "Dangerous Privileges", "description": null, - "query": "MATCH p = (s:Base)-[:GenericAll|GenericWrite|Owns|WriteOwner|WriteDacl]->(:IssuancePolicy)\nWHERE NOT s.objectid ENDS WITH '-512' AND NOT s.objectid ENDS WITH '-519'\nRETURN p\nLIMIT 1000", + "query": "MATCH p=shortestPath((s:Group)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|MemberOf|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation*1..]->(t:Base))\nWHERE s.objectid ENDS WITH '-513' AND s<>t\nAND ((t:Tag_Tier_Zero) OR COALESCE(t.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Accounts with SID History to a same-domain account", - "guid": "275d2d58-0cad-4cad-8103-e0874cece666", + "name": "Enabled built-in guest user accounts", + "guid": "bb0f620d-6a55-4413-ac74-4c82905e8598", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Dangerous Privileges", + "category": "Active Directory Hygiene", "description": null, - "query": "MATCH p=(n:Base)-[:HasSIDHistory]->(m:Base)\nWHERE n.domainsid = m.domainsid\nRETURN p", + "query": "MATCH (n:User)\nWHERE n.objectid ENDS WITH \"-501\"\nAND n.enabled = true\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "On-Prem Users synced to Entra Users with Entra Admin Roles (direct)", - "guid": "de717635-d31f-4fbd-930b-b4dac0f22118", + "name": "Domain controllers with UPN certificate mapping enabled", + "guid": "799ea3ce-572b-4594-98c4-041aa2ae6176", "prebuilt": true, - "platform": [ - "Active Directory", - "Azure" + "platforms": [ + "Active Directory" ], - "category": "Cross Platform Attack Paths", + "category": "Active Directory Certificate Services", "description": null, - "query": "MATCH p = (:User)-[:SyncedToEntraUser]->(:AZUser)-[:AZHasRole]->(:AZRole)\nRETURN p\nLIMIT 1000", + "query": "MATCH p = (s:Computer)-[:DCFor]->(:Domain)\nWHERE s.certificatemappingmethodsraw IN [4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31]\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://support.microsoft.com/en-us/topic/kb5014754-certificate-based-authentication-changes-on-windows-domain-controllers-ad2c23b0-15d8-4340-a468-4d4f3b188f16", + "https://specterops.io/blog/2024/02/28/adcs-esc14-abuse-technique/" + ], + "acknowledgements": [ + "Jonas B\u00fclow Knudsen, @Jonas_B_K" + ] }, { - "name": "Tier Zero users not member of Protected Users", - "guid": "543eb01d-9fa3-4b8f-a936-b46bbfdaa2ae", + "name": "Non-Tier Zero principals with BadSuccessor rights (no prerequisites check)", + "guid": "2b9fb71e-73ad-4061-a2df-40c7132b044d", + "prebuilt": false, + "platforms": [ + "Active Directory" + ], + "category": "Dangerous Privileges", + "description": "Finds non-Tier Zero principals with BadSuccessor rights with no prerequisites check (DC2025 & KDC key).", + "query": "// Find OU control\nMATCH p = (ou:OU)<-[:WriteDacl|Owns|GenericAll|WriteOwner]-(n:Base)\n// Exclude Tier Zero\nWHERE NOT ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p LIMIT 1000", + "revision": 1, + "resources": [ + "https://bsky.app/profile/specterops.io/post/3lpua65qeu22l" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] + }, + { + "name": "Tier Zero users with email", + "guid": "9654c0d4-f1e8-4393-a2d1-53a5554a9de8", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", - "description": null, - "query": "MATCH (m:User)\nWHERE ((m:Tag_Tier_Zero) OR COALESCE(m.system_tags, '') CONTAINS 'admin_tier_0')\nOPTIONAL MATCH (g:Group)<-[:MemberOf*1..]-(n:Base)\nWHERE g.objectid ENDS WITH '-525'\nWITH m, COLLECT(n) AS matchingNs\nWHERE NONE(n IN matchingNs WHERE n.objectid = m.objectid)\nRETURN m", + "description": "Tier Zero accounts with email access have an increased attack surface.", + "query": "MATCH (n)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nAND n.email <> \"\"\nAND n.enabled = true\nAND NOT toUpper(n.email) ENDS WITH \".ONMICROSOFT.COM\"\nAND NOT (\n (toUpper(n.email) STARTS WITH \"HEALTHMAILBOX\"\n OR toUpper(n.email) STARTS WITH \"MSEXCHDISCOVERYMAILBOX\"\n OR toUpper(n.email) STARTS WITH \"MSEXCHDISCOVERY\"\n OR toUpper(n.email) STARTS WITH \"MSEXCHAPPROVAL\"\n OR toUpper(n.email) STARTS WITH \"FEDERATEDEMAIL\"\n OR toUpper(n.email) STARTS WITH \"SYSTEMMAILBOX\"\n OR toUpper(n.email) STARTS WITH \"MIGRATION.\")\n AND\n (n.name STARTS WITH \"SM_\"\n OR n.name STARTS WITH \"HEALTHMAILBOX\")\n)\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Usage of built-in domain Administrator account", - "guid": "35b1206f-871b-44aa-a601-c5258060dfcf", + "name": "Domain migration groups", + "guid": "f39c4953-ae92-4d67-bb50-eb1a161d4d3f", "prebuilt": false, - "platform": [ + "platforms": [ + "Active Directory" + ], + "category": "Domain Information", + "description": null, + "query": "MATCH (n:Group)\nWHERE n.name CONTAINS \"$$$@\"\nRETURN n", + "revision": 1, + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] + }, + { + "name": "Shortest paths from Owned objects", + "guid": "e370a01d-c129-4f19-b88d-9479cbe00028", + "prebuilt": true, + "platforms": [ + "Active Directory" + ], + "category": "Shortest Paths", + "description": null, + "query": "MATCH p=shortestPath((s:Base)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|MemberOf|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation*1..]->(t:Base))\nWHERE (s:Tag_Owned)\nAND s<>t\nRETURN p\nLIMIT 1000", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, + { + "name": "Domains with smart card accounts where smart account passwords do not expire", + "guid": "97e05e67-5961-4aba-a8e7-fe5f92334035", + "prebuilt": true, + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", - "description": "Usage of Active Directory's built-in Administrator account is a sign that the account is not only used for break-glass purposes.", - "query": "MATCH (n:User)\nWHERE n.objectid ENDS WITH \"-500\"\nAND (\n n.lastlogontimestamp > (datetime().epochseconds - (60 * 86400)) OR\n n.lastlogon > (datetime().epochseconds - (60 * 86400))\n)\nAND NOT n.whencreated > (datetime().epochseconds - (60 * 86400))\nRETURN n", + "description": null, + "query": "MATCH (s:Domain)-[:Contains*1..]->(t:Base)\nWHERE s.expirepasswordsonsmartcardonlyaccounts = false\nAND t.enabled = true\nAND t.smartcardrequired = true\nRETURN s", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "Enabled built-in guest user accounts", - "guid": "bb0f620d-6a55-4413-ac74-4c82905e8598", + "name": "Foreign Service Principals With an EntraID Admin Role", + "guid": "b6235820-4e0d-4dfa-af5b-729b5644feb5", "prebuilt": false, - "platform": [ + "platforms": [ + "Azure" + ], + "category": "Dangerous Privileges", + "description": "Entra ID admin roles grant significant control over a tenant environment, even if the role is not a default Tier Zero / High Value role", + "query": "MATCH p = (sp:AZServicePrincipal)-[:AZHasRole]->(r:AZRole)\nWHERE toUpper(sp.appownerorganizationid) <> toUpper(sp.tenantid)\n// Ensure AZServicePrincipal has a valid appownerorganizationid\nAND sp.appownerorganizationid CONTAINS \"-\"\nRETURN p\nLIMIT 1000", + "revision": 1, + "resources": [ + "https://posts.specterops.io/microsoft-breach-how-can-i-see-this-in-bloodhound-33c92dca4c65" + ], + "acknowledgements": [ + "Stephen Hinck" + ] + }, + { + "name": "All coerce and NTLM relay edges", + "guid": "15c5ff3b-856c-44d1-a731-a8cb72512dd1", + "prebuilt": true, + "platforms": [ + "Active Directory" + ], + "category": "NTLM Relay Attacks", + "description": null, + "query": "MATCH p = (n:Base)-[:CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|CoerceAndRelayNTLMToADCS|CoerceAndRelayNTLMToSMB]->(:Base)\nRETURN p LIMIT 500", + "revision": 1, + "resources": [ + "https://specterops.io/blog/2025/04/08/the-renaissance-of-ntlm-relay-attacks-everything-you-need-to-know/" + ], + "acknowledgements": [] + }, + { + "name": "Users which do not require password to authenticate", + "guid": "23bdc2ad-6739-4b2b-85d3-258e3f424eb2", + "prebuilt": true, + "platforms": [ "Active Directory" ], "category": "Active Directory Hygiene", "description": null, - "query": "MATCH (n:User)\nWHERE n.objectid ENDS WITH \"-501\"\nAND n.enabled = true\nRETURN n", + "query": "MATCH (u:User)\nWHERE u.passwordnotreqd = true\nRETURN u\nLIMIT 100", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] }, { - "name": "All Operators", - "guid": "3dfd0843-1ff9-4c21-aa67-feae08d109de", + "name": "Shortest paths to Tier Zero / High Value targets", + "guid": "237aac58-8641-4703-a9f7-001d69546fd8", + "prebuilt": true, + "platforms": [ + "Active Directory" + ], + "category": "Shortest Paths", + "description": null, + "query": "MATCH p=shortestPath((s)-[:Owns|GenericAll|GenericWrite|WriteOwner|WriteDacl|MemberOf|ForceChangePassword|AllExtendedRights|AddMember|HasSession|GPLink|AllowedToDelegate|CoerceToTGT|AllowedToAct|AdminTo|CanPSRemote|CanRDP|ExecuteDCOM|HasSIDHistory|AddSelf|DCSync|ReadLAPSPassword|ReadGMSAPassword|DumpSMSAPassword|SQLAdmin|AddAllowedToAct|WriteSPN|AddKeyCredentialLink|SyncLAPSPassword|WriteAccountRestrictions|WriteGPLink|GoldenCert|ADCSESC1|ADCSESC3|ADCSESC4|ADCSESC6a|ADCSESC6b|ADCSESC9a|ADCSESC9b|ADCSESC10a|ADCSESC10b|ADCSESC13|SyncedToEntraUser|CoerceAndRelayNTLMToSMB|CoerceAndRelayNTLMToADCS|WriteOwnerLimitedRights|OwnsLimitedRights|CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|Contains|DCFor|SameForestTrust|SpoofSIDHistory|AbuseTGTDelegation*1..]->(t:Tag_Tier_Zero))\nWHERE s<>t\nRETURN p\nLIMIT 1000", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, + { + "name": "All incoming and local paths for a specific computer", + "guid": "1f67e538-19d4-4020-89c8-5b39b31571bd", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Domain Information", - "description": null, - "query": "MATCH p=(:Base)-[:MemberOf]->(n:Group)\nWHERE (\n n.objectid ENDS WITH 'S-1-5-32-551' OR // Backup Operators\n n.objectid ENDS WITH 'S-1-5-32-556' OR // Network Configuration Operators\n n.objectid ENDS WITH 'S-1-5-32-549' OR // Server Operators\n n.objectid ENDS WITH 'S-1-5-32-579' OR // Access Control Assistance Operators\n n.objectid ENDS WITH 'S-1-5-32-548' OR // Account Operators\n n.objectid ENDS WITH 'S-1-5-32-569' OR // Cryptographic Operators\n n.objectid ENDS WITH 'S-1-5-32-550' // Print Operators\n)\nRETURN p", + "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, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Principal with SPN keyword", - "guid": "38a9c4c9-3d70-453f-a017-cbfd35ed9917", + "name": "Accounts with weak password storage encryption", + "guid": "8bd6fcf2-3f3c-414c-857a-4caf28e49def", + "prebuilt": true, + "platforms": [ + "Active Directory" + ], + "category": "Active Directory Hygiene", + "description": "Accounts with passwords set before the existence of Windows Server 2008 Domain Controller which therefore lack AES encryption keys.", + "query": "MATCH (n:Base)\nWHERE n.pwdlastset < 1204070400 // Password Last Set before Windows Server 2008 release\nRETURN n\nLIMIT 100", + "revision": 2, + "resources": [ + "https://techcommunity.microsoft.com/blog/coreinfrastructureandsecurityblog/decrypting-the-selection-of-supported-kerberos-encryption-types/1628797" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] + }, + { + "name": "Sessions across trusts", + "guid": "aea7ac64-1f51-407b-b0ee-19fd30075794", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Kerberos Interaction", - "description": "Finds service accounts used with a specific Kerberos-enabled service or all service accounts running on a Kerberos-enabled service on a specific server.", - "query": "// Replace keyword with a service type or server name (not FQDN)\nWITH \"KEYWORD\" as SPNKeyword\nMATCH (n:User)\nWHERE ANY(keyword IN n.serviceprincipalnames WHERE toUpper(keyword) CONTAINS toUpper(SPNKeyword))\nRETURN n", + "category": "Domain Information", + "description": "Users logging on across a trust, the users originate from trusted domains.", + "query": "MATCH p=(trustedDomainPrincipal:Computer)-[r:HasSession]->(trustingDomainPrincipal:User)\nWHERE trustedDomainPrincipal.domainsid <> trustingDomainPrincipal.domainsid\nRETURN p\nLIMIT 1000", "revision": 1, - "note": null, - "resources": "https://adsecurity.org/?page_id=183", - "acknowledgement": "Ryan, @haus3c" + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Tier Zero computers at risk of constrained delegation", - "guid": "8641e593-f2f2-48ba-bd45-fbc86e9f632a", + "name": "Domains not mitigating CVE-2021-42291", + "guid": "02202726-d86d-46c2-891c-9770c635f76f", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Dangerous Privileges", - "description": null, - "query": "MATCH p = (n:Computer)<-[:AllowedToDelegate]-(:Base)\nWHERE ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p", + "category": "Active Directory Hygiene", + "description": "Checks the AttributeAuthorizationOnLDAPAdd flag of dSHeuristics.", + "query": "MATCH (n:Domain)\nWHERE n.dsheuristics =~ \".{27}[^1].*\"\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [ + "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/e5899be4-862e-496f-9a38-33950617d2c5" + ], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Large default groups with outbound control of OUs", - "guid": "310b3626-f8e6-4ab0-832c-72df6048597f", + "name": "Domains affected by AdPrep privilege escalation risk", + "guid": "815ff190-f6f3-4757-a516-2f4bf589b705", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], "category": "Dangerous Privileges", "description": null, - "query": "MATCH p=(n:Group)-[]->(:OU)\nWHERE n.objectid ENDS WITH \"-513\" // DOMAIN USERS\nOR n.objectid ENDS WITH \"-515\" // DOMAIN COMPUTERS\nOR n.objectid ENDS WITH \"-S-1-5-11\" // AUTHENTICATED USERS\nOR n.objectid ENDS WITH \"-S-1-1-0\" // EVERYONE\nOR n.objectid ENDS WITH \"S-1-5-32-545\" // USERS\nOR n.objectid ENDS WITH \"S-1-5-32-546\" // GUESTS\nOR n.objectid ENDS WITH \"S-1-5-7\" // ANONYMOUS\nRETURN p", + "query": "MATCH p=(n:Group)-[r:GenericAll]->(m:Domain)\nWHERE n.objectid ENDS WITH \"-527\" // Enterprise Key Admins\nAND NOT ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN p", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "Non-Tier Zero account with unconstrained delegation", - "guid": "e7e9a927-3f34-42c7-b921-d8bcf626011e", + "name": "Tier Zero computers with passwords older than the default maximum password age", + "guid": "b6d6d0bf-130e-4719-996b-adc29bba36e9", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Dangerous Privileges", + "category": "Active Directory Hygiene", "description": null, - "query": "MATCH (n:Base)\nWHERE n.unconstraineddelegation = true\nAND NOT ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN n", + "query": "MATCH (n:Computer)\nWHERE n.enabled = true\nAND n.whencreated < (datetime().epochseconds - (60 * 3 * 86400))\nAND n.pwdlastset < (datetime().epochseconds - (60 * 3 * 86400))\nAND coalesce(n.system_tags, \"\") CONTAINS \"admin_tier_0\"\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] }, { - "name": "All Kerberoastable users", - "guid": "14ab4eaa-b73b-49c4-b2d1-1e020757c995", + "name": "On-Prem Users synced to Entra Users with Entra Group Membership", + "guid": "edb575df-2048-4ef0-a0e4-168544a496e9", + "prebuilt": true, + "platforms": [ + "Active Directory", + "Azure" + ], + "category": "Cross Platform Attack Paths", + "description": null, + "query": "MATCH p = (:User)-[:SyncedToEntraUser]->(:AZUser)-[:AZMemberOf]->(:AZGroup)\nRETURN p\nLIMIT 1000", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, + { + "name": "Domains where any user can join a computer to the domain", + "guid": "421921fa-bc0f-4659-9680-b7481adcb132", "prebuilt": true, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "Kerberos Interaction", + "category": "Active Directory Hygiene", "description": null, - "query": "MATCH (u:User)\nWHERE 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", + "query": "MATCH (n:Domain)\nWHERE n.machineaccountquota > 0\nRETURN n", "revision": 1, - "note": null, - "resources": "https://attack.mitre.org/techniques/T1558/003/", - "acknowledgement": null + "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" + ], + "acknowledgements": [] }, { - "name": "Tier Zero omputers not requiring inbound SMB signing", - "guid": "13485477-f026-4b1f-906d-4f2e37364ba4", + "name": "Usage of built-in domain Administrator account", + "guid": "35b1206f-871b-44aa-a601-c5258060dfcf", "prebuilt": false, - "platform": [ + "platforms": [ "Active Directory" ], - "category": "NTLM Relay Attacks", + "category": "Active Directory Hygiene", + "description": "Usage of Active Directory's built-in Administrator account is a sign that the account is not only used for break-glass purposes.", + "query": "MATCH (n:User)\nWHERE n.objectid ENDS WITH \"-500\"\nAND (\n n.lastlogontimestamp > (datetime().epochseconds - (60 * 86400)) OR\n n.lastlogon > (datetime().epochseconds - (60 * 86400))\n)\nAND NOT n.whencreated > (datetime().epochseconds - (60 * 86400))\nRETURN n", + "revision": 1, + "resources": [], + "acknowledgements": [ + "Martin Sohn Christensen, @martinsohndk" + ] + }, + { + "name": "Shortest paths to privileged roles", + "guid": "3dc73dd8-4873-4aeb-a88f-56a58c77f512", + "prebuilt": true, + "platforms": [ + "Azure" + ], + "category": "Shortest Paths", "description": null, - "query": "MATCH (n:Computer)\nWHERE n.smbsigning = False\nAND ((n:Tag_Tier_Zero) OR COALESCE(n.system_tags, '') CONTAINS 'admin_tier_0')\nRETURN n", + "query": "MATCH p=shortestPath((s:AZBase)-[:AZAvereContributor|AZContributor|AZGetCertificates|AZGetKeys|AZGetSecrets|AZHasRole|AZMemberOf|AZOwner|AZRunsAs|AZVMContributor|AZAutomationContributor|AZKeyVaultContributor|AZVMAdminLogin|AZAddMembers|AZAddSecret|AZExecuteCommand|AZGlobalAdmin|AZPrivilegedAuthAdmin|AZGrant|AZGrantSelf|AZPrivilegedRoleAdmin|AZResetPassword|AZUserAccessAdministrator|AZOwns|AZCloudAppAdmin|AZAppAdmin|AZAddOwner|AZManagedIdentity|AZAKSContributor|AZNodeResourceGroup|AZWebsiteContributor|AZLogicAppContributor|AZMGAddMember|AZMGAddOwner|AZMGAddSecret|AZMGGrantAppRoles|AZMGGrantRole|SyncedToADUser|AZRoleEligible|AZContains*1..]->(t:AZRole))\nWHERE t.name =~ '(?i)Global Administrator|User Administrator|Cloud Application Administrator|Authentication Policy Administrator|Exchange Administrator|Helpdesk Administrator|Privileged Authentication Administrator' AND s<>t\nRETURN p\nLIMIT 1000", + "revision": 1, + "resources": [], + "acknowledgements": [] + }, + { + "name": "Principals with DES-only Kerberos authentication", + "guid": "d03ea1ef-70f0-439b-b1ef-d7f94ceb2af3", + "prebuilt": true, + "platforms": [ + "Active Directory" + ], + "category": "Active Directory Hygiene", + "description": null, + "query": "MATCH (n:Base)\nWHERE n.enabled = true\nAND n.usedeskeyonly = true\nRETURN n", "revision": 1, - "note": null, - "resources": null, - "acknowledgement": null + "resources": [], + "acknowledgements": [] } ] \ No newline at end of file From 6232efec9f72a6b6d1bc3afedfe4a43bae0ae46d Mon Sep 17 00:00:00 2001 From: Joey Dreijer Date: Tue, 17 Jun 2025 12:30:25 +0200 Subject: [PATCH 12/17] Make sure test runs on the current PR branch --- .github/workflows/syntax.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/syntax.yml b/.github/workflows/syntax.yml index 12ae534..982ef47 100644 --- a/.github/workflows/syntax.yml +++ b/.github/workflows/syntax.yml @@ -14,6 +14,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} - name: Set up Python 3.10 uses: actions/setup-python@v3 with: @@ -61,7 +63,6 @@ jobs: run: | python utilities/python/convert.py ./queries ./Queries.json - - name: Configure Git run: | git config --global user.name "github-actions[bot]" From 090483dfb3616b64545cb478579333dad1b8b51e Mon Sep 17 00:00:00 2001 From: Joey Dreijer Date: Tue, 17 Jun 2025 12:34:53 +0200 Subject: [PATCH 13/17] Make sure test runs on the current PR branch --- .github/workflows/syntax.yml | 72 ++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/.github/workflows/syntax.yml b/.github/workflows/syntax.yml index 982ef47..09deef0 100644 --- a/.github/workflows/syntax.yml +++ b/.github/workflows/syntax.yml @@ -27,7 +27,7 @@ jobs: - name: Test queries with pytest run: | - pytest tests/test_cypher_syntax.py + pytest tests/test_cypher_syntax.py -rs - name: Add test report to summary run: cat test-report.md >> $GITHUB_STEP_SUMMARY @@ -38,42 +38,42 @@ jobs: name: test-report path: test-report.md - build: - runs-on: ubuntu-latest - needs: test - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.head_ref }} - - name: Set up Python 3.10 - uses: actions/setup-python@v3 - with: - python-version: "3.10" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt + # build: + # runs-on: ubuntu-latest + # needs: test + # steps: + # - uses: actions/checkout@v4 + # with: + # ref: ${{ github.head_ref }} + # - name: Set up Python 3.10 + # uses: actions/setup-python@v3 + # with: + # python-version: "3.10" + # - name: Install dependencies + # run: | + # python -m pip install --upgrade pip + # pip install -r requirements.txt - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt + # - name: Install dependencies + # run: | + # python -m pip install --upgrade pip + # pip install -r requirements.txt - - name: Convert queries into single json - run: | - python utilities/python/convert.py ./queries ./Queries.json + # - name: Convert queries into single json + # run: | + # python utilities/python/convert.py ./queries ./Queries.json - - name: Configure Git - run: | - git config --global user.name "github-actions[bot]" - git config --global user.email "github-actions[bot]@users.noreply.github.com" + # - name: Configure Git + # run: | + # git config --global user.name "github-actions[bot]" + # git config --global user.email "github-actions[bot]@users.noreply.github.com" - - name: Commit if changed - run: | - git add ./Queries.json - if git diff --staged --quiet; then - echo "No changes to commit" - else - git commit -m "Update combined queries" - git push - fi + # - name: Commit if changed + # run: | + # git add ./Queries.json + # if git diff --staged --quiet; then + # echo "No changes to commit" + # else + # git commit -m "Update combined queries" + # git push + # fi From ca0c8bb4ec608e40d8e7e5fd01d47ffdf1c1a5b4 Mon Sep 17 00:00:00 2001 From: Joey Dreijer Date: Tue, 17 Jun 2025 12:36:00 +0200 Subject: [PATCH 14/17] Make sure test runs on the current PR branch --- tests/test_cypher_syntax.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_cypher_syntax.py b/tests/test_cypher_syntax.py index 01d07a2..8e6bc46 100644 --- a/tests/test_cypher_syntax.py +++ b/tests/test_cypher_syntax.py @@ -23,13 +23,13 @@ def syntaxError(self, recognizer, offendingSymbol, line: int, column: int, raise ValueError(f"Syntax error at line: {line}, msg: {msg}") -def get_query_files(cypher_dir: str = "Queries") -> list: +def get_query_files(cypher_dir: str = "queries") -> list: if not os.path.exists(cypher_dir): return [] - return glob.glob(os.path.join("Queries", "**", "*.yml"), recursive=True) + return glob.glob(os.path.join("queries", "**", "*.yml"), recursive=True) -@pytest.mark.parametrize("file_path", get_query_files("Queries")) +@pytest.mark.parametrize("file_path", get_query_files("queries")) def test_cypher_validation(file_path: str, request: pytest.FixtureRequest) -> None: with open(file_path, "r") as f: yaml_object = yaml.safe_load(f) @@ -64,7 +64,7 @@ def test_cypher_validation(file_path: str, request: pytest.FixtureRequest) -> No def test_duplicate_guid() -> None: - query_files = get_query_files("Queries") + query_files = get_query_files("queries") guids = set() # Iterate over all query files and check for duplicate GUIDs From 623e5bb17e771fa2829927ee944e3980af8316d5 Mon Sep 17 00:00:00 2001 From: Joey Dreijer Date: Tue, 17 Jun 2025 12:38:03 +0200 Subject: [PATCH 15/17] Run build to combine all queries --- .github/workflows/syntax.yml | 70 ++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/.github/workflows/syntax.yml b/.github/workflows/syntax.yml index 09deef0..6d3d9d7 100644 --- a/.github/workflows/syntax.yml +++ b/.github/workflows/syntax.yml @@ -38,42 +38,42 @@ jobs: name: test-report path: test-report.md - # build: - # runs-on: ubuntu-latest - # needs: test - # steps: - # - uses: actions/checkout@v4 - # with: - # ref: ${{ github.head_ref }} - # - name: Set up Python 3.10 - # uses: actions/setup-python@v3 - # with: - # python-version: "3.10" - # - name: Install dependencies - # run: | - # python -m pip install --upgrade pip - # pip install -r requirements.txt + build: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt - # - name: Install dependencies - # run: | - # python -m pip install --upgrade pip - # pip install -r requirements.txt + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt - # - name: Convert queries into single json - # run: | - # python utilities/python/convert.py ./queries ./Queries.json + - name: Convert queries into single json + run: | + python utilities/python/convert.py ./queries ./Queries.json - # - name: Configure Git - # run: | - # git config --global user.name "github-actions[bot]" - # git config --global user.email "github-actions[bot]@users.noreply.github.com" + - name: Configure Git + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" - # - name: Commit if changed - # run: | - # git add ./Queries.json - # if git diff --staged --quiet; then - # echo "No changes to commit" - # else - # git commit -m "Update combined queries" - # git push - # fi + - name: Commit if changed + run: | + git add ./Queries.json + if git diff --staged --quiet; then + echo "No changes to commit" + else + git commit -m "Update combined queries" + git push + fi From 11af03ad405e2bf413376784d377162481e9f392 Mon Sep 17 00:00:00 2001 From: Joey Dreijer Date: Tue, 17 Jun 2025 12:41:49 +0200 Subject: [PATCH 16/17] Added code owners for github workflows/actions --- .github/CODEOWNERS | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..7a2747a --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,4 @@ +/.github/workflows/ @d3vzer0 +/.github/actions/ @d3vzer0 +**/action.yml @d3vzer0 +**/action.yaml @d3vzer0 \ No newline at end of file From f3a062a523cf6376043c6176ac9dd3506c97dada Mon Sep 17 00:00:00 2001 From: Joey Dreijer Date: Tue, 17 Jun 2025 12:49:06 +0200 Subject: [PATCH 17/17] Added code owners for github workflows/actions --- .github/workflows/syntax.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/syntax.yml b/.github/workflows/syntax.yml index 6d3d9d7..982ef47 100644 --- a/.github/workflows/syntax.yml +++ b/.github/workflows/syntax.yml @@ -27,7 +27,7 @@ jobs: - name: Test queries with pytest run: | - pytest tests/test_cypher_syntax.py -rs + pytest tests/test_cypher_syntax.py - name: Add test report to summary run: cat test-report.md >> $GITHUB_STEP_SUMMARY