Potential Okta Password Spray (Single Source)

Detects potential password spray attacks where a single source IP attempts authentication against multiple Okta user accounts with repeated attempts per user, indicating common password guessing paced to avoid lockouts.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2020/07/16"
  3integration = ["okta"]
  4maturity = "production"
  5updated_date = "2026/09/18"
  6
  7[rule]
  8author = ["Elastic"]
  9description = """
 10Detects potential password spray attacks where a single source IP attempts authentication against multiple Okta
 11user accounts with repeated attempts per user, indicating common password guessing paced to avoid lockouts.
 12"""
 13false_positives = [
 14    "Corporate proxy or VPN exit nodes may aggregate traffic from multiple legitimate users with login issues.",
 15    "Automated processes or misconfigured applications retrying authentication may trigger this rule.",
 16]
 17from = "now-1h"
 18interval = "15m"
 19language = "esql"
 20license = "Elastic License v2"
 21name = "Potential Okta Password Spray (Single Source)"
 22note = """## Triage and analysis
 23
 24### Investigating Potential Okta Password Spray (Single Source)
 25
 26This rule identifies a single source IP attempting authentication against multiple user accounts with repeated attempts per user over time. This pattern indicates password spraying where attackers try common passwords while pacing attempts to avoid lockouts.
 27
 28#### Possible investigation steps
 29- Identify the source IP and determine if it belongs to known proxy, VPN, or cloud infrastructure.
 30- Review the list of targeted user accounts and check if any authentications succeeded.
 31- Analyze the timing of attempts to determine if they are paced to avoid lockout thresholds.
 32- Check if Okta flagged the source as a known threat or proxy.
 33- Examine user agent strings for signs of automation or consistent tooling across attempts.
 34- Review the geographic location and ASN of the source IP for anomalies.
 35
 36### False positive analysis
 37- Corporate proxies or VPN exit nodes may aggregate traffic from multiple legitimate users with login issues.
 38- Automated processes or misconfigured applications retrying authentication may trigger this rule.
 39- Password rotation events may cause legitimate widespread authentication failures.
 40
 41### Response and remediation
 42- If attack is confirmed, block the source IP at the network perimeter.
 43- Notify targeted users and enforce password resets for accounts that may have been compromised.
 44- Enable or strengthen MFA for targeted accounts.
 45- Consider implementing CAPTCHA or additional friction for suspicious authentication patterns.
 46- Review Okta sign-on policies to ensure lockout thresholds are appropriately configured.
 47"""
 48references = [
 49    "https://support.okta.com/help/s/article/Troubleshooting-Distributed-Brute-Force-andor-Password-Spray-attacks-in-Okta",
 50    "https://www.okta.com/identity-101/brute-force/",
 51    "https://developer.okta.com/docs/reference/api/system-log/",
 52    "https://developer.okta.com/docs/reference/api/event-types/",
 53    "https://www.elastic.co/security-labs/testing-okta-visibility-and-detection-dorothy",
 54    "https://www.elastic.co/security-labs/monitoring-okta-threats-with-elastic-security",
 55    "https://www.elastic.co/security-labs/starter-guide-to-understanding-okta",
 56]
 57risk_score = 47
 58rule_id = "42bf698b-4738-445b-8231-c834ddefd8a0"
 59severity = "medium"
 60tags = [
 61    "Domain: Identity",
 62    "Use Case: Identity and Access Audit",
 63    "Tactic: Credential Access",
 64    "Data Source: Okta",
 65    "Data Source: Okta System Logs",
 66    "Resources: Investigation Guide",
 67    "Noise: Medium",
 68    "Performance: Normal",
 69    "Profile: Recommended",
 70    "Threat: Brute Force",
 71    "Rule Type: ES|QL",
 72    "Platform: Okta",
 73]
 74timestamp_override = "event.ingested"
 75type = "esql"
 76
 77query = '''
 78FROM logs-okta.system-* METADATA _id, _version, _index
 79| WHERE
 80    data_stream.dataset == "okta.system"
 81    AND (event.action LIKE "user.authentication.*" OR event.action == "user.session.start")
 82    AND okta.outcome.reason IN ("INVALID_CREDENTIALS", "LOCKED_OUT")
 83    AND okta.actor.alternate_id IS NOT NULL
 84// Build user-source context as JSON for enrichment
 85| EVAL Esql.user_source_info = CONCAT(
 86    "{\"user\":\"", okta.actor.alternate_id,
 87    "\",\"ip\":\"", COALESCE(okta.client.ip::STRING, "unknown"),
 88    "\",\"user_agent\":\"", COALESCE(okta.client.user_agent.raw_user_agent, "unknown"), "\"}"
 89  )
 90// FIRST STATS: Aggregate by (IP, user) to get per-user attempt counts
 91// This prevents skew from outlier users with many attempts
 92| STATS
 93    Esql.user_attempts = COUNT(*),
 94    Esql.user_source_info = VALUES(Esql.user_source_info),
 95    Esql.user_agents_per_user = VALUES(okta.client.user_agent.raw_user_agent),
 96    Esql.devices_per_user = VALUES(okta.client.device),
 97    Esql.is_proxy = VALUES(okta.security_context.is_proxy),
 98    Esql.geo_country = VALUES(client.geo.country_name),
 99    Esql.geo_city = VALUES(client.geo.city_name),
100    Esql.asn_number = VALUES(source.as.number),
101    Esql.asn_org = VALUES(source.as.organization.name),
102    Esql.threat_suspected = VALUES(okta.debug_context.debug_data.threat_suspected),
103    Esql.risk_level = VALUES(okta.debug_context.debug_data.risk_level),
104    Esql.event_actions = VALUES(event.action),
105    Esql.first_seen_user = MIN(@timestamp),
106    Esql.last_seen_user = MAX(@timestamp)
107  BY okta.client.ip, okta.actor.alternate_id
108// SECOND STATS: Aggregate by IP to detect password spray pattern
109// Now we can accurately measure the distribution of attempts across users
110| STATS
111    Esql.unique_users = COUNT(*),
112    Esql.total_attempts = SUM(Esql.user_attempts),
113    Esql.max_attempts_per_user = MAX(Esql.user_attempts),
114    Esql.min_attempts_per_user = MIN(Esql.user_attempts),
115    Esql.avg_attempts_per_user = AVG(Esql.user_attempts),
116    // Spray band: 2-6 attempts per user (deliberate slow spray below lockout)
117    Esql.users_in_spray_band = SUM(CASE(Esql.user_attempts >= 2 AND Esql.user_attempts <= 6, 1, 0)),
118    // Also track users with only 1 attempt (stuffing-like) for differentiation
119    Esql.users_with_single_attempt = SUM(CASE(Esql.user_attempts == 1, 1, 0)),
120    Esql.first_seen = MIN(Esql.first_seen_user),
121    Esql.last_seen = MAX(Esql.last_seen_user),
122    Esql.target_users = VALUES(okta.actor.alternate_id),
123    Esql.user_source_mapping = VALUES(Esql.user_source_info),
124    Esql.event_action_values = VALUES(Esql.event_actions),
125    Esql.user_agent_values = VALUES(Esql.user_agents_per_user),
126    Esql.device_values = VALUES(Esql.devices_per_user),
127    Esql.is_proxy_values = VALUES(Esql.is_proxy),
128    Esql.geo_country_values = VALUES(Esql.geo_country),
129    Esql.geo_city_values = VALUES(Esql.geo_city),
130    Esql.source_asn_values = VALUES(Esql.asn_number),
131    Esql.source_asn_org_values = VALUES(Esql.asn_org),
132    Esql.threat_suspected_values = VALUES(Esql.threat_suspected),
133    Esql.risk_level_values = VALUES(Esql.risk_level)
134  BY okta.client.ip
135// Calculate spray signature metrics
136| EVAL
137    // Percentage of users in the spray band (2-6 attempts)
138    Esql.pct_users_in_spray_band = Esql.users_in_spray_band * 100.0 / Esql.unique_users,
139    // Attack duration in minutes (spray is paced, not bursty)
140    Esql.attack_duration_minutes = DATE_DIFF("minute", Esql.first_seen, Esql.last_seen)
141// Password spraying detection logic:
142// - Many users targeted (>= 5)
143// - Hard cap below Okta lockout threshold (max <= 8 attempts per user)
144// - Majority of users in spray band (2-6 attempts) (at least 60%)
145// - Attack is paced over time (>= 5 minutes) (not a 10-second burst like stuffing)
146// - Minimum total attempts to reduce noise
147// Note: For IP rotation attacks, see "Distributed Password Spray Attack in Okta" rule
148| WHERE
149    Esql.unique_users >= 5
150    AND Esql.total_attempts >= 15
151    AND Esql.max_attempts_per_user <= 8
152    AND Esql.max_attempts_per_user >= 2
153    AND Esql.pct_users_in_spray_band >= 60.0
154    AND Esql.attack_duration_minutes >= 5
155| SORT Esql.total_attempts DESC
156| KEEP Esql.*, okta.client.ip
157'''
158
159
160[[rule.threat]]
161framework = "MITRE ATT&CK"
162[[rule.threat.technique]]
163id = "T1110"
164name = "Brute Force"
165reference = "https://attack.mitre.org/techniques/T1110/"
166[[rule.threat.technique.subtechnique]]
167id = "T1110.003"
168name = "Password Spraying"
169reference = "https://attack.mitre.org/techniques/T1110/003/"
170
171
172[rule.threat.tactic]
173id = "TA0006"
174name = "Credential Access"
175reference = "https://attack.mitre.org/tactics/TA0006/"

Triage and analysis

Investigating Potential Okta Password Spray (Single Source)

This rule identifies a single source IP attempting authentication against multiple user accounts with repeated attempts per user over time. This pattern indicates password spraying where attackers try common passwords while pacing attempts to avoid lockouts.

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.
  • Analyze the timing of attempts to determine if they are paced to avoid lockout thresholds.
  • Check if Okta flagged the source as a known threat or proxy.
  • Examine user agent strings for signs of automation or consistent tooling across attempts.
  • 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 with login issues.
  • Automated processes or misconfigured applications retrying authentication may trigger this rule.
  • Password rotation events may cause legitimate widespread authentication failures.

Response and remediation

  • If attack is confirmed, block the source IP at the network perimeter.
  • Notify targeted users and enforce password resets for accounts that may have been compromised.
  • Enable or strengthen MFA for targeted accounts.
  • Consider implementing CAPTCHA or additional friction for suspicious authentication patterns.
  • Review Okta sign-on policies to ensure lockout thresholds are appropriately configured.

References

Related rules

to-top