cabf1c2a02
* Tuning azure and m365 rule names and file paths * addressing unit test failures * addressing unit test failures * Changed Frontdoor to Front Door * removed extra space in name * adjusted Microsoft 365 to M365 in rule name * Update rules/integrations/azure/credential_access_storage_account_key_regenerated.toml * Update rules/integrations/azure/defense_evasion_automation_runbook_deleted.toml * Update rules/integrations/azure/execution_automation_runbook_created_or_modified.toml * Update rules/integrations/azure/persistence_automation_account_created.toml * Update rules/integrations/azure/impact_key_vault_modified_by_unusual_user.toml * Update rules/integrations/azure/initial_access_entra_id_protection_sign_in_risk_detected.toml * Update rules/integrations/azure/initial_access_entra_id_protection_user_risk_detected.toml * Update rules/integrations/azure/persistence_automation_webhook_created.toml * Update rules/integrations/azure/persistence_entra_id_global_administrator_role_assigned.toml * Update rules/integrations/azure/persistence_entra_id_mfa_disabled_for_user.toml * Update rules/integrations/azure/persistence_event_hub_created_or_updated.toml * Update rules/integrations/o365/collection_onedrive_excessive_file_downloads.toml * Update rules/integrations/o365/initial_access_defender_for_m365_threat_intelligence_signal.toml * Update rules/integrations/azure/credential_access_entra_id_signin_brute_force_microsoft_365.toml Co-authored-by: Isai <59296946+imays11@users.noreply.github.com> * Update rules/integrations/azure/credential_access_entra_id_signin_brute_force_microsoft_365.toml Co-authored-by: Isai <59296946+imays11@users.noreply.github.com> * Update rules/integrations/o365/credential_access_entra_id_potential_user_account_brute_force.toml Co-authored-by: Isai <59296946+imays11@users.noreply.github.com> * Update rules/integrations/o365/credential_access_entra_id_potential_user_account_brute_force.toml Co-authored-by: Isai <59296946+imays11@users.noreply.github.com> * fixed additional rule names * Update rule dates and investigation guide headers - Set updated_date to 2025/12/10 for all modified rules - Fix investigation guide headers to match actual rule names - Ensures compliance with test_rule_change_has_updated_date - Ensures compliance with test_investigation_guide_uses_rule_name 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * changed kibana alert rule name to rule ID --------- Co-authored-by: Isai <59296946+imays11@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
156 lines
6.4 KiB
TOML
156 lines
6.4 KiB
TOML
[metadata]
|
|
creation_date = "2025/05/10"
|
|
integration = ["o365"]
|
|
maturity = "production"
|
|
updated_date = "2025/12/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"
|
|
interval = "8m"
|
|
language = "esql"
|
|
license = "Elastic License v2"
|
|
name = "M365 Identity User Account Lockouts"
|
|
note = """## Triage and Analysis
|
|
|
|
### Investigating M365 Identity User Account Lockouts
|
|
|
|
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 ESQL 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
|
|
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
|
|
'''
|
|
|
|
|
|
[[rule.threat]]
|
|
framework = "MITRE ATT&CK"
|
|
[[rule.threat.technique]]
|
|
id = "T1110"
|
|
name = "Brute Force"
|
|
reference = "https://attack.mitre.org/techniques/T1110/"
|
|
[[rule.threat.technique.subtechnique]]
|
|
id = "T1110.001"
|
|
name = "Password Guessing"
|
|
reference = "https://attack.mitre.org/techniques/T1110/001/"
|
|
|
|
[[rule.threat.technique.subtechnique]]
|
|
id = "T1110.003"
|
|
name = "Password Spraying"
|
|
reference = "https://attack.mitre.org/techniques/T1110/003/"
|
|
|
|
[[rule.threat.technique.subtechnique]]
|
|
id = "T1110.004"
|
|
name = "Credential Stuffing"
|
|
reference = "https://attack.mitre.org/techniques/T1110/004/"
|
|
|
|
|
|
|
|
[rule.threat.tactic]
|
|
id = "TA0006"
|
|
name = "Credential Access"
|
|
reference = "https://attack.mitre.org/tactics/TA0006/"
|
|
|