Azure Entra Sign-in Brute Force against Microsoft 365 Accounts

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.

Elastic rule (View on GitHub)

 1[metadata]
 2creation_date = "2024/09/06"
 3integration = ["azure"]
 4maturity = "production"
 5min_stack_comments = "ES|QL not available until 8.13.0 in technical preview."
 6min_stack_version = "8.13.0"
 7updated_date = "2024/10/09"
 8
 9[rule]
10author = ["Elastic"]
11description = """
12Identifies potential brute-force attempts against Microsoft 365 user accounts by detecting a high number of failed
13interactive or non-interactive login attempts within a 30-minute window. Attackers may attempt to brute force user
14accounts to gain unauthorized access to Microsoft 365 services via different services such as Exchange, SharePoint, or
15Teams.
16"""
17false_positives = [
18    """
19    Automated processes that attempt to authenticate using expired credentials or have misconfigured authentication
20    settings may lead to false positives.
21    """,
22]
23from = "now-60m"
24language = "esql"
25interval = "10m"
26license = "Elastic License v2"
27name = "Azure Entra Sign-in Brute Force against Microsoft 365 Accounts"
28note = "This rule relies on Azure Entra ID sign-in logs, but filters for Microsoft 365 resources."
29references = [
30    "https://cloud.hacktricks.xyz/pentesting-cloud/azure-security/az-unauthenticated-enum-and-initial-entry/az-password-spraying",
31    "https://github.com/0xZDH/o365spray"
32]
33risk_score = 47
34rule_id = "35ab3cfa-6c67-11ef-ab4d-f661ea17fbcc"
35severity = "medium"
36tags = [
37    "Domain: Cloud",
38    "Domain: SaaS",
39    "Data Source: Azure",
40    "Data Source: Entra ID",
41    "Data Source: Entra ID Sign-in",
42    "Use Case: Identity and Access Audit",
43    "Use Case: Threat Detection",
44    "Tactic: Credential Access",
45]
46timestamp_override = "event.ingested"
47type = "esql"
48
49query = '''
50from logs-azure.signinlogs*
51// truncate the timestamp to a 30-minute window
52| eval target_time_window = DATE_TRUNC(30 minutes, @timestamp)
53| WHERE
54  event.dataset == "azure.signinlogs"
55  and event.category == "authentication"
56  and to_lower(azure.signinlogs.properties.resource_display_name) rlike "(.*)365(.*)"
57  and azure.signinlogs.category in ("NonInteractiveUserSignInLogs", "SignInLogs")
58  and event.outcome != "success"
59  // for tuning review azure.signinlogs.properties.status.error_code
60  // https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes
61
62// keep only relevant fields
63| keep target_time_window, event.dataset, event.category, azure.signinlogs.properties.resource_display_name, azure.signinlogs.category, event.outcome, azure.signinlogs.properties.user_principal_name, source.ip
64
65// count the number of login sources and failed login attempts
66| stats
67  login_source_count = count(source.ip),
68  failed_login_count = count(*) by target_time_window, azure.signinlogs.properties.user_principal_name
69
70// filter for users with more than 20 login sources or failed login attempts
71| where (login_source_count >= 20 or failed_login_count >= 20)
72'''
73
74
75[[rule.threat]]
76framework = "MITRE ATT&CK"
77[[rule.threat.technique]]
78id = "T1110"
79name = "Brute Force"
80reference = "https://attack.mitre.org/techniques/T1110/"
81
82
83[rule.threat.tactic]
84id = "TA0006"
85name = "Credential Access"
86reference = "https://attack.mitre.org/tactics/TA0006/"

This rule relies on Azure Entra ID sign-in logs, but filters for Microsoft 365 resources.

References

Related rules

to-top