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/09/18"
  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    "Noise: High",
 70    "Performance: Normal",
 71    "Profile: Aggressive",
 72    "Threat: Brute Force",
 73    "Rule Type: ES|QL",
 74    "Platform: Okta",
 75]
 76timestamp_override = "event.ingested"
 77type = "esql"
 78
 79query = '''
 80FROM logs-okta.system-* METADATA _id, _version, _index
 81| WHERE
 82    data_stream.dataset == "okta.system"
 83    AND (event.action LIKE "user.authentication.*" OR event.action == "user.session.start")
 84    AND okta.outcome.reason IN ("INVALID_CREDENTIALS", "LOCKED_OUT")
 85    AND okta.actor.alternate_id IS NOT NULL
 86// Build user-source context as JSON for enrichment
 87| EVAL Esql.user_source_info = CONCAT(
 88    "{\"user\":\"", okta.actor.alternate_id,
 89    "\",\"ip\":\"", COALESCE(okta.client.ip::STRING, "unknown"),
 90    "\",\"user_agent\":\"", COALESCE(okta.client.user_agent.raw_user_agent, "unknown"), "\"}"
 91  )
 92// FIRST STATS: Aggregate by (IP, user) to get per-user attempt counts
 93// This prevents skew from outlier users with many attempts
 94| STATS
 95    Esql.user_attempts = COUNT(*),
 96    Esql.user_dt_hashes = COUNT_DISTINCT(okta.debug_context.debug_data.dt_hash),
 97    Esql.user_source_info = VALUES(Esql.user_source_info),
 98    Esql.user_agents_per_user = VALUES(okta.client.user_agent.raw_user_agent),
 99    Esql.devices_per_user = VALUES(okta.client.device),
100    Esql.is_proxy = VALUES(okta.security_context.is_proxy),
101    Esql.geo_country = VALUES(client.geo.country_name),
102    Esql.geo_city = VALUES(client.geo.city_name),
103    Esql.asn_number = VALUES(source.as.number),
104    Esql.asn_org = VALUES(source.as.organization.name),
105    Esql.threat_suspected = VALUES(okta.debug_context.debug_data.threat_suspected),
106    Esql.risk_level = VALUES(okta.debug_context.debug_data.risk_level),
107    Esql.risk_reasons = VALUES(okta.debug_context.debug_data.risk_reasons),
108    Esql.event_actions = VALUES(event.action),
109    Esql.first_seen_user = MIN(@timestamp),
110    Esql.last_seen_user = MAX(@timestamp)
111  BY okta.client.ip, okta.actor.alternate_id
112// SECOND STATS: Aggregate by IP to detect credential stuffing pattern
113// Now we can accurately measure the distribution of attempts across users
114| STATS
115    Esql.unique_users = COUNT(*),
116    Esql.total_attempts = SUM(Esql.user_attempts),
117    Esql.max_attempts_per_user = MAX(Esql.user_attempts),
118    Esql.min_attempts_per_user = MIN(Esql.user_attempts),
119    Esql.avg_attempts_per_user = AVG(Esql.user_attempts),
120    Esql.users_with_single_attempt = SUM(CASE(Esql.user_attempts == 1, 1, 0)),
121    Esql.users_with_few_attempts = SUM(CASE(Esql.user_attempts <= 2, 1, 0)),
122    Esql.first_seen = MIN(Esql.first_seen_user),
123    Esql.last_seen = MAX(Esql.last_seen_user),
124    Esql.target_users = VALUES(okta.actor.alternate_id),
125    Esql.user_source_mapping = VALUES(Esql.user_source_info),
126    Esql.event_action_values = VALUES(Esql.event_actions),
127    Esql.user_agent_values = VALUES(Esql.user_agents_per_user),
128    Esql.device_values = VALUES(Esql.devices_per_user),
129    Esql.is_proxy_values = VALUES(Esql.is_proxy),
130    Esql.geo_country_values = VALUES(Esql.geo_country),
131    Esql.geo_city_values = VALUES(Esql.geo_city),
132    Esql.source_asn_values = VALUES(Esql.asn_number),
133    Esql.source_asn_org_values = VALUES(Esql.asn_org),
134    Esql.threat_suspected_values = VALUES(Esql.threat_suspected),
135    Esql.risk_level_values = VALUES(Esql.risk_level),
136    Esql.risk_reasons_values = VALUES(Esql.risk_reasons)
137  BY okta.client.ip
138// Calculate stuffing signature: most users should have very few attempts
139| EVAL Esql.pct_users_few_attempts = Esql.users_with_few_attempts * 100.0 / Esql.unique_users
140// Credential stuffing: many users, most with 1-2 attempts each, low max per user
141// Stacked stats gives us accurate per-user distribution instead of skewed averages
142| WHERE
143    Esql.total_attempts >= 25
144    AND Esql.unique_users >= 15
145    AND Esql.max_attempts_per_user <= 2
146    AND Esql.pct_users_few_attempts >= 80.0
147| SORT Esql.unique_users DESC
148| KEEP Esql.*, okta.client.ip
149'''
150
151
152[[rule.threat]]
153framework = "MITRE ATT&CK"
154[[rule.threat.technique]]
155id = "T1110"
156name = "Brute Force"
157reference = "https://attack.mitre.org/techniques/T1110/"
158[[rule.threat.technique.subtechnique]]
159id = "T1110.004"
160name = "Credential Stuffing"
161reference = "https://attack.mitre.org/techniques/T1110/004/"
162
163
164[rule.threat.tactic]
165id = "TA0006"
166name = "Credential Access"
167reference = "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

to-top