[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
This commit is contained in:
Terrance DeJesus
2024-09-10 11:26:40 -04:00
committed by GitHub
parent 0a08f5e677
commit 8d27b6069b
4 changed files with 192 additions and 25 deletions
@@ -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/"
@@ -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/"
@@ -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/"
@@ -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"