[New Rule] Multiple Microsoft 365 User Account Lockouts in Short Time Window (#4717)
* new rule 'Multiple Microsoft 365 User Account Lockouts in Short Time Window' * adjusted logic --------- Co-authored-by: shashank-elastic <91139415+shashank-elastic@users.noreply.github.com> Co-authored-by: Colson Wilhoit <48036388+DefSecSentinel@users.noreply.github.com>
This commit is contained in:
+143
@@ -0,0 +1,143 @@
|
||||
[metadata]
|
||||
creation_date = "2025/05/10"
|
||||
integration = ["o365"]
|
||||
maturity = "production"
|
||||
min_stack_version = "8.17.0"
|
||||
min_stack_comments = "Elastic ES|QL values aggregation is more performant in 8.16.5 and above."
|
||||
updated_date = "2025/05/10"
|
||||
|
||||
[rule]
|
||||
author = ["Elastic"]
|
||||
description = """
|
||||
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.
|
||||
"""
|
||||
from = "now-9m"
|
||||
language = "esql"
|
||||
license = "Elastic License v2"
|
||||
name = "Multiple Microsoft 365 User Account Lockouts in Short Time Window"
|
||||
note = """## Triage 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` and `source_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` and `last_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`.
|
||||
"""
|
||||
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",
|
||||
]
|
||||
risk_score = 47
|
||||
rule_id = "de67f85e-2d43-11f0-b8c9-f661ea17fbcc"
|
||||
severity = "medium"
|
||||
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",
|
||||
]
|
||||
timestamp_override = "event.ingested"
|
||||
type = "esql"
|
||||
|
||||
query = '''
|
||||
FROM 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
|
||||
'''
|
||||
|
||||
|
||||
[[rule.threat]]
|
||||
framework = "MITRE ATT&CK"
|
||||
[[rule.threat.technique]]
|
||||
id = "T1110"
|
||||
name = "Brute Force"
|
||||
reference = "https://attack.mitre.org/techniques/T1110/"
|
||||
|
||||
|
||||
[rule.threat.tactic]
|
||||
id = "TA0006"
|
||||
name = "Credential Access"
|
||||
reference = "https://attack.mitre.org/tactics/TA0006/"
|
||||
|
||||
Reference in New Issue
Block a user