Microsoft Entra ID Exccessive Account Lockouts Detected
editMicrosoft Entra ID Exccessive Account Lockouts Detected
editIdentifies a high count of failed Microsoft Entra ID sign-in attempts as the result of the target user account being locked out. Adversaries may attempt to brute-force user accounts by repeatedly trying to authenticate with incorrect credentials, leading to account lockouts by Entra ID Smart Lockout policies.
Rule type: esql
Rule indices: None
Severity: high
Risk score: 73
Runs every: 15m
Searches indices from: now-60m (Date Math format, see also Additional look-back time
)
Maximum alerts per execution: 100
References:
- https://www.microsoft.com/en-us/security/blog/2025/05/27/new-russia-affiliated-actor-void-blizzard-targets-critical-sectors-for-espionage/
- https://cloud.hacktricks.xyz/pentesting-cloud/azure-security/az-unauthenticated-enum-and-initial-entry/az-password-spraying
- https://learn.microsoft.com/en-us/security/operations/incident-response-playbook-password-spray
- https://www.sprocketsecurity.com/blog/exploring-modern-password-spraying
- https://learn.microsoft.com/en-us/purview/audit-log-detailed-properties
- https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes
- https://github.com/0xZDH/Omnispray
- https://github.com/0xZDH/o365spray
Tags:
- Domain: Cloud
- Domain: Identity
- Data Source: Azure
- Data Source: Entra ID
- Data Source: Entra ID Sign-in Logs
- Use Case: Identity and Access Audit
- Use Case: Threat Detection
- Tactic: Credential Access
- Resources: Investigation Guide
Version: 1
Rule authors:
- Elastic
Rule license: Elastic License v2
Investigation guide
editTriage and analysis
Investigating Microsoft Entra ID Exccessive Account Lockouts Detected
This rule detects a high number of sign-in failures due to account lockouts (error code 50053
) in Microsoft Entra ID sign-in logs. These lockouts are typically caused by repeated authentication failures, often as a result of brute-force tactics such as password spraying, credential stuffing, or automated guessing. This detection is time-bucketed and aggregates attempts to identify bursts or coordinated campaigns targeting multiple users.
Possible investigation steps
-
Review
user_id_list
anduser_principal_name
: Check if targeted users include high-value accounts such as administrators, service principals, or shared inboxes. -
Check
error_codes
andresult_description
: Validate that50053
(account locked) is the consistent failure type. Messages indicating "malicious IP" activity suggest Microsoft’s backend flagged the source. -
Analyze
ip_list
andsource_orgs
: Identify whether the activity originated from known malicious infrastructure (e.g., VPNs, botnets, or public cloud providers). In the example, traffic originates fromMASSCOM
, which should be validated. -
Inspect
device_detail_browser
anduser_agent
: Clients like"Python Requests"
indicate scripted automation rather than legitimate login attempts. -
Evaluate
unique_users
vs.total_attempts
: A high ratio suggests distributed attacks across multiple accounts, characteristic of password spraying. -
Correlate
client_app_display_name
andincoming_token_type
: PowerShell or unattended sign-in clients may be targeted for automation or legacy auth bypass. -
Review
conditional_access_status
andrisk_state
: If Conditional Access was not applied and risk was not flagged, policy scope or coverage should be reviewed. -
Validate time range (
first_seen
,last_seen
): Determine whether the attack is a short burst or part of a longer campaign.
False positive analysis
- Misconfigured clients, scripts, or services with outdated credentials may inadvertently cause lockouts.
- Repeated lockouts from known internal IPs or during credential rotation windows could be benign.
- Legacy applications without modern auth support may repeatedly fail and trigger Smart Lockout.
- Specific known user agents (e.g., corporate service accounts).
- Internal IPs or cloud-hosted automation with expected failure behavior.
Response and remediation
- Investigate locked accounts immediately. Confirm if the account was successfully accessed prior to lockout.
- Reset credentials for impacted users and enforce MFA before re-enabling accounts.
- Block malicious IPs or ASN at the firewall, identity provider, or Conditional Access level.
- Audit authentication methods in use, and enforce modern auth (OAuth, SAML) over legacy protocols.
- Strengthen Conditional Access policies to reduce exposure from weak locations, apps, or clients.
- Conduct credential hygiene audits to assess reuse and rotation for targeted accounts.
Rule query
editFROM logs-azure.signinlogs* | EVAL time_window = DATE_TRUNC(30 minutes, @timestamp), user_id = TO_LOWER(azure.signinlogs.properties.user_principal_name), ip = source.ip, login_error = azure.signinlogs.result_description, error_code = azure.signinlogs.properties.status.error_code, request_type = TO_LOWER(azure.signinlogs.properties.incoming_token_type), app_name = TO_LOWER(azure.signinlogs.properties.app_display_name), asn_org = source.`as`.organization.name, country = source.geo.country_name, user_agent = user_agent.original, event_time = @timestamp | WHERE event.dataset == "azure.signinlogs" AND event.category == "authentication" AND azure.signinlogs.category IN ("NonInteractiveUserSignInLogs", "SignInLogs") AND event.outcome == "failure" AND azure.signinlogs.properties.authentication_requirement == "singleFactorAuthentication" AND error_code == 50053 AND user_id IS NOT NULL AND user_id != "" AND asn_org != "MICROSOFT-CORP-MSN-AS-BLOCK" | STATS authentication_requirement = VALUES(azure.signinlogs.properties.authentication_requirement), client_app_id = VALUES(azure.signinlogs.properties.app_id), client_app_display_name = VALUES(azure.signinlogs.properties.app_display_name), target_resource_id = VALUES(azure.signinlogs.properties.resource_id), target_resource_display_name = VALUES(azure.signinlogs.properties.resource_display_name), conditional_access_status = VALUES(azure.signinlogs.properties.conditional_access_status), device_detail_browser = VALUES(azure.signinlogs.properties.device_detail.browser), device_detail_device_id = VALUES(azure.signinlogs.properties.device_detail.device_id), device_detail_operating_system = VALUES(azure.signinlogs.properties.device_detail.operating_system), incoming_token_type = VALUES(azure.signinlogs.properties.incoming_token_type), risk_state = VALUES(azure.signinlogs.properties.risk_state), session_id = VALUES(azure.signinlogs.properties.session_id), user_id = VALUES(azure.signinlogs.properties.user_id), user_principal_name = VALUES(azure.signinlogs.properties.user_principal_name), result_description = VALUES(azure.signinlogs.result_description), result_signature = VALUES(azure.signinlogs.result_signature), result_type = VALUES(azure.signinlogs.result_type), unique_users = COUNT_DISTINCT(user_id), user_id_list = VALUES(user_id), login_errors = VALUES(login_error), unique_login_errors = COUNT_DISTINCT(login_error), error_codes = VALUES(error_code), unique_error_codes = COUNT_DISTINCT(error_code), request_types = VALUES(request_type), app_names = VALUES(app_name), 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 | WHERE unique_users >= 15 AND total_attempts >= 20 | KEEP time_window, total_attempts, first_seen, last_seen, unique_users, user_id_list, login_errors, unique_login_errors, unique_error_codes, error_codes, request_types, app_names, ip_list, unique_ips, source_orgs, countries, unique_country_count, unique_asn_orgs, authentication_requirement, client_app_id, client_app_display_name, target_resource_id, target_resource_display_name, conditional_access_status, device_detail_browser, device_detail_device_id, device_detail_operating_system, incoming_token_type, risk_state, session_id, user_id, user_principal_name, result_description, result_signature, result_type
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/