Back
Idc2697b81-7fe9-4f57-ba1d-de46c6f91f9c
RulenameMFA Fatigue (OKTA)
DescriptionMFA fatigue attack is a cybersecurity threat where attackers exploit user exhaustion from multi-factor authentication prompts to trick them into approving multi-factor authentication prompts, potentially compromising their own security. The query identifies MFA fatigue attempts in Okta by correlating repeated Okta Verify push requests, denied pushes, and eventual successful MFA authentication within the same session.

Ref: https://www.okta.com/blog/identity-security/mfa-fatigue-growing-security-concern/.
SeverityMedium
TacticsCredentialAccess
TechniquesT1621
Required data connectorsOktaSSO
OktaSSOv2
KindScheduled
Query frequency1h
Query period1h
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Okta%20Single%20Sign-On/Analytic%20Rules/MFAFatigue.yaml
Version1.1.3
Arm templatec2697b81-7fe9-4f57-ba1d-de46c6f91f9c.json
Deploy To Azure
// Adjust threshold for MFA pushes to reduce noise
let PushThreshold = 10;
OktaSSO
| where ((eventType_s =="user.authentication.auth_via_mfa" and column_ifexists('debugContext_debugData_factor_s', '') == "OKTA_VERIFY_PUSH") or eventType_s == "system.push.send_factor_verify_push" or eventType_s == "user.mfa.okta_verify.deny_push") 
| summarize IPAddress = make_set(client_ipAddress_s,100), City = make_set(client_geographicalContext_city_s,100),
          successes = countif(eventType_s == "user.authentication.auth_via_mfa"),
          denies = countif(eventType_s == "user.mfa.okta_verify.deny_push"),
          pushes = countif(eventType_s == "system.push.send_factor_verify_push") by TimeGenerated, authenticationContext_externalSessionId_s, actor_alternateId_s,actor_displayName_s, outcome_result_s 
| summarize lasttime = max(TimeGenerated), firsttime = min(TimeGenerated),
          successes = sum(successes), failures = sum(denies), pushes = sum(pushes) by  authenticationContext_externalSessionId_s, actor_alternateId_s,actor_displayName_s, outcome_result_s 
| extend seconds = lasttime - firsttime
| where pushes > PushThreshold
| extend totalattempts = successes + failures
| extend finding = case(
            failures == pushes and pushes > 1, "Authentication attempts not successful because multiple pushes denied",
            totalattempts == 0, "Multiple pushes sent and ignored",
            successes > 0 and pushes > 3, "Multiple pushes sent, eventual successful authentication!",
            "Normal authentication pattern")
| project lasttime, firsttime, seconds, actor_alternateId_s, actor_displayName_s, authenticationContext_externalSessionId_s, pushes, successes, failures, totalattempts, finding, outcome_result_s
| extend AccountName = tostring(split(actor_alternateId_s, "@")[0]), AccountUPNSuffix = tostring(split(actor_alternateId_s, "@")[1])
requiredDataConnectors:
- dataTypes:
  - Okta_CL
  connectorId: OktaSSO
- dataTypes:
  - OktaSSO
  connectorId: OktaSSOv2
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Okta%20Single%20Sign-On/Analytic%20Rules/MFAFatigue.yaml
version: 1.1.3
status: Available
alertDetailsOverride:
  alertDescriptionFormat: Okta session {{authenticationContext_externalSessionId_s}} recorded {{pushes}} MFA pushes, over {{seconds}} seconds.
  alertDisplayNameFormat: MFA fatigue suspected for user {{actor_displayName_s}} ({{actor_alternateId_s}})
customDetails:
  TotalAttempts: totalattempts
  Duration: seconds
  SessionId: authenticationContext_externalSessionId_s
  Denies: failures
  Pushes: pushes
  Successes: successes
queryFrequency: 1h
query: |
  // Adjust threshold for MFA pushes to reduce noise
  let PushThreshold = 10;
  OktaSSO
  | where ((eventType_s =="user.authentication.auth_via_mfa" and column_ifexists('debugContext_debugData_factor_s', '') == "OKTA_VERIFY_PUSH") or eventType_s == "system.push.send_factor_verify_push" or eventType_s == "user.mfa.okta_verify.deny_push") 
  | summarize IPAddress = make_set(client_ipAddress_s,100), City = make_set(client_geographicalContext_city_s,100),
            successes = countif(eventType_s == "user.authentication.auth_via_mfa"),
            denies = countif(eventType_s == "user.mfa.okta_verify.deny_push"),
            pushes = countif(eventType_s == "system.push.send_factor_verify_push") by TimeGenerated, authenticationContext_externalSessionId_s, actor_alternateId_s,actor_displayName_s, outcome_result_s 
  | summarize lasttime = max(TimeGenerated), firsttime = min(TimeGenerated),
            successes = sum(successes), failures = sum(denies), pushes = sum(pushes) by  authenticationContext_externalSessionId_s, actor_alternateId_s,actor_displayName_s, outcome_result_s 
  | extend seconds = lasttime - firsttime
  | where pushes > PushThreshold
  | extend totalattempts = successes + failures
  | extend finding = case(
              failures == pushes and pushes > 1, "Authentication attempts not successful because multiple pushes denied",
              totalattempts == 0, "Multiple pushes sent and ignored",
              successes > 0 and pushes > 3, "Multiple pushes sent, eventual successful authentication!",
              "Normal authentication pattern")
  | project lasttime, firsttime, seconds, actor_alternateId_s, actor_displayName_s, authenticationContext_externalSessionId_s, pushes, successes, failures, totalattempts, finding, outcome_result_s
  | extend AccountName = tostring(split(actor_alternateId_s, "@")[0]), AccountUPNSuffix = tostring(split(actor_alternateId_s, "@")[1])
