Skip to content

Commit 9569aa4

Browse files
[New Rule] Microsoft Entra ID Excessive Account Lockouts Detected (#4782)
* new rule Microsoft Entra ID Exccessive Account Lockouts Detected * updating investigation guide * removed user agent exception * linted
1 parent c8d6e32 commit 9569aa4

File tree

1 file changed

+192
-0
lines changed

1 file changed

+192
-0
lines changed
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
[metadata]
2+
creation_date = "2026/06/06"
3+
integration = ["azure"]
4+
maturity = "production"
5+
min_stack_comments = "Elastic ES|QL values aggregation is more performant in 8.16.5 and above."
6+
min_stack_version = "8.17.0"
7+
updated_date = "2026/06/06"
8+
9+
[rule]
10+
author = ["Elastic"]
11+
description = """
12+
Identifies a high count of failed Microsoft Entra ID sign-in attempts as the result of the target user account being
13+
locked out. Adversaries may attempt to brute-force user accounts by repeatedly trying to authenticate with incorrect
14+
credentials, leading to account lockouts by Entra ID Smart Lockout policies.
15+
"""
16+
false_positives = [
17+
"""
18+
Automated processes that attempt to authenticate using expired credentials or have misconfigured authentication
19+
settings may lead to false positives.
20+
""",
21+
]
22+
from = "now-60m"
23+
interval = "15m"
24+
language = "esql"
25+
license = "Elastic License v2"
26+
name = "Microsoft Entra ID Exccessive Account Lockouts Detected"
27+
note = """## Triage and analysis
28+
29+
### Investigating Microsoft Entra ID Exccessive Account Lockouts Detected
30+
31+
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.
32+
33+
### Possible investigation steps
34+
35+
- Review `user_id_list` and `user_principal_name`: Check if targeted users include high-value accounts such as administrators, service principals, or shared inboxes.
36+
- Check `error_codes` and `result_description`: Validate that `50053` (account locked) is the consistent failure type. Messages indicating "malicious IP" activity suggest Microsoft’s backend flagged the source.
37+
- Analyze `ip_list` and `source_orgs`: Identify whether the activity originated from known malicious infrastructure (e.g., VPNs, botnets, or public cloud providers). In the example, traffic originates from `MASSCOM`, which should be validated.
38+
- Inspect `device_detail_browser` and `user_agent`: Clients like `"Python Requests"` indicate scripted automation rather than legitimate login attempts.
39+
- Evaluate `unique_users` vs. `total_attempts`: A high ratio suggests distributed attacks across multiple accounts, characteristic of password spraying.
40+
- Correlate `client_app_display_name` and `incoming_token_type`: PowerShell or unattended sign-in clients may be targeted for automation or legacy auth bypass.
41+
- Review `conditional_access_status` and `risk_state`: If Conditional Access was not applied and risk was not flagged, policy scope or coverage should be reviewed.
42+
- Validate time range (`first_seen`, `last_seen`): Determine whether the attack is a short burst or part of a longer campaign.
43+
44+
### False positive analysis
45+
46+
- Misconfigured clients, scripts, or services with outdated credentials may inadvertently cause lockouts.
47+
- Repeated lockouts from known internal IPs or during credential rotation windows could be benign.
48+
- Legacy applications without modern auth support may repeatedly fail and trigger Smart Lockout.
49+
- Specific known user agents (e.g., corporate service accounts).
50+
- Internal IPs or cloud-hosted automation with expected failure behavior.
51+
52+
### Response and remediation
53+
54+
- Investigate locked accounts immediately. Confirm if the account was successfully accessed prior to lockout.
55+
- Reset credentials for impacted users and enforce MFA before re-enabling accounts.
56+
- Block malicious IPs or ASN at the firewall, identity provider, or Conditional Access level.
57+
- Audit authentication methods in use, and enforce modern auth (OAuth, SAML) over legacy protocols.
58+
- Strengthen Conditional Access policies to reduce exposure from weak locations, apps, or clients.
59+
- Conduct credential hygiene audits to assess reuse and rotation for targeted accounts.
60+
"""
61+
references = [
62+
"https://www.microsoft.com/en-us/security/blog/2025/05/27/new-russia-affiliated-actor-void-blizzard-targets-critical-sectors-for-espionage/",
63+
"https://cloud.hacktricks.xyz/pentesting-cloud/azure-security/az-unauthenticated-enum-and-initial-entry/az-password-spraying",
64+
"https://learn.microsoft.com/en-us/security/operations/incident-response-playbook-password-spray",
65+
"https://www.sprocketsecurity.com/blog/exploring-modern-password-spraying",
66+
"https://learn.microsoft.com/en-us/purview/audit-log-detailed-properties",
67+
"https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes",
68+
"https://github.com/0xZDH/Omnispray",
69+
"https://github.com/0xZDH/o365spray",
70+
]
71+
risk_score = 73
72+
rule_id = "2d6f5332-42ea-11f0-b09a-f661ea17fbcd"
73+
severity = "high"
74+
tags = [
75+
"Domain: Cloud",
76+
"Domain: Identity",
77+
"Data Source: Azure",
78+
"Data Source: Entra ID",
79+
"Data Source: Entra ID Sign-in Logs",
80+
"Use Case: Identity and Access Audit",
81+
"Use Case: Threat Detection",
82+
"Tactic: Credential Access",
83+
"Resources: Investigation Guide",
84+
]
85+
timestamp_override = "event.ingested"
86+
type = "esql"
87+
88+
query = '''
89+
FROM logs-azure.signinlogs*
90+
91+
| EVAL
92+
time_window = DATE_TRUNC(30 minutes, @timestamp),
93+
user_id = TO_LOWER(azure.signinlogs.properties.user_principal_name),
94+
ip = source.ip,
95+
login_error = azure.signinlogs.result_description,
96+
error_code = azure.signinlogs.properties.status.error_code,
97+
request_type = TO_LOWER(azure.signinlogs.properties.incoming_token_type),
98+
app_name = TO_LOWER(azure.signinlogs.properties.app_display_name),
99+
asn_org = source.`as`.organization.name,
100+
country = source.geo.country_name,
101+
user_agent = user_agent.original,
102+
event_time = @timestamp
103+
104+
| WHERE event.dataset == "azure.signinlogs"
105+
AND event.category == "authentication"
106+
AND azure.signinlogs.category IN ("NonInteractiveUserSignInLogs", "SignInLogs")
107+
AND event.outcome == "failure"
108+
AND azure.signinlogs.properties.authentication_requirement == "singleFactorAuthentication"
109+
AND error_code == 50053
110+
AND user_id IS NOT NULL AND user_id != ""
111+
AND asn_org != "MICROSOFT-CORP-MSN-AS-BLOCK"
112+
113+
| STATS
114+
authentication_requirement = VALUES(azure.signinlogs.properties.authentication_requirement),
115+
client_app_id = VALUES(azure.signinlogs.properties.app_id),
116+
client_app_display_name = VALUES(azure.signinlogs.properties.app_display_name),
117+
target_resource_id = VALUES(azure.signinlogs.properties.resource_id),
118+
target_resource_display_name = VALUES(azure.signinlogs.properties.resource_display_name),
119+
conditional_access_status = VALUES(azure.signinlogs.properties.conditional_access_status),
120+
device_detail_browser = VALUES(azure.signinlogs.properties.device_detail.browser),
121+
device_detail_device_id = VALUES(azure.signinlogs.properties.device_detail.device_id),
122+
device_detail_operating_system = VALUES(azure.signinlogs.properties.device_detail.operating_system),
123+
incoming_token_type = VALUES(azure.signinlogs.properties.incoming_token_type),
124+
risk_state = VALUES(azure.signinlogs.properties.risk_state),
125+
session_id = VALUES(azure.signinlogs.properties.session_id),
126+
user_id = VALUES(azure.signinlogs.properties.user_id),
127+
user_principal_name = VALUES(azure.signinlogs.properties.user_principal_name),
128+
result_description = VALUES(azure.signinlogs.result_description),
129+
result_signature = VALUES(azure.signinlogs.result_signature),
130+
result_type = VALUES(azure.signinlogs.result_type),
131+
132+
unique_users = COUNT_DISTINCT(user_id),
133+
user_id_list = VALUES(user_id),
134+
login_errors = VALUES(login_error),
135+
unique_login_errors = COUNT_DISTINCT(login_error),
136+
error_codes = VALUES(error_code),
137+
unique_error_codes = COUNT_DISTINCT(error_code),
138+
request_types = VALUES(request_type),
139+
app_names = VALUES(app_name),
140+
ip_list = VALUES(ip),
141+
unique_ips = COUNT_DISTINCT(ip),
142+
source_orgs = VALUES(asn_org),
143+
countries = VALUES(country),
144+
unique_country_count = COUNT_DISTINCT(country),
145+
unique_asn_orgs = COUNT_DISTINCT(asn_org),
146+
first_seen = MIN(event_time),
147+
last_seen = MAX(event_time),
148+
total_attempts = COUNT()
149+
BY time_window
150+
| WHERE unique_users >= 15 AND total_attempts >= 20
151+
| KEEP
152+
time_window, total_attempts, first_seen, last_seen,
153+
unique_users, user_id_list, login_errors, unique_login_errors,
154+
unique_error_codes, error_codes, request_types, app_names,
155+
ip_list, unique_ips, source_orgs, countries,
156+
unique_country_count, unique_asn_orgs,
157+
authentication_requirement, client_app_id, client_app_display_name,
158+
target_resource_id, target_resource_display_name, conditional_access_status,
159+
device_detail_browser, device_detail_device_id, device_detail_operating_system,
160+
incoming_token_type, risk_state, session_id, user_id,
161+
user_principal_name, result_description, result_signature, result_type
162+
'''
163+
164+
165+
[[rule.threat]]
166+
framework = "MITRE ATT&CK"
167+
[[rule.threat.technique]]
168+
id = "T1110"
169+
name = "Brute Force"
170+
reference = "https://attack.mitre.org/techniques/T1110/"
171+
[[rule.threat.technique.subtechnique]]
172+
id = "T1110.001"
173+
name = "Password Guessing"
174+
reference = "https://attack.mitre.org/techniques/T1110/001/"
175+
176+
[[rule.threat.technique.subtechnique]]
177+
id = "T1110.003"
178+
name = "Password Spraying"
179+
reference = "https://attack.mitre.org/techniques/T1110/003/"
180+
181+
[[rule.threat.technique.subtechnique]]
182+
id = "T1110.004"
183+
name = "Credential Stuffing"
184+
reference = "https://attack.mitre.org/techniques/T1110/004/"
185+
186+
187+
188+
[rule.threat.tactic]
189+
id = "TA0006"
190+
name = "Credential Access"
191+
reference = "https://attack.mitre.org/tactics/TA0006/"
192+

0 commit comments

Comments
 (0)