Multiple Microsoft 365 User Account Lockouts in Short Time Window

Last updated a month ago on 2025-07-16
Created 3 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)(opens in a new tab or window)

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

Definition

Integration Pack
Prebuilt Security Detection Rules
Related Integrations

o365(opens in a new tab or window)

Query
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 Multiple Microsoft 365 User Account Lockouts in Short Time Window 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(opens in a new tab or window).