M365 Identity User Account Lockouts

Last updated 23 days ago on 2025-12-10
Created 8 months ago on 2025-05-10

About

Detects a burst of Microsoft 365 user account lockouts within a short 5-minute window. A high number of IdsLocked login errors across multiple user accounts may indicate brute-force attempts for the same users resulting in lockouts.
Tags
Domain: CloudDomain: SaaSData Source: Microsoft 365Data Source: Microsoft 365 Audit LogsUse Case: Threat DetectionUse Case: Identity and Access AuditTactic: Credential AccessLanguage: esql
Severity
medium
Risk Score
47
MITRE ATT&CK™

Credential Access (TA0006)(external, opens in a new tab or window)

License
Elastic License v2(external, opens in a new tab or window)

Definition

Integration Pack
Prebuilt Security Detection Rules
Related Integrations

o365(external, opens in a new tab or window)

Query
text code block:
from logs-o365.audit-* | mv_expand event.category | eval Esql.time_window_date_trunc = date_trunc(5 minutes, @timestamp) | where event.dataset == "o365.audit" and event.category == "authentication" and event.provider in ("AzureActiveDirectory", "Exchange") and event.action in ("UserLoginFailed", "PasswordLogonInitialAuthUsingPassword") and to_lower(o365.audit.ExtendedProperties.RequestType) rlike "(oauth.*||.*login.*)" and o365.audit.LogonError == "IdsLocked" and to_lower(o365.audit.UserId) != "not available" and o365.audit.Target.Type in ("0", "2", "6", "10") and source.`as`.organization.name != "MICROSOFT-CORP-MSN-as-BLOCK" | stats Esql_priv.o365_audit_UserId_count_distinct = count_distinct(to_lower(o365.audit.UserId)), Esql_priv.o365_audit_UserId_values = values(to_lower(o365.audit.UserId)), Esql.source_ip_values = values(source.ip), Esql.source_ip_count_distinct = count_distinct(source.ip), Esql.source_as_organization_name_values = values(source.`as`.organization.name), Esql.source_as_organization_name_count_distinct = count_distinct(source.`as`.organization.name), Esql.source_geo_country_name_values = values(source.geo.country_name), Esql.source_geo_country_name_count_distinct = count_distinct(source.geo.country_name), Esql.o365_audit_ExtendedProperties_RequestType_values = values(to_lower(o365.audit.ExtendedProperties.RequestType)), Esql.timestamp_first_seen = min(@timestamp), Esql.timestamp_last_seen = max(@timestamp), Esql.event_count = count(*) by Esql.time_window_date_trunc | eval Esql.event_duration_seconds = date_diff("seconds", Esql.timestamp_first_seen, Esql.timestamp_last_seen) | keep Esql.time_window_date_trunc, Esql_priv.o365_audit_UserId_count_distinct, Esql_priv.o365_audit_UserId_values, Esql.source_ip_values, Esql.source_ip_count_distinct, Esql.source_as_organization_name_values, Esql.source_as_organization_name_count_distinct, Esql.source_geo_country_name_values, Esql.source_geo_country_name_count_distinct, Esql.o365_audit_ExtendedProperties_RequestType_values, Esql.timestamp_first_seen, Esql.timestamp_last_seen, Esql.event_count, Esql.event_duration_seconds | where Esql_priv.o365_audit_UserId_count_distinct >= 10 and Esql.event_count >= 10 and Esql.event_duration_seconds <= 300

Install detection rules in Elastic Security

Detect M365 Identity User Account Lockouts in the Elastic Security detection engine by installing this rule into your Elastic Stack.

To setup this rule, check out the installation guide for Prebuilt Security Detection Rules(external, opens in a new tab or window).