Detection rules › Splunk

Okta Mismatch Between Source and Response for Verify Push Request

Status
production
Severity
medium
Group by
"actor.alternateId", "authenticationContext.externalSessionId", "client.device", "client.ipAddress", "client.userAgent.rawUserAgent", "debugContext.debugData.behaviors", "debugContext.debugData.factor", "outcome.result", eventType, group_push_time
Author
John Murphy and Jordan Ruocco, Okta, Michael Haag, Bhavin Patel, Splunk
Source
github.com/splunk/security_content

The following analytic identifies discrepancies between the source and response events for Okta Verify Push requests, indicating potential suspicious behavior. It leverages Okta System Log events, specifically system.push.send_factor_verify_push and user.authentication.auth_via_mfa with the factor "OKTA_VERIFY_PUSH." The detection groups events by SessionID, calculates the ratio of successful sign-ins to push requests, and checks for session roaming and new device/IP usage. This activity is significant as it may indicate push spam or unauthorized access attempts. If confirmed malicious, attackers could bypass MFA, leading to unauthorized access to sensitive systems.

Known false positives

  • False positives may be present based on organization size and configuration of Okta. Monitor, tune and filter as needed.

MITRE ATT&CK coverage

Telemetry coverage

Rules detecting the same action

These rules filter on the same operation.

Rule body

name: Okta Mismatch Between Source and Response for Verify Push Request
id: 8085b79b-9b85-4e67-ad63-351c9e9a5e9a
version: 11
creation_date: '2024-04-17'
modification_date: '2026-05-13'
author: John Murphy and Jordan Ruocco, Okta, Michael Haag, Bhavin Patel, Splunk
status: production
type: TTP
description: The following analytic identifies discrepancies between the source and response events for Okta Verify Push requests, indicating potential suspicious behavior. It leverages Okta System Log events, specifically `system.push.send_factor_verify_push` and `user.authentication.auth_via_mfa` with the factor "OKTA_VERIFY_PUSH." The detection groups events by SessionID, calculates the ratio of successful sign-ins to push requests, and checks for session roaming and new device/IP usage. This activity is significant as it may indicate push spam or unauthorized access attempts. If confirmed malicious, attackers could bypass MFA, leading to unauthorized access to sensitive systems.
data_source:
    - Okta
search: |-
    `okta` eventType IN (system.push.send_factor_verify_push) OR (eventType IN (user.authentication.auth_via_mfa) debugContext.debugData.factor="OKTA_VERIFY_PUSH")
      | eval groupby="authenticationContext.externalSessionId"
      | eval group_push_time=_time
      | bin span=2s group_push_time
      | fillnull value=NULL
      | stats min(_time) as _time
        BY authenticationContext.externalSessionId eventType debugContext.debugData.factor
           outcome.result actor.alternateId client.device
           client.ipAddress client.userAgent.rawUserAgent debugContext.debugData.behaviors
           group_push_time
      | iplocation client.ipAddress
      | fields - lat, lon, group_push_time
      | stats min(_time) as _time dc(client.ipAddress) as dc_ip sum(eval(if(eventType="system.push.send_factor_verify_push" AND $outcome.result$="SUCCESS", 1, 0))) as total_pushes sum(eval(if(eventType="user.authentication.auth_via_mfa" AND $outcome.result$="SUCCESS", 1, 0))) as total_successes sum(eval(if(eventType="user.authentication.auth_via_mfa" AND $outcome.result$="FAILURE", 1, 0))) as total_rejected sum(eval(if(eventType="system.push.send_factor_verify_push" AND $debugContext.debugData.behaviors$ LIKE "%New Device=POSITIVE%", 1, 0))) as suspect_device_from_source sum(eval(if(eventType="system.push.send_factor_verify_push" AND $debugContext.debugData.behaviors$ LIKE "%New IP=POSITIVE%", 1, 0))) as suspect_ip_from_source values(eval(if(eventType="system.push.send_factor_verify_push", $client.ipAddress$, ""))) as src values(eval(if(eventType="user.authentication.auth_via_mfa", $client.ipAddress$, ""))) as dest values(*) as *
        BY authenticationContext.externalSessionId
      | eval ratio = round(total_successes / total_pushes, 2)
      | search ((ratio < 0.5 AND total_pushes > 1) OR (total_rejected > 0)) AND dc_ip > 1 AND suspect_device_from_source > 0 AND suspect_ip_from_source > 0
      | rename actor.alternateId as user
      | `okta_mismatch_between_source_and_response_for_verify_push_request_filter`
