Potential Microsoft 365 User Account Brute Force
editPotential Microsoft 365 User Account Brute Force
editIdentifies brute-force authentication activity targeting Microsoft 365 user accounts using failed sign-in patterns that match password spraying, credential stuffing, or password guessing behavior. Adversaries may attempt brute-force authentication with credentials obtained from previous breaches, leaks, marketplaces or guessable passwords.
Rule type: esql
Rule indices: None
Severity: medium
Risk score: 47
Runs every: 10m
Searches indices from: now-60m (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: Identity and Access Audit
- Use Case: Threat Detection
- Tactic: Credential Access
- Resources: Investigation Guide
Version: 413
Rule authors:
- Elastic
- Willem D’Haese
- Austin Songer
Rule license: Elastic License v2
Investigation guide
editTriage and Analysis
Investigating Potential Microsoft 365 User Account Brute Force
Identifies brute-force authentication activity targeting Microsoft 365 user accounts using failed sign-in patterns that match password spraying, credential stuffing, or password guessing behavior. Adversaries may attempt brute-force authentication with credentials obtained from previous breaches, leaks, marketplaces or guessable passwords.
Possible investigation steps
-
Review
user_id_list
: Enumerates the user accounts targeted. Look for naming patterns or privilege levels (e.g., admins). -
Check
login_errors
: A consistent error such as"InvalidUserNameOrPassword"
confirms a spray-style attack using one or a few passwords. -
Examine
ip_list
andsource_orgs
: Determine if the traffic originates from a known corporate VPN, datacenter, or suspicious ASN like hosting providers or anonymizers. -
Review
countries
andunique_country_count
: Geographic anomalies (e.g., login attempts from unexpected regions) may indicate malicious automation. -
Validate
total_attempts
vsduration_seconds
: A high frequency of login attempts over a short period may suggest automation rather than manual logins. -
Cross-reference with successful logins: Pivot to surrounding sign-in logs (
azure.signinlogs
) or risk detections (identityprotection
) for any account that eventually succeeded. - Check for multi-factor challenges or bypasses: Determine if any of the accounts were protected or if the attack bypassed MFA.
False positive analysis
- IT administrators using automation tools (e.g., PowerShell) during account provisioning may trigger false positives if login attempts cluster.
- Penetration testing or red team simulations may resemble spray activity.
- Infrequent, low-volume login testing tools like ADFS testing scripts can exhibit similar patterns.
Response and remediation
- Initiate an internal incident ticket and inform the affected identity/IT team.
- Temporarily disable impacted user accounts if compromise is suspected.
- Investigate whether any login attempts succeeded after the spray window.
- Block the offending IPs or ASN temporarily via firewall or conditional access policies.
- Rotate passwords for all targeted accounts and audit for password reuse.
- Enforce or verify MFA is enabled for all user accounts.
- Consider deploying account lockout or progressive delay mechanisms if not already enabled.
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 login_error NOT IN ( "EntitlementGrantsNotFound", "UserStrongAuthEnrollmentRequired", "UserStrongAuthClientAuthNRequired", "InvalidReplyTo", "SsoArtifactExpiredDueToConditionalAccess", "PasswordResetRegistrationRequiredInterrupt", "SsoUserAccountNotFoundInResourceTenant", "UserStrongAuthExpired", "CmsiInterrupt" ) AND user_id != "not available" AND o365.audit.Target.Type IN ("0", "2", "6", "10") | STATS unique_users = COUNT_DISTINCT(user_id), user_id_list = VALUES(user_id), login_errors = VALUES(login_error), unique_login_errors = COUNT_DISTINCT(login_error), request_types = VALUES(request_type), 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), first_seen = MIN(event_time), last_seen = MAX(event_time), total_attempts = COUNT() BY time_window | EVAL duration_seconds = DATE_DIFF("seconds", first_seen, last_seen), bf_type = CASE( unique_users >= 15 AND unique_login_errors == 1 AND total_attempts >= 10 AND duration_seconds <= 1800, "password_spraying", unique_users >= 8 AND total_attempts >= 15 AND unique_login_errors <= 3 AND unique_ips <= 5 AND duration_seconds <= 600, "credential_stuffing", unique_users == 1 AND unique_login_errors == 1 AND total_attempts >= 20 AND duration_seconds <= 300, "password_guessing", "other" ) | KEEP time_window, unique_users, user_id_list, login_errors, unique_login_errors, request_types, ip_list, unique_ips, source_orgs, countries, unique_country_count, unique_asn_orgs, first_seen, last_seen, duration_seconds, total_attempts, bf_type | WHERE bf_type != "other"
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/
-
Sub-technique:
- Name: Password Guessing
- ID: T1110.001
- Reference URL: https://attack.mitre.org/techniques/T1110/001/
-
Sub-technique:
- Name: Password Spraying
- ID: T1110.003
- Reference URL: https://attack.mitre.org/techniques/T1110/003/
-
Sub-technique:
- Name: Credential Stuffing
- ID: T1110.004
- Reference URL: https://attack.mitre.org/techniques/T1110/004/