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.

MITRE ATT&CK coverage

Rules detecting the same action

Other rules on this platform that filter on the same API call or operation.

Rule body splunk

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
drilldown_searches:
    - name: View the detection results for - "$user$"
      search: '%original_detection_search% | search  user = "$user$"'
      earliest_offset: $info_min_time$
      latest_offset: $info_max_time$
    - name: View risk events for the last 7 days for - "$user$"
      search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`'
      earliest_offset: 7d
      latest_offset: "0"
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
tests:
    - name: True Positive Test
      attack_data:
        - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1621/okta_mismatch/okta_mismatch.log
          source: Okta
          sourcetype: OktaIM2:log
      test_type: unit

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

Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.

FieldKindValues
dc_ipgt
  • 1
debugContext.debugData.factoreq
  • "OKTA_VERIFY_PUSH"
eventTypein
  • "system.push.send_factor_verify_push"
  • "user.authentication.auth_via_mfa"
ratiolt
  • 0.5
sourcetypeeq
  • OktaIM2:log
suspect_device_from_sourcegt
  • 0
suspect_ip_from_sourcegt
  • 0
total_pushesgt
  • 1
total_rejectedgt
  • 0

Search terms

Bare-string tokens in the SPL search body. Splunk matches each token against _raw (the untyped raw event text) anywhere it appears, not against a specific field. These don't surface in the Indicators table because they aren't predicates on a known field.

StageTerm
7iplocation
7client.ipAddress