how_to_implement: The analytic leverages Okta OktaIm2 logs to be ingested using the Splunk Add-on for Okta Identity Cloud (https://splunkbase.splunk.com/app/6553).
known_false_positives: False positives may be present based on organization size and configuration of Okta. Monitor, tune and filter as needed.
references:
    - https://attack.mitre.org/techniques/T1621
    - https://splunkbase.splunk.com/app/6553
finding:
    title: A mismatch between source and response for verifying a push request has occurred for $user$
    entity:
        field: user
        type: user
        score: 50
analytic_story:
    - Okta Account Takeover
    - Okta MFA Exhaustion
    - Scattered Lapsus$ Hunters
asset_type: Okta Tenant
mitre_attack_id:
    - T1621
product:
    - Splunk Enterprise
    - Splunk Enterprise Security
    - Splunk Cloud
category: application
security_domain: access

Stages and Predicates

Stage 1: search

`okta` eventType IN (system.push.send_factor_verify_push) OR (eventType IN (user.authentication.auth_via_mfa) debugContext.debugData.factor="OKTA_VERIFY_PUSH")

Stage 2: eval

| eval groupby="authenticationContext.externalSessionId"

Stage 3: eval

| eval group_push_time=_time

Stage 4: bucket

| bin span=2s group_push_time

Stage 5: fillnull

| fillnull value=NULL

Stage 6: stats

| stats min(_time) as _time
    BY authenticationContext.externalSessionId eventType debugContext.debugData.factor
       outcome.result actor.alternateId client.device
       client.ipAddress client.userAgent.rawUserAgent debugContext.debugData.behaviors
       group_push_time

Stage 7: search

| iplocation client.ipAddress

Stage 8: fields

| fields - lat, lon, group_push_time

Stage 9: macro (not parsed)

| stats min(_time) as _time dc(client.ipAddress) as dc_ip sum(eval(if(eventType="system.push.send_factor_verify_push" AND $outcome.result$="SUCCESS", 1, 0))) as total_pushes sum(eval(if(eventType="user.authentication.auth_via_mfa" AND $outcome.result$="SUCCESS", 1, 0))) as total_successes sum(eval(if(eventType="user.authentication.auth_via_mfa" AND $outcome.result$="FAILURE", 1, 0))) as total_rejected sum(eval(if(eventType="system.push.send_factor_verify_push" AND $debugContext.debugData.behaviors$ LIKE "%New Device=POSITIVE%", 1, 0))) as suspect_device_from_source sum(eval(if(eventType="system.push.send_factor_verify_push" AND $debugContext.debugData.behaviors$ LIKE "%New IP=POSITIVE%", 1, 0))) as suspect_ip_from_source values(eval(if(eventType="system.push.send_factor_verify_push", $client.ipAddress$, ""))) as src values(eval(if(eventType="user.authentication.auth_via_mfa", $client.ipAddress$, ""))) as dest values(*) as *
    BY authenticationContext.externalSessionId

Stage 10: eval

| eval ratio = round(total_successes / total_pushes, 2)

Stage 11: search

| search ((ratio < 0.5 AND total_pushes > 1) OR (total_rejected > 0)) AND dc_ip > 1 AND suspect_device_from_source > 0 AND suspect_ip_from_source > 0

Stage 12: rename

| rename actor.alternateId as user

Stage 13: search

| `okta_mismatch_between_source_and_response_for_verify_push_request_filter`

Indicators

These rows show field, operator, and value matches.

Search terms

These SPL tokens match against raw event text.

StageTerm
7iplocation
7client.ipAddress