Potential Okta Password Spray (Multi-Source)

edit
IMPORTANT: This documentation is no longer updated. Refer to Elastic's version policy and the latest documentation.

Potential Okta Password Spray (Multi-Source)

edit

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.

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:

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

edit

Triage 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

edit

The Okta Fleet integration, Filebeat module, or similarly structured data is required to be compatible with this rule.

Rule query

edit
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.*

Framework: MITRE ATT&CKTM