Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
creation_date = "2025/07/01"
integration = ["azure"]
maturity = "production"
min_stack_version = "9.0.0"
min_stack_comments = "Bug fix in threshold rules."
updated_date = "2025/12/10"
min_stack_version = "9.0.0"
updated_date = "2025/12/18"

[rule]
author = ["Elastic"]
Expand All @@ -19,47 +19,52 @@ false_positives = [
settings may lead to false positives.
""",
]
from = "now-60m"
from = "now-30m"
index = ["filebeat-*", "logs-azure.signinlogs-*"]
interval = "30m"
interval = "15m"
language = "kuery"
license = "Elastic License v2"
name = "Entra ID Excessive Account Lockouts Detected"
note = """## Triage and analysis

### Investigating Entra ID Excessive 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.
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. Error code `50053` indicates Entra ID Smart Lockout, which is IP-based and tracks "unfamiliar" vs "familiar" locations. Familiar IPs have a higher threshold before lockout, so if lockouts are occurring, the source IP was unfamiliar to those accounts.

### Possible investigation steps

Please note this is as threshold rule that aggregates multiple account lockouts over a specified time window. To properly investigate, pivot into the individual sign-in log events that contributed to the threshold being met.

- Review users impacted by pivoting searching for `user.name` in events where `azure.signinlogs.properties.status.error_code` is `50053`.
- Analyze source addresses associated with these lockouts. Identify whether the activity originated from known malicious infrastructure (e.g., VPNs, botnets, or public cloud providers).
- Inspect the user-agents involved in these account lockouts. Clients like `Python Requests` indicate scripted automation rather than legitimate login attempts. ROPC agents may suggest brute-forcing against legacy auth.
- A high ratio suggests distributed attacks across multiple accounts, characteristic of password spraying.
- Correlate client apps associated such as PowerShell or unattended sign-in clients may be targeted for automation or legacy auth bypass.
- Review conditional access state or risk state of the user involved. If Conditional Access was not applied and risk was not flagged, policy scope or coverage should be reviewed.
- Check for any successful sign-ins for the affected users around the same time frame to determine if any accounts were compromised prior to lockout.
- This is a threshold rule that aggregates events, so the alert document won't contain individual sign-in attempts. Pivot to the raw logs in Discover or Timeline using: `event.dataset: "azure.signinlogs" and azure.signinlogs.properties.status.error_code: 50053`. Match the time range to the alert window (rule lookback is 15 minutes).
- Add relevant columns: `@timestamp`, `user.name`, `source.ip`, `user_agent.original`, `azure.signinlogs.properties.app_display_name`, `azure.signinlogs.properties.client_app_used`, `source.as.organization.name`, `geo.country_iso_code`, `azure.signinlogs.properties.authentication_details`.
- Aggregate by `source.ip` and `user.name` to identify the attack pattern: password spray (single/small set of IPs targeting many accounts), credential stuffing (many source IPs, may target specific users with leaked credentials), targeted brute force (focused on single high-value account), or misconfigured service (single source IP with consistent user-agent matching legitimate application).
- Analyze `user_agent.original` for indicators of automation. Values like `curl/*`, `python-requests/*`, `Go-http-client/*`, `axios/*` indicate scripted attacks. `BAV2ROPC` indicates Resource Owner Password Credential flow (legacy auth), commonly abused by spray tools like o365spray and MSOLSpray.
- Review `azure.signinlogs.properties.client_app_used`. `Other clients` indicates legacy auth (IMAP, SMTP, POP) which is high-risk. `Exchange ActiveSync` is legacy auth often targeted.
- Check `azure.signinlogs.properties.authentication_protocol` for `ropc` (Resource Owner Password Credential), a legacy flow where credentials are sent directly with no MFA interruption possible.
- Review `source.as.organization.name`. Cloud hosting providers (DigitalOcean, Vultr, Linode, AWS, Azure, GCP) or VPN providers (NordVPN, ExpressVPN, Mullvad) are high-risk as attackers use this infrastructure.
- Check `azure.signinlogs.properties.conditional_access_status`. `notApplied` means no CA policy evaluated, indicating a coverage gap. `failure` means CA policy blocked the sign-in.
- Determine if any accounts were compromised. Query for successful sign-ins from attacking infrastructure: `event.dataset: "azure.signinlogs" and event.outcome: "success" and source.ip: "<attacker_ip>"`. Also query for successful sign-ins for affected users from any source during the attack window.
- Check for MFA bypass: `event.dataset: "azure.signinlogs" and event.outcome: "success" and azure.signinlogs.properties.authentication_requirement: "singleFactorAuthentication" and source.ip: "<attacker_ip>"`
- Review Entra ID Protection risk events for affected users by checking `risk_level_during_signin`, `risk_level_aggregated`, and `risk_state` for values other than `none`.
- Check for username enumeration preceding the spray. Error code `50034` (user not found) indicates enumeration: `event.dataset: "azure.signinlogs" and azure.signinlogs.properties.status.error_code: 50034 and source.ip: "<attacker_ip>"`
- Query for other error codes from the same source: `50126` (invalid password), `50034` (user not found), `50057` (account disabled), `50055` (password expired), `53003` (blocked by CA), `50158` (MFA challenge not satisfied). A mix of `50126` and `50053` confirms password guessing.

### 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.
- Lockouts 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.
- Specific known user agents from corporate service accounts.
- Cloud-hosted automation with expected failure behavior.
- This rule can be customized. Adjust the threshold and/or interval/lookback window based on baselining efforts. Specific UPNs can be excluded if they generate known false positives.

### 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.
- If false positives are identified, create exceptions for known benign sources, users or user agents to reduce noise.
- Confirm no successful authentications from attacking source.
- Query for successful sign-ins to affected users from any source during the attack window.
- If compromise confirmed: disable account, revoke refresh tokens (`Revoke-AzureADUserAllRefreshToken` or via portal), reset password, require MFA re-registration.
- Block source IP/ASN via Conditional Access named locations.
- If legacy auth was used: create CA policy blocking legacy authentication.
- If MFA wasn't required: review CA policy coverage for gaps.
- Document affected users for credential reset and monitoring.
"""
references = [
"https://www.microsoft.com/en-us/security/blog/2025/05/27/new-russia-affiliated-actor-void-blizzard-targets-critical-sectors-for-espionage/",
Expand Down Expand Up @@ -96,16 +101,9 @@ event.dataset: "azure.signinlogs" and event.category: "authentication"
and azure.signinlogs.properties.status.error_code: 50053
and azure.signinlogs.properties.user_principal_name: (* and not "")
and not source.as.organization.name: "MICROSOFT-CORP-MSN-as-BLOCK"
and not source.ip: ("10.0.0.0/8" or "172.16.0.0/12" or "192.168.0.0/16" or "127.0.0.0/8" or "::1/128" or "fd00::/8")
'''

[rule.threshold]
field = []
value = 20

[[rule.threshold.cardinality]]
field = "user.name"
value = 15


[[rule.threat]]
framework = "MITRE ATT&CK"
Expand Down Expand Up @@ -135,3 +133,9 @@ id = "TA0006"
name = "Credential Access"
reference = "https://attack.mitre.org/tactics/TA0006/"

[rule.threshold]
field = []
value = 30
[[rule.threshold.cardinality]]
field = "user.name"
value = 25
Loading