EXPLORE
← Back to Explore
elasticlowTTP

Potential Okta Brute Force (Device Token Rotation)

Detects potential brute force attacks against a single Okta user account where excessive unique device token hashes are generated, indicating automated tooling that fails to persist browser cookies between attempts.

MITRE ATT&CK

credential-access

Detection Query

FROM logs-okta.system-* METADATA _id, _version, _index
| WHERE
    data_stream.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
    // Primary authn endpoint; sessions API provides additional coverage
    AND (
        okta.debug_context.debug_data.request_uri == "/api/v1/authn"
        OR okta.debug_context.debug_data.request_uri LIKE "/api/v1/sessions*"
    )
// Track whether each event has a device token
| EVAL has_dt_hash = CASE(okta.debug_context.debug_data.dt_hash IS NOT NULL, 1, 0)
// Aggregate by IP + user to detect single-user brute force
| STATS
    Esql.unique_dt_hashes = COUNT_DISTINCT(okta.debug_context.debug_data.dt_hash),
    Esql.total_attempts = COUNT(*),
    Esql.attempts_with_dt = SUM(has_dt_hash),
    Esql.unique_user_agents = COUNT_DISTINCT(okta.client.user_agent.raw_user_agent),
    Esql.first_seen = MIN(@timestamp),
    Esql.last_seen = MAX(@timestamp),
    Esql.dt_hash_values = VALUES(okta.debug_context.debug_data.dt_hash),
    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 okta.client.ip, okta.actor.alternate_id
// Calculate automation detection metrics (float-safe division)
| EVAL Esql.dt_coverage = Esql.attempts_with_dt * 1.0 / Esql.total_attempts,
       Esql.dt_per_attempt = Esql.unique_dt_hashes * 1.0 / Esql.total_attempts
// Detection branches:
//   A) Many unique DT hashes relative to attempts = tooling generating new tokens per attempt
//   B) High attempts + very low DT coverage = cookie-less automation (no DT sent at all)
//   C) Multiple user agents for same user = evasion or automation
| WHERE
    (Esql.unique_dt_hashes >= 7 AND Esql.total_attempts >= 10 AND Esql.dt_per_attempt >= 0.5)
    OR (Esql.total_attempts >= 12 AND Esql.dt_coverage < 0.15)
    OR (Esql.total_attempts >= 10 AND Esql.unique_user_agents >= 5)
| SORT Esql.total_attempts DESC
| KEEP Esql.*, okta.client.ip, okta.actor.alternate_id

Author

Elastic

Created

2024/06/17

Data Sources

Okta

Tags

Domain: IdentityUse Case: Identity and Access AuditData Source: OktaTactic: Credential AccessResources: Investigation Guide
Raw Content
[metadata]
creation_date = "2024/06/17"
integration = ["okta"]
maturity = "production"
updated_date = "2026/04/10"

