Detection rules › Splunk
Okta Mismatch Between Source and Response for Verify Push Request
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
| Tactic | Techniques |
|---|---|
| Credential Access | T1621 Multi-Factor Authentication Request Generation |
Rules detecting the same action
Other rules on this platform that filter on the same API call or operation.
- First Occurrence of Okta User Session Started via Proxy (Elastic)
- MFA Fatigue (OKTA) (Kusto)
- Okta AiTM Phishing Attempt Blocked by FastPass (Panther)
- Okta Fast Pass phishing Detection (Kusto)
- Okta FastPass Phishing Detection (Sigma)
- Okta FastPass Phishing Detection (Elastic)
- Okta MFA Bruteforce Attack (YARA-L)
- Okta MFA Exhaustion Hunt (Splunk)
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.
| Field | Kind | Values |
|---|---|---|
dc_ip | gt |
|
debugContext.debugData.factor | eq |
|
eventType | in |
|
ratio | lt |
|
sourcetype | eq |
|
suspect_device_from_source | gt |
|
suspect_ip_from_source | gt |
|
total_pushes | gt |
|
total_rejected | gt |
|
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.
| Stage | Term |
|---|---|
| 7 | iplocation |
| 7 | client.ipAddress |