Attempts to Brute Force a Microsoft 365 User Account

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.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2020/11/30"
  3integration = ["o365"]
  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", "Willem D'Haese", "Austin Songer"]
 11description = """
 12Identifies 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.
 13"""
 14false_positives = [
 15    """
 16    Automated processes that attempt to authenticate using expired credentials and unbounded retries may lead to false
 17    positives.
 18    """,
 19]
 20from = "now-9m"
 21language = "esql"
 22license = "Elastic License v2"
 23name = "Attempts to Brute Force a Microsoft 365 User Account"
 24references = [
 25    "https://blueteamblog.com/7-ways-to-monitor-your-office-365-logs-using-siem",
 26    "https://learn.microsoft.com/en-us/purview/audit-log-detailed-properties",
 27]
 28risk_score = 47
 29rule_id = "26f68dba-ce29-497b-8e13-b4fde1db5a2d"
 30severity = "medium"
 31tags = [
 32    "Domain: Cloud",
 33    "Domain: SaaS",
 34    "Data Source: Microsoft 365",
 35    "Use Case: Identity and Access Audit",
 36    "Use Case: Threat Detection",
 37    "Tactic: Credential Access",
 38]
 39timestamp_override = "event.ingested"
 40type = "esql"
 41
 42query = '''
 43from logs-o365.audit-*
 44// truncate the timestamp to a 30-minute window
 45| eval target_time_window = DATE_TRUNC(30 minutes, @timestamp)
 46| mv_expand event.category
 47| where event.dataset == "o365.audit"
 48  and event.category == "authentication"
 49
 50  // filter only on Entra ID or Exchange audit logs in O365 integration
 51  and event.provider in ("AzureActiveDirectory", "Exchange")
 52
 53  // filter only for UserLoginFailed or partial failures
 54  and event.action in ("UserLoginFailed", "PasswordLogonInitialAuthUsingPassword")
 55
 56  // ignore specific logon errors
 57  and not o365.audit.LogonError in (
 58    "EntitlementGrantsNotFound",
 59    "UserStrongAuthEnrollmentRequired",
 60    "UserStrongAuthClientAuthNRequired",
 61    "InvalidReplyTo",
 62    "SsoArtifactExpiredDueToConditionalAccess",
 63    "PasswordResetRegistrationRequiredInterrupt",
 64    "SsoUserAccountNotFoundInResourceTenant",
 65    "UserStrongAuthExpired",
 66    "CmsiInterrupt"
 67)
 68
 69  // ignore unavailable
 70  and o365.audit.UserId != "Not Available"
 71
 72  // filters out non user or application logins based on target
 73  and o365.audit.Target.Type in ("0", "2", "3", "5", "6", "10")
 74
 75  // filters only for logins from user or application, ignoring oauth:token
 76  and to_lower(o365.audit.ExtendedProperties.RequestType) rlike "(.*)login(.*)"
 77
 78// keep only relevant fields
 79| keep event.provider, event.dataset, event.category, o365.audit.UserId, event.action, source.ip, o365.audit.LogonError, o365.audit.ExtendedProperties.RequestType, o365.audit.Target.Type, target_time_window
 80
 81// count the number of login sources and failed login attempts
 82| stats
 83  login_source_count = count(source.ip),
 84  failed_login_count = count(*) by target_time_window, o365.audit.UserId
 85
 86// filter for users with more than 20 login sources or failed login attempts
 87| where (login_source_count >= 20 or failed_login_count >= 20)
 88'''
 89
 90
 91[[rule.threat]]
 92framework = "MITRE ATT&CK"
 93[[rule.threat.technique]]
 94id = "T1110"
 95name = "Brute Force"
 96reference = "https://attack.mitre.org/techniques/T1110/"
 97
 98
 99[rule.threat.tactic]
100id = "TA0006"
101name = "Credential Access"
102reference = "https://attack.mitre.org/tactics/TA0006/"

References

Related rules

to-top