[rule]
author = ["Elastic"]
description = """
Detects potential brute force attacks against a single Okta user account where excessive unique device token hashes
are generated, indicating automated tooling that fails to persist browser cookies between attempts.
"""
false_positives = [
    "A user experiencing login issues may generate multiple device tokens through repeated legitimate attempts.",
    "Automated testing or monitoring tools that do not persist cookies may trigger this rule.",
]
from = "now-30m"
language = "esql"
license = "Elastic License v2"
name = "Potential Okta Brute Force (Device Token Rotation)"
note = """## Triage and analysis

### Investigating Potential Okta Brute Force (Device Token Rotation)

This rule identifies excessive unique device token hashes generated for a single user account, indicating automated brute force tooling that fails to persist browser cookies between authentication attempts.

#### Possible investigation steps
- Identify the targeted user account and determine if it has elevated privileges or sensitive access.
- Review the source IP and check if it belongs to known proxy, VPN, or cloud infrastructure.
- Examine user agent strings for signs of automation, scripting tools, or inconsistent browser fingerprints.
- Check if Okta flagged the source as a known threat or proxy.
- Determine if any authentication attempts succeeded following the failed attempts.
- Review the user's recent activity for signs of account compromise.

### False positive analysis
- Users experiencing login issues may generate multiple device tokens through repeated legitimate attempts.
- Automated testing or monitoring tools that do not persist cookies may trigger this rule.
- Browser extensions or privacy tools that clear cookies between requests may cause device token rotation.

### Response and remediation
- If attack is confirmed, reset the user's password immediately.
- Block the source IP at the network perimeter.
- Review and potentially reset MFA for the targeted account.
- Monitor for any successful authentication that may indicate compromise.
- Contact the user to verify if they experienced legitimate login issues.
"""
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://support.okta.com/help/s/article/How-does-the-Device-Token-work?language=en_US",
    "https://developer.okta.com/docs/reference/api/event-types/",
    "https://www.elastic.co/security-labs/testing-okta-visibility-and-detection-dorothy",
    "https://sec.okta.com/articles/2023/08/cross-tenant-impersonation-prevention-and-detection",
    "https://www.okta.com/resources/whitepaper-how-adaptive-mfa-can-help-in-mitigating-brute-force-attacks/",
    "https://www.elastic.co/security-labs/monitoring-okta-threats-with-elastic-security",
    "https://www.elastic.co/security-labs/starter-guide-to-understanding-okta",
]
risk_score = 21
rule_id = "23f18264-2d6d-11ef-9413-f661ea17fbce"
setup = "The Okta Fleet integration, Filebeat module, or similarly structured data is required to be compatible with this rule."
severity = "low"
tags = [
    "Domain: Identity",
    "Use Case: Identity and Access Audit",
    "Data Source: Okta",
    "Tactic: Credential Access",
    "Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "esql"

query = '''
FROM logs-okta.system-* METADATA _id, _version, _index
| WHERE
    data_stream.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
    // Primary authn endpoint; sessions API provides additional coverage
    AND (
        okta.debug_context.debug_data.request_uri == "/api/v1/authn"
        OR okta.debug_context.debug_data.request_uri LIKE "/api/v1/sessions*"
    )
// Track whether each event has a device token
| EVAL has_dt_hash = CASE(okta.debug_context.debug_data.dt_hash IS NOT NULL, 1, 0)
// Aggregate by IP + user to detect single-user brute force
| STATS
    Esql.unique_dt_hashes = COUNT_DISTINCT(okta.debug_context.debug_data.dt_hash),
    Esql.total_attempts = COUNT(*),
    Esql.attempts_with_dt = SUM(has_dt_hash),
    Esql.unique_user_agents = COUNT_DISTINCT(okta.client.user_agent.raw_user_agent),
    Esql.first_seen = MIN(@timestamp),
    Esql.last_seen = MAX(@timestamp),
    Esql.dt_hash_values = VALUES(okta.debug_context.debug_data.dt_hash),
    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 okta.client.ip, okta.actor.alternate_id
// Calculate automation detection metrics (float-safe division)
| EVAL Esql.dt_coverage = Esql.attempts_with_dt * 1.0 / Esql.total_attempts,
       Esql.dt_per_attempt = Esql.unique_dt_hashes * 1.0 / Esql.total_attempts
// Detection branches:
//   A) Many unique DT hashes relative to attempts = tooling generating new tokens per attempt
//   B) High attempts + very low DT coverage = cookie-less automation (no DT sent at all)
//   C) Multiple user agents for same user = evasion or automation
| WHERE
    (Esql.unique_dt_hashes >= 7 AND Esql.total_attempts >= 10 AND Esql.dt_per_attempt >= 0.5)
    OR (Esql.total_attempts >= 12 AND Esql.dt_coverage < 0.15)
    OR (Esql.total_attempts >= 10 AND Esql.unique_user_agents >= 5)
| SORT Esql.total_attempts DESC
| KEEP Esql.*, okta.client.ip, okta.actor.alternate_id
'''


[[rule.threat]]
framework = "MITRE ATT&CK"

[[rule.threat.technique]]
id = "T1110"
name = "Brute Force"
reference = "https://attack.mitre.org/techniques/T1110/"

[[rule.threat.technique.subtechnique]]
id = "T1110.001"
name = "Password Guessing"
reference = "https://attack.mitre.org/techniques/T1110/001/"

[rule.threat.tactic]
id = "TA0006"
name = "Credential Access"
reference = "https://attack.mitre.org/tactics/TA0006/"