Potential Okta Password Spray (Multi-Source)
editPotential Okta Password Spray (Multi-Source)
editDetects 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.
Rule type: esql
Rule indices: None
Severity: medium
Risk score: 47
Runs every: 15m
Searches indices from: now-1h (Date Math format, see also Additional look-back time)
Maximum alerts per execution: 100
References:
- https://support.okta.com/help/s/article/Troubleshooting-Distributed-Brute-Force-andor-Password-Spray-attacks-in-Okta
- https://www.okta.com/identity-101/brute-force/
- https://developer.okta.com/docs/reference/api/event-types/
- https://www.elastic.co/security-labs/testing-okta-visibility-and-detection-dorothy
- https://www.elastic.co/security-labs/monitoring-okta-threats-with-elastic-security
- https://www.elastic.co/security-labs/starter-guide-to-understanding-okta
Tags:
- Domain: Identity
- Use Case: Identity and Access Audit
- Use Case: Threat Detection
- Data Source: Okta
- Data Source: Okta System Logs
- Tactic: Credential Access
- Resources: Investigation Guide
Version: 2
Rule authors:
- Elastic
Rule license: Elastic License v2
Investigation guide
editTriage and analysis
Investigating Potential Okta Password Spray (Multi-Source)
This rule identifies coordinated password spray attacks where multiple source IPs target multiple user accounts within a time window. This pattern indicates attackers using IP rotation to evade single-source detection while spraying passwords across the organization.
Possible investigation steps
- Review the list of targeted user accounts and check if any authentications succeeded.
- Examine the source IPs and their ASN ownership for signs of proxy, VPN, or cloud infrastructure.
- Check if Okta flagged any of the sources as known threats or proxies.
- Analyze the attempts-per-user ratio to confirm spray behavior versus brute force.
- Review the geographic distribution of source IPs for coordination patterns.
- Cross-reference with successful authentication events to identify potential compromises.
False positive analysis
- Organization-wide password rotation or expiration events may cause widespread authentication failures.
- Misconfigured SSO or SAML integrations can cause batch failures from legitimate infrastructure.
- Penetration testing should be coordinated and whitelisted in advance.
Response and remediation
- If attack is confirmed, notify affected users and enforce password resets for potentially compromised accounts.
- Block attacking IP ranges at the network perimeter.
- Enable or strengthen MFA for targeted accounts.
- Review Okta sign-on policies to add additional friction for suspicious authentication patterns.
- Consider temporary lockdowns for highly targeted accounts.
Setup
editThe Okta Fleet integration, Filebeat module, or similarly structured data is required to be compatible with this rule.
Rule query
editFROM 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.*
Framework: MITRE ATT&CKTM
-
Tactic:
- Name: Credential Access
- ID: TA0006
- Reference URL: https://attack.mitre.org/tactics/TA0006/
-
Technique:
- Name: Brute Force
- ID: T1110
- Reference URL: https://attack.mitre.org/techniques/T1110/
-
Sub-technique:
- Name: Password Spraying
- ID: T1110.003
- Reference URL: https://attack.mitre.org/techniques/T1110/003/