Potential Okta Password Spray (Multi-Source)

Last updated 6 days ago on 2026-02-19
Created 6 days ago on 2026-02-19

About

Detects potential password spray attacks where multiple source IPs target multiple Okta user accounts within a time window, indicating coordinated attacks using IP rotation to evade single-source detection.
Tags
Domain: IdentityUse Case: Identity and Access AuditUse Case: Threat DetectionData Source: OktaData Source: Okta System LogsTactic: Credential AccessLanguage: esql
Severity
medium
Risk Score
47
MITRE ATT&CK™

Credential Access (TA0006)(external, opens in a new tab or window)

False Positive Examples
Large enterprises with many users experiencing simultaneous password issues during credential rotation events.Automated monitoring or penetration testing tools scanning from multiple IPs.
License
Elastic License v2(external, opens in a new tab or window)

Definition

Integration Pack
Prebuilt Security Detection Rules
Related Integrations

okta(external, opens in a new tab or window)

Query
text code block:
FROM logs-okta.system-* METADATA _id, _version, _index | WHERE event.dataset == "okta.system" AND (event.action LIKE "user.authentication.*" OR event.action == "user.session.start") AND okta.outcome.reason IN ("INVALID_CREDENTIALS", "LOCKED_OUT") AND okta.actor.alternate_id IS NOT NULL // Bucket into 15-minute windows and create user-source mapping for context | EVAL Esql.time_bucket = DATE_TRUNC(15 minutes, @timestamp), Esql.user_source_info = CONCAT( "{\"user\":\"", okta.actor.alternate_id, "\",\"ip\":\"", COALESCE(okta.client.ip::STRING, "unknown"), "\",\"user_agent\":\"", COALESCE(okta.client.user_agent.raw_user_agent, "unknown"), "\"}" ) // Aggregate across entire tenant per time bucket to detect distributed spray | STATS Esql.unique_users = COUNT_DISTINCT(okta.actor.alternate_id), Esql.unique_source_ips = COUNT_DISTINCT(okta.client.ip), Esql.total_attempts = COUNT(*), Esql.unique_user_agents = COUNT_DISTINCT(okta.client.user_agent.raw_user_agent), Esql.unique_asns = COUNT_DISTINCT(source.as.number), Esql.unique_countries = COUNT_DISTINCT(client.geo.country_name), Esql.first_seen = MIN(@timestamp), Esql.last_seen = MAX(@timestamp), Esql.target_users = VALUES(okta.actor.alternate_id), Esql.source_ip_values = VALUES(okta.client.ip), Esql.user_source_mapping = VALUES(Esql.user_source_info), Esql.event_action_values = VALUES(event.action), Esql.user_agent_values = VALUES(okta.client.user_agent.raw_user_agent), Esql.device_values = VALUES(okta.client.device), Esql.is_proxy_values = VALUES(okta.security_context.is_proxy), Esql.geo_country_values = VALUES(client.geo.country_name), Esql.geo_city_values = VALUES(client.geo.city_name), Esql.source_asn_values = VALUES(source.as.number), Esql.source_asn_org_values = VALUES(source.as.organization.name), Esql.threat_suspected_values = VALUES(okta.debug_context.debug_data.threat_suspected), Esql.risk_level_values = VALUES(okta.debug_context.debug_data.risk_level), Esql.risk_reasons_values = VALUES(okta.debug_context.debug_data.risk_reasons) BY Esql.time_bucket // Calculate spray metrics | EVAL Esql.attempts_per_user = Esql.total_attempts * 1.0 / Esql.unique_users, Esql.attempts_per_ip = Esql.total_attempts * 1.0 / Esql.unique_source_ips, Esql.users_per_ip = Esql.unique_users * 1.0 / Esql.unique_source_ips // Distributed spray: many IPs, many users, moderate spread across both // Key differentiator: attacks come from multiple IPs (evading per-IP rules) | WHERE Esql.unique_source_ips >= 5 AND Esql.unique_users >= 8 AND Esql.total_attempts >= 25 AND Esql.attempts_per_user <= 5.0 AND Esql.users_per_ip >= 1.0 | SORT Esql.total_attempts DESC | KEEP Esql.*

Install detection rules in Elastic Security

Detect Potential Okta Password Spray (Multi-Source) in the Elastic Security detection engine by installing this rule into your Elastic Stack.

To setup this rule, check out the installation guide for Prebuilt Security Detection Rules(external, opens in a new tab or window).