tactics:
- CredentialAccess
relevantTechniques:
- T1621
id: c2697b81-7fe9-4f57-ba1d-de46c6f91f9c
triggerThreshold: 0
triggerOperator: gt
name: MFA Fatigue (OKTA)
queryPeriod: 1h
severity: Medium
entityMappings:
- fieldMappings:
  - columnName: AccountName
    identifier: Name
  - columnName: AccountUPNSuffix
    identifier: UPNSuffix
  entityType: Account
kind: Scheduled
description: |
  MFA fatigue attack is a cybersecurity threat where attackers exploit user exhaustion from multi-factor authentication prompts to trick them into approving multi-factor authentication prompts, potentially compromising their own security. The query identifies MFA fatigue attempts in Okta by correlating repeated Okta Verify push requests, denied pushes, and eventual successful MFA authentication within the same session. 
  Ref: https://www.okta.com/blog/identity-security/mfa-fatigue-growing-security-concern/.
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "apiVersion": "2024-01-01-preview",
      "id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/c2697b81-7fe9-4f57-ba1d-de46c6f91f9c')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/c2697b81-7fe9-4f57-ba1d-de46c6f91f9c')]",
      "properties": {
        "alertDetailsOverride": {
          "alertDescriptionFormat": "Okta session {{authenticationContext_externalSessionId_s}} recorded {{pushes}} MFA pushes, over {{seconds}} seconds.",
          "alertDisplayNameFormat": "MFA fatigue suspected for user {{actor_displayName_s}} ({{actor_alternateId_s}})"
        },
        "alertRuleTemplateName": "c2697b81-7fe9-4f57-ba1d-de46c6f91f9c",
        "customDetails": {
          "Denies": "failures",
          "Duration": "seconds",
          "Pushes": "pushes",
          "SessionId": "authenticationContext_externalSessionId_s",
          "Successes": "successes",
          "TotalAttempts": "totalattempts"
        },
        "description": "MFA fatigue attack is a cybersecurity threat where attackers exploit user exhaustion from multi-factor authentication prompts to trick them into approving multi-factor authentication prompts, potentially compromising their own security. The query identifies MFA fatigue attempts in Okta by correlating repeated Okta Verify push requests, denied pushes, and eventual successful MFA authentication within the same session. \nRef: https://www.okta.com/blog/identity-security/mfa-fatigue-growing-security-concern/.\n",
        "displayName": "MFA Fatigue (OKTA)",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "AccountName",
                "identifier": "Name"
              },
              {
                "columnName": "AccountUPNSuffix",
                "identifier": "UPNSuffix"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Okta%20Single%20Sign-On/Analytic%20Rules/MFAFatigue.yaml",
        "query": "// Adjust threshold for MFA pushes to reduce noise\nlet PushThreshold = 10;\nOktaSSO\n| where ((eventType_s ==\"user.authentication.auth_via_mfa\" and column_ifexists('debugContext_debugData_factor_s', '') == \"OKTA_VERIFY_PUSH\") or eventType_s == \"system.push.send_factor_verify_push\" or eventType_s == \"user.mfa.okta_verify.deny_push\") \n| summarize IPAddress = make_set(client_ipAddress_s,100), City = make_set(client_geographicalContext_city_s,100),\n          successes = countif(eventType_s == \"user.authentication.auth_via_mfa\"),\n          denies = countif(eventType_s == \"user.mfa.okta_verify.deny_push\"),\n          pushes = countif(eventType_s == \"system.push.send_factor_verify_push\") by TimeGenerated, authenticationContext_externalSessionId_s, actor_alternateId_s,actor_displayName_s, outcome_result_s \n| summarize lasttime = max(TimeGenerated), firsttime = min(TimeGenerated),\n          successes = sum(successes), failures = sum(denies), pushes = sum(pushes) by  authenticationContext_externalSessionId_s, actor_alternateId_s,actor_displayName_s, outcome_result_s \n| extend seconds = lasttime - firsttime\n| where pushes > PushThreshold\n| extend totalattempts = successes + failures\n| extend finding = case(\n            failures == pushes and pushes > 1, \"Authentication attempts not successful because multiple pushes denied\",\n            totalattempts == 0, \"Multiple pushes sent and ignored\",\n            successes > 0 and pushes > 3, \"Multiple pushes sent, eventual successful authentication!\",\n            \"Normal authentication pattern\")\n| project lasttime, firsttime, seconds, actor_alternateId_s, actor_displayName_s, authenticationContext_externalSessionId_s, pushes, successes, failures, totalattempts, finding, outcome_result_s\n| extend AccountName = tostring(split(actor_alternateId_s, \"@\")[0]), AccountUPNSuffix = tostring(split(actor_alternateId_s, \"@\")[1])\n",
        "queryFrequency": "PT1H",
        "queryPeriod": "PT1H",
        "severity": "Medium",
        "status": "Available",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "CredentialAccess"
        ],
        "techniques": [
          "T1621"
        ],
        "templateVersion": "1.1.3",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}