Potential Okta Credential Stuffing (Single Source)
Detects potential credential stuffing attacks where a single source IP attempts authentication against many Okta user accounts with minimal attempts per user, indicating the use of breached credential lists.
Elastic rule (View on GitHub)
1[metadata]
2creation_date = "2024/06/17"
3integration = ["okta"]
4maturity = "production"
5updated_date = "2026/02/19"
6
7[rule]
8author = ["Elastic"]
9description = """
10Detects potential credential stuffing attacks where a single source IP attempts authentication against many Okta
11user accounts with minimal attempts per user, indicating the use of breached credential lists.
12"""
13false_positives = [
14 "Corporate proxy or VPN exit nodes may aggregate traffic from multiple legitimate users.",
15 "Shared systems such as kiosks or conference room computers may have multiple users authenticating.",
16]
17from = "now-15m"
18language = "esql"
19license = "Elastic License v2"
20name = "Potential Okta Credential Stuffing (Single Source)"
21note = """## Triage and analysis
22
23### Investigating Potential Okta Credential Stuffing (Single Source)
24
25This rule identifies a single source IP attempting authentication against many user accounts with minimal attempts per user. This pattern indicates credential stuffing where attackers rapidly test breached username and password pairs.
26
27#### Possible investigation steps
28- Identify the source IP and determine if it belongs to known proxy, VPN, or cloud infrastructure.
29- Review the list of targeted user accounts and check if any authentications succeeded.
30- Examine the user agent strings for signs of automation or scripting tools.
31- Check if Okta flagged the source as a known threat or proxy.
32- Determine if any targeted accounts have elevated privileges or access to sensitive systems.
33- Review the geographic location and ASN of the source IP for anomalies.
34
35### False positive analysis
36- Corporate proxies or VPN exit nodes may aggregate traffic from multiple legitimate users.
37- Shared systems such as kiosks or conference room computers may have multiple users authenticating.
38- Legitimate SSO integrations may generate multiple authentication attempts from a single source.
39
40### Response and remediation
41- If attack is confirmed, block the source IP at the network perimeter.
42- Reset passwords for any accounts that may have been compromised.
43- Enable or strengthen MFA for targeted accounts.
44- Review Okta sign-on policies to add additional friction for suspicious authentication patterns.
45- If this is a known legitimate source, consider adding an exception for the IP or ASN.
46"""
47references = [
48 "https://support.okta.com/help/s/article/Troubleshooting-Distributed-Brute-Force-andor-Password-Spray-attacks-in-Okta",
49 "https://www.okta.com/identity-101/brute-force/",
50 "https://support.okta.com/help/s/article/How-does-the-Device-Token-work?language=en_US",
51 "https://developer.okta.com/docs/reference/api/event-types/",
52 "https://www.elastic.co/security-labs/testing-okta-visibility-and-detection-dorothy",
53 "https://sec.okta.com/articles/2023/08/cross-tenant-impersonation-prevention-and-detection",
54 "https://www.okta.com/resources/whitepaper-how-adaptive-mfa-can-help-in-mitigating-brute-force-attacks/",
55 "https://www.elastic.co/security-labs/monitoring-okta-threats-with-elastic-security",
56 "https://www.elastic.co/security-labs/starter-guide-to-understanding-okta",
57]
58risk_score = 47
59rule_id = "94e734c0-2cda-11ef-84e1-f661ea17fbce"
60setup = "The Okta Fleet integration, Filebeat module, or similarly structured data is required to be compatible with this rule."
61severity = "medium"
62tags = [
63 "Domain: Identity",
64 "Use Case: Identity and Access Audit",
65 "Data Source: Okta",
66 "Data Source: Okta System Logs",
67 "Tactic: Credential Access",
68 "Resources: Investigation Guide",
69]
70timestamp_override = "event.ingested"
71type = "esql"
72
73query = '''
74FROM logs-okta.system-* METADATA _id, _version, _index
75| WHERE
76 event.dataset == "okta.system"
77 AND (event.action LIKE "user.authentication.*" OR event.action == "user.session.start")
78 AND okta.outcome.reason IN ("INVALID_CREDENTIALS", "LOCKED_OUT")
79 AND okta.actor.alternate_id IS NOT NULL
80// Build user-source context as JSON for enrichment
81| EVAL Esql.user_source_info = CONCAT(
82 "{\"user\":\"", okta.actor.alternate_id,
83 "\",\"ip\":\"", COALESCE(okta.client.ip::STRING, "unknown"),
84 "\",\"user_agent\":\"", COALESCE(okta.client.user_agent.raw_user_agent, "unknown"), "\"}"
85 )
86// FIRST STATS: Aggregate by (IP, user) to get per-user attempt counts
87// This prevents skew from outlier users with many attempts
88| STATS
89 Esql.user_attempts = COUNT(*),
90 Esql.user_dt_hashes = COUNT_DISTINCT(okta.debug_context.debug_data.dt_hash),
91 Esql.user_source_info = VALUES(Esql.user_source_info),
92 Esql.user_agents_per_user = VALUES(okta.client.user_agent.raw_user_agent),
93 Esql.devices_per_user = VALUES(okta.client.device),
94 Esql.is_proxy = VALUES(okta.security_context.is_proxy),
95 Esql.geo_country = VALUES(client.geo.country_name),
96 Esql.geo_city = VALUES(client.geo.city_name),
97 Esql.asn_number = VALUES(source.as.number),
98 Esql.asn_org = VALUES(source.as.organization.name),
99 Esql.threat_suspected = VALUES(okta.debug_context.debug_data.threat_suspected),
100 Esql.risk_level = VALUES(okta.debug_context.debug_data.risk_level),
101 Esql.risk_reasons = VALUES(okta.debug_context.debug_data.risk_reasons),
102 Esql.event_actions = VALUES(event.action),
103 Esql.first_seen_user = MIN(@timestamp),
104 Esql.last_seen_user = MAX(@timestamp)
105 BY okta.client.ip, okta.actor.alternate_id
106// SECOND STATS: Aggregate by IP to detect credential stuffing pattern
107// Now we can accurately measure the distribution of attempts across users
108| STATS
109 Esql.unique_users = COUNT(*),
110 Esql.total_attempts = SUM(Esql.user_attempts),
111 Esql.max_attempts_per_user = MAX(Esql.user_attempts),
112 Esql.min_attempts_per_user = MIN(Esql.user_attempts),
113 Esql.avg_attempts_per_user = AVG(Esql.user_attempts),
114 Esql.users_with_single_attempt = SUM(CASE(Esql.user_attempts == 1, 1, 0)),
115 Esql.users_with_few_attempts = SUM(CASE(Esql.user_attempts <= 2, 1, 0)),
116 Esql.first_seen = MIN(Esql.first_seen_user),
117 Esql.last_seen = MAX(Esql.last_seen_user),
118 Esql.target_users = VALUES(okta.actor.alternate_id),
119 Esql.user_source_mapping = VALUES(Esql.user_source_info),
120 Esql.event_action_values = VALUES(Esql.event_actions),
121 Esql.user_agent_values = VALUES(Esql.user_agents_per_user),
122 Esql.device_values = VALUES(Esql.devices_per_user),
123 Esql.is_proxy_values = VALUES(Esql.is_proxy),
124 Esql.geo_country_values = VALUES(Esql.geo_country),
125 Esql.geo_city_values = VALUES(Esql.geo_city),
126 Esql.source_asn_values = VALUES(Esql.asn_number),
127 Esql.source_asn_org_values = VALUES(Esql.asn_org),
128 Esql.threat_suspected_values = VALUES(Esql.threat_suspected),
129 Esql.risk_level_values = VALUES(Esql.risk_level),
130 Esql.risk_reasons_values = VALUES(Esql.risk_reasons)
131 BY okta.client.ip
132// Calculate stuffing signature: most users should have very few attempts
133| EVAL Esql.pct_users_few_attempts = Esql.users_with_few_attempts * 100.0 / Esql.unique_users
134// Credential stuffing: many users, most with 1-2 attempts each, low max per user
135// Stacked stats gives us accurate per-user distribution instead of skewed averages
136| WHERE
137 Esql.total_attempts >= 25
138 AND Esql.unique_users >= 15
139 AND Esql.max_attempts_per_user <= 2
140 AND Esql.pct_users_few_attempts >= 80.0
141| SORT Esql.unique_users DESC
142| KEEP Esql.*, okta.client.ip
143'''
144
145
146[[rule.threat]]
147framework = "MITRE ATT&CK"
148[[rule.threat.technique]]
149id = "T1110"
150name = "Brute Force"
151reference = "https://attack.mitre.org/techniques/T1110/"
152[[rule.threat.technique.subtechnique]]
153id = "T1110.004"
154name = "Credential Stuffing"
155reference = "https://attack.mitre.org/techniques/T1110/004/"
156
157
158[rule.threat.tactic]
159id = "TA0006"
160name = "Credential Access"
161reference = "https://attack.mitre.org/tactics/TA0006/"
Triage and analysis
Investigating Potential Okta Credential Stuffing (Single Source)
This rule identifies a single source IP attempting authentication against many user accounts with minimal attempts per user. This pattern indicates credential stuffing where attackers rapidly test breached username and password pairs.
Possible investigation steps
- Identify the source IP and determine if it belongs to known proxy, VPN, or cloud infrastructure.
- Review the list of targeted user accounts and check if any authentications succeeded.
- Examine the user agent strings for signs of automation or scripting tools.
- Check if Okta flagged the source as a known threat or proxy.
- Determine if any targeted accounts have elevated privileges or access to sensitive systems.
- Review the geographic location and ASN of the source IP for anomalies.
False positive analysis
- Corporate proxies or VPN exit nodes may aggregate traffic from multiple legitimate users.
- Shared systems such as kiosks or conference room computers may have multiple users authenticating.
- Legitimate SSO integrations may generate multiple authentication attempts from a single source.
Response and remediation
- If attack is confirmed, block the source IP at the network perimeter.
- Reset passwords for any accounts that may have been compromised.
- Enable or strengthen MFA for targeted accounts.
- Review Okta sign-on policies to add additional friction for suspicious authentication patterns.
- If this is a known legitimate source, consider adding an exception for the IP or ASN.
References
Related rules
- Okta Successful Login After Credential Attack
- Potential Okta Brute Force (Multi-Source)
- Potential Okta Password Spray (Multi-Source)
- Potential Okta Password Spray (Single Source)
- Okta AiTM Session Cookie Replay