From 8d27b6069b890d3f4229684f479f4ab400f36ecb Mon Sep 17 00:00:00 2001 From: Terrance DeJesus <99630311+terrancedejesus@users.noreply.github.com> Date: Tue, 10 Sep 2024 11:26:40 -0400 Subject: [PATCH] [Rule Tuning] M365/Azure Brute-Forcing New Rule and Tuning; Deprecate Similar Rule (#4057) * deprecated rule; tuned for single source inclusion * adjusted query comments * added min-stack * updated date * added Azure-based rule for brute forcing * added reference to o365spray * fixed tag * adjusted query comment * added rule for repeat source * adjusted query to use count distinct * added intervals; adjusted lookback window according to time truncation --- ...ntra_signin_brute_force_microsoft_365.toml | 83 +++++++++++++++++++ ...ute_force_microsoft_365_repeat_source.toml | 82 ++++++++++++++++++ ..._365_brute_force_user_account_attempt.toml | 44 +++++----- ...65_potential_password_spraying_attack.toml | 8 +- 4 files changed, 192 insertions(+), 25 deletions(-) create mode 100644 rules/integrations/azure/credential_access_entra_signin_brute_force_microsoft_365.toml create mode 100644 rules/integrations/azure/credential_access_entra_signin_brute_force_microsoft_365_repeat_source.toml diff --git a/rules/integrations/azure/credential_access_entra_signin_brute_force_microsoft_365.toml b/rules/integrations/azure/credential_access_entra_signin_brute_force_microsoft_365.toml new file mode 100644 index 000000000..651a63259 --- /dev/null +++ b/rules/integrations/azure/credential_access_entra_signin_brute_force_microsoft_365.toml @@ -0,0 +1,83 @@ +[metadata] +creation_date = "2024/09/06" +integration = ["azure"] +maturity = "production" +min_stack_comments = "ES|QL not available until 8.13.0 in technical preview." +min_stack_version = "8.13.0" +updated_date = "2024/09/06" + +[rule] +author = ["Elastic"] +description = """ +Identifies potential brute-force attempts against Microsoft 365 user accounts by detecting a high number of failed +interactive or non-interactive login attempts within a 30-minute window. Attackers may attempt to brute force user +accounts to gain unauthorized access to Microsoft 365 services via different services such as Exchange, SharePoint, or +Teams. +""" +false_positives = [ + """ + Automated processes that attempt to authenticate using expired credentials or have misconfigured authentication + settings may lead to false positives. + """, +] +from = "now-60m" +language = "esql" +interval = "10m" +license = "Elastic License v2" +name = "Azure Entra Sign-in Brute Force against Microsoft 365 Accounts" +note = "This rule relies on Azure Entra ID sign-in logs, but filters for Microsoft 365 resources." +references = [ + "https://cloud.hacktricks.xyz/pentesting-cloud/azure-security/az-unauthenticated-enum-and-initial-entry/az-password-spraying", + "https://github.com/0xZDH/o365spray" +] +risk_score = 47 +rule_id = "35ab3cfa-6c67-11ef-ab4d-f661ea17fbcc" +severity = "medium" +tags = [ + "Domain: Cloud", + "Domain: SaaS", + "Data Source: Azure", + "Data Source: Entra ID", + "Data Source: Entra ID Sign-in", + "Use Case: Identity and Access Audit", + "Use Case: Threat Detection", + "Tactic: Credential Access", +] +timestamp_override = "event.ingested" +type = "esql" + +query = ''' +from logs-azure.signinlogs* +// truncate the timestamp to a 30-minute window +| eval target_time_window = DATE_TRUNC(30 minutes, @timestamp) +| WHERE + event.dataset == "azure.signinlogs" + and event.category == "authentication" + and to_lower(azure.signinlogs.properties.resource_display_name) rlike "(.*)365(.*)" + and azure.signinlogs.category in ("NonInteractiveUserSignInLogs", "SignInLogs") + and event.outcome != "success" + // for tuning review azure.signinlogs.properties.status.error_code + // https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes +// count the number of login sources and failed login attempts +| stats + login_source_count = count(source.ip), + failed_login_count = count(*) by target_time_window, azure.signinlogs.properties.user_principal_name + +// filter for users with more than 20 login sources or failed login attempts +| where (login_source_count >= 20 or failed_login_count >= 20) +''' + + +[[rule.threat]] +framework = "MITRE ATT&CK" +[[rule.threat.technique]] +id = "T1110" +name = "Brute Force" +reference = "https://attack.mitre.org/techniques/T1110/" + + +[rule.threat.tactic] +id = "TA0006" +name = "Credential Access" +reference = "https://attack.mitre.org/tactics/TA0006/" + diff --git a/rules/integrations/azure/credential_access_entra_signin_brute_force_microsoft_365_repeat_source.toml b/rules/integrations/azure/credential_access_entra_signin_brute_force_microsoft_365_repeat_source.toml new file mode 100644 index 000000000..1297dd557 --- /dev/null +++ b/rules/integrations/azure/credential_access_entra_signin_brute_force_microsoft_365_repeat_source.toml @@ -0,0 +1,82 @@ +[metadata] +creation_date = "2024/09/06" +integration = ["azure"] +maturity = "production" +min_stack_comments = "ES|QL not available until 8.13.0 in technical preview." +min_stack_version = "8.13.0" +updated_date = "2024/09/06" + +[rule] +author = ["Elastic"] +description = """ +Identifies potential brute-force attempts against Microsoft 365 user accounts by detecting a high number of failed +interactive or non-interactive login attempts within a 30-minute window from a single source. Attackers may attempt to brute force user +accounts to gain unauthorized access to Microsoft 365 services via different services such as Exchange, SharePoint, or +Teams. +""" +false_positives = [ + """ + Automated processes that attempt to authenticate using expired credentials or have misconfigured authentication + settings may lead to false positives. + """, +] +from = "now-30m" +language = "esql" +interval = "10m" +license = "Elastic License v2" +name = "Azure Entra Sign-in Brute Force Microsoft 365 Accounts by Repeat Source" +note = "This rule relies on Azure Entra ID sign-in logs, but filters for Microsoft 365 resources." +references = [ + "https://cloud.hacktricks.xyz/pentesting-cloud/azure-security/az-unauthenticated-enum-and-initial-entry/az-password-spraying", + "https://github.com/0xZDH/o365spray" +] +risk_score = 47 +rule_id = "c6655282-6c79-11ef-bbb5-f661ea17fbcc" +severity = "medium" +tags = [ + "Domain: Cloud", + "Domain: SaaS", + "Data Source: Azure", + "Data Source: Entra ID", + "Data Source: Entra ID Sign-in", + "Use Case: Identity and Access Audit", + "Use Case: Threat Detection", + "Tactic: Credential Access", +] +timestamp_override = "event.ingested" +type = "esql" + +query = ''' +from logs-azure.signinlogs* +| WHERE + event.dataset == "azure.signinlogs" + and event.category == "authentication" + and to_lower(azure.signinlogs.properties.resource_display_name) rlike "(.*)365(.*)" + and azure.signinlogs.category in ("NonInteractiveUserSignInLogs", "SignInLogs") + and event.outcome != "success" + + // For tuning, review azure.signinlogs.properties.status.error_code + // https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes + +// Count the number of unique targets per source IP +| stats + target_count = count_distinct(azure.signinlogs.properties.user_principal_name) by source.ip + +// Filter for at least 10 distinct failed login attempts from a single source +| where target_count >= 10 +''' + + +[[rule.threat]] +framework = "MITRE ATT&CK" +[[rule.threat.technique]] +id = "T1110" +name = "Brute Force" +reference = "https://attack.mitre.org/techniques/T1110/" + + +[rule.threat.tactic] +id = "TA0006" +name = "Credential Access" +reference = "https://attack.mitre.org/tactics/TA0006/" + diff --git a/rules/integrations/o365/credential_access_microsoft_365_brute_force_user_account_attempt.toml b/rules/integrations/o365/credential_access_microsoft_365_brute_force_user_account_attempt.toml index 383bb71d6..a74a09a64 100644 --- a/rules/integrations/o365/credential_access_microsoft_365_brute_force_user_account_attempt.toml +++ b/rules/integrations/o365/credential_access_microsoft_365_brute_force_user_account_attempt.toml @@ -2,13 +2,14 @@ creation_date = "2020/11/30" integration = ["o365"] maturity = "production" -updated_date = "2024/07/01" +min_stack_comments = "ES|QL not available until 8.13.0 in technical preview." +min_stack_version = "8.13.0" +updated_date = "2024/09/05" [rule] author = ["Elastic", "Willem D'Haese", "Austin Songer"] description = """ -Identifies attempts to brute force a Microsoft 365 user account. An adversary may attempt a brute force attack to obtain -unauthorized access to user accounts. +Identifies potential brute-force attempts against Microsoft 365 user accounts by detecting a high number of failed login attempts or login sources within a 30-minute window. Attackers may attempt to brute force user accounts to gain unauthorized access to Microsoft 365 services. """ false_positives = [ """ @@ -20,13 +21,10 @@ from = "now-9m" language = "esql" license = "Elastic License v2" name = "Attempts to Brute Force a Microsoft 365 User Account" -note = """## Setup - -The Office 365 Logs Fleet integration, Filebeat module, or similarly structured data is required to be compatible with this rule.""" references = [ "https://blueteamblog.com/7-ways-to-monitor-your-office-365-logs-using-siem", - "https://learn.microsoft.com/en-us/purview/audit-log-detailed-properties" - ] + "https://learn.microsoft.com/en-us/purview/audit-log-detailed-properties", +] risk_score = 47 rule_id = "26f68dba-ce29-497b-8e13-b4fde1db5a2d" severity = "medium" @@ -35,6 +33,7 @@ tags = [ "Domain: SaaS", "Data Source: Microsoft 365", "Use Case: Identity and Access Audit", + "Use Case: Threat Detection", "Tactic: Credential Access", ] timestamp_override = "event.ingested" @@ -42,18 +41,20 @@ type = "esql" query = ''' from logs-o365.audit-* -| MV_EXPAND event.category -| WHERE event.dataset == "o365.audit" - AND event.category == "authentication" +// truncate the timestamp to a 30-minute window +| eval target_time_window = DATE_TRUNC(30 minutes, @timestamp) +| mv_expand event.category +| where event.dataset == "o365.audit" + and event.category == "authentication" // filter only on Entra ID or Exchange audit logs in O365 integration - AND event.provider in ("AzureActiveDirectory", "Exchange") + and event.provider in ("AzureActiveDirectory", "Exchange") // filter only for UserLoginFailed or partial failures - AND event.action in ("UserLoginFailed", "PasswordLogonInitialAuthUsingPassword") + and event.action in ("UserLoginFailed", "PasswordLogonInitialAuthUsingPassword") // ignore specific logon errors - AND not o365.audit.LogonError in ( + and not o365.audit.LogonError in ( "EntitlementGrantsNotFound", "UserStrongAuthEnrollmentRequired", "UserStrongAuthClientAuthNRequired", @@ -65,16 +66,18 @@ from logs-o365.audit-* "CmsiInterrupt" ) // filters out non user or application logins based on target - AND o365.audit.Target.Type in ("0", "2", "3", "5", "6", "10") + and o365.audit.Target.Type in ("0", "2", "3", "5", "6", "10") // filters only for logins from user or application, ignoring oauth:token - AND to_lower(o365.audit.ExtendedProperties.RequestType) rlike "(.*)login(.*)" + and to_lower(o365.audit.ExtendedProperties.RequestType) rlike "(.*)login(.*)" -| STATS - // count the number of failed login attempts target per user - login_attempt_counts = COUNT(*) by o365.audit.Target.ID, o365.audit.LogonError +// count the number of login sources and failed login attempts +| stats + login_source_count = count(source.ip), + failed_login_count = count(*) by target_time_window, o365.audit.UserId -| WHERE login_attempt_counts > 10 +// filter for users with more than 20 login sources or failed login attempts +| where (login_source_count >= 20 or failed_login_count >= 20) ''' @@ -90,3 +93,4 @@ reference = "https://attack.mitre.org/techniques/T1110/" id = "TA0006" name = "Credential Access" reference = "https://attack.mitre.org/tactics/TA0006/" + diff --git a/rules/integrations/o365/credential_access_microsoft_365_potential_password_spraying_attack.toml b/rules/integrations/o365/credential_access_microsoft_365_potential_password_spraying_attack.toml index 697a0f771..7b2baeefe 100644 --- a/rules/integrations/o365/credential_access_microsoft_365_potential_password_spraying_attack.toml +++ b/rules/integrations/o365/credential_access_microsoft_365_potential_password_spraying_attack.toml @@ -2,7 +2,7 @@ creation_date = "2020/12/01" integration = ["o365"] maturity = "production" -updated_date = "2024/05/21" +updated_date = "2024/09/05" [rule] author = ["Elastic"] @@ -21,10 +21,8 @@ from = "now-30m" index = ["filebeat-*", "logs-o365*"] language = "kuery" license = "Elastic License v2" -name = "Potential Password Spraying of Microsoft 365 User Accounts" -note = """## Setup - -The Office 365 Logs Fleet integration, Filebeat module, or similarly structured data is required to be compatible with this rule.""" +name = "Deprecated - Potential Password Spraying of Microsoft 365 User Accounts" +note = """This rule has been deprecated in favor of `Attempts to Brute Force a Microsoft 365 User Account` (26f68dba-ce29-497b-8e13-b4fde1db5a2d).""" risk_score = 73 rule_id = "3efee4f0-182a-40a8-a835-102c68a4175d" severity = "high"