Multiple Microsoft 365 User Account Lockouts in Short Time Window
editMultiple Microsoft 365 User Account Lockouts in Short Time Window
editDetects 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.
Rule type: esql
Rule indices: None
Severity: medium
Risk score: 47
Runs every: 5m
Searches indices from: now-9m (Date Math format, see also Additional look-back time
)
Maximum alerts per execution: 100
References:
- https://learn.microsoft.com/en-us/security/operations/incident-response-playbook-password-spray
- https://learn.microsoft.com/en-us/purview/audit-log-detailed-properties
- https://securityscorecard.com/research/massive-botnet-targets-m365-with-stealthy-password-spraying-attacks/
- https://github.com/0xZDH/Omnispray
- https://github.com/0xZDH/o365spray
Tags:
- Domain: Cloud
- Domain: SaaS
- Data Source: Microsoft 365
- Data Source: Microsoft 365 Audit Logs
- Use Case: Threat Detection
- Use Case: Identity and Access Audit
- Tactic: Credential Access
- Resources: Investigation Guide
Version: 1
Rule authors:
- Elastic
Rule license: Elastic License v2
Investigation guide
editTriage and Analysis
Investigating Multiple Microsoft 365 User Account Lockouts in Short Time Window
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.
This rule uses ES|QL aggregations and thus has dynamically generated fields. Correlation of the values in the alert document may need to be performed to the original sign-in and Graph events for further context.
Investigation Steps
-
Review the
user_id_list
: Are specific naming patterns targeted (e.g., admin, helpdesk)? -
Examine
ip_list
andsource_orgs
: Look for suspicious ISPs or hosting providers. -
Check
duration_seconds
: A very short window with a high lockout rate often indicates automation. - Confirm lockout policy thresholds with IAM or Entra ID admins. Did the policy trigger correctly?
-
Use the
first_seen
andlast_seen
values to pivot into related authentication or audit logs. - Correlate with any recent detection of password spraying or credential stuffing activity.
-
Review the
request_type
field to identify which authentication methods were used (e.g., OAuth, SAML, etc.). - Check for any successful logins from the same IP or ASN after the lockouts.
False Positive Analysis
- Automated systems with stale credentials may cause repeated failed logins.
- Legitimate bulk provisioning or scripted tests could unintentionally cause account lockouts.
- Red team exercises or penetration tests may resemble the same lockout pattern.
- Some organizations may have a high volume of lockouts due to user behavior or legacy systems.
Response Recommendations
- Notify affected users and confirm whether activity was expected or suspicious.
- Lock or reset credentials for impacted accounts.
- Block the source IP(s) or ASN temporarily using conditional access or firewall rules.
- Strengthen lockout and retry delay policies if necessary.
-
Review the originating application(s) involved via
request_types
.
Rule query
editFROM logs-o365.audit-* | MV_EXPAND event.category | EVAL time_window = DATE_TRUNC(5 minutes, @timestamp), user_id = TO_LOWER(o365.audit.UserId), ip = source.ip, login_error = o365.audit.LogonError, request_type = TO_LOWER(o365.audit.ExtendedProperties.RequestType), asn_org = source.`as`.organization.name, country = source.geo.country_name, event_time = @timestamp | WHERE event.dataset == "o365.audit" AND event.category == "authentication" AND event.provider IN ("AzureActiveDirectory", "Exchange") AND event.action IN ("UserLoginFailed", "PasswordLogonInitialAuthUsingPassword") AND request_type RLIKE "(oauth.*||.*login.*)" AND login_error == "IdsLocked" AND user_id != "not available" AND o365.audit.Target.Type IN ("0", "2", "6", "10") AND asn_org != "MICROSOFT-CORP-MSN-AS-BLOCK" | STATS unique_users = COUNT_DISTINCT(user_id), user_id_list = VALUES(user_id), ip_list = VALUES(ip), unique_ips = COUNT_DISTINCT(ip), source_orgs = VALUES(asn_org), countries = VALUES(country), unique_country_count = COUNT_DISTINCT(country), unique_asn_orgs = COUNT_DISTINCT(asn_org), request_types = VALUES(request_type), first_seen = MIN(event_time), last_seen = MAX(event_time), total_lockout_responses = COUNT() BY time_window | EVAL duration_seconds = DATE_DIFF("seconds", first_seen, last_seen) | KEEP time_window, unique_users, user_id_list, ip_list, unique_ips, source_orgs, countries, unique_country_count, unique_asn_orgs, request_types, first_seen, last_seen, total_lockout_responses, duration_seconds | WHERE unique_users >= 10 AND total_lockout_responses >= 10 AND duration_seconds <= 300
Framework: MITRE ATT&CKTM
-
Tactic:
- Name: Credential Access
- ID: TA0006
- Reference URL: https://attack.mitre.org/tactics/TA0006/
-
Technique:
- Name: Brute Force
- ID: T1110
- Reference URL: https://attack.mitre.org/techniques/T1110/