Detection rules › Panther

Panther rules: duo

Duo Admin App Integration Secret Key Viewed

#
Severity
medium
Log types
Duo.Administrator
Reference
duo.com
Source
github.com/panther-labs/panther-analysis

An administrator viewed a Secret Key for an Application Integration

Detection logic

def rule(event):
    # Return True to match the log event and trigger an alert.
    return event.get("action", "") == "integration_skey_view"


def title(event):
    # If no 'dedup' function is defined, the return value of
    # this method will act as deduplication string.
    return (
        f"'Duo: [{event.get('username', '<NO_USER_FOUND>')}] viewed "
        f"the Secret Key for Application [{event.get('object', '<NO_OBJECT_FOUND>')}]"
    )

Rule specification

AnalysisType: rule
Description: An administrator viewed a Secret Key for an Application Integration
DisplayName: "Duo Admin App Integration Secret Key Viewed"
Enabled: true
Filename: duo_admin_app_integration_secret_key_viewed.py
Reference: https://duo.com/docs/adminapi
Runbook: The security of your Duo application is tied to the security of your secret key (skey). Secure it as you would any sensitive credential. Don't share it with unauthorized individuals or email it to anyone under any circumstances!
Severity: Medium
DedupPeriodMinutes: 60
LogTypes:
  - Duo.Administrator
RuleID: "Duo.Admin.App.Integration.Secret.Key.Viewed"
Threshold: 1

Stages and Predicates

Fires on Duo.Administrator events when the condition below holds.

Condition

  • action is integration_skey_view

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
actioneq
  • integration_skey_view
field:"action" kind:eq value:"integration_skey_view"

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
username
object

Response runbook

The security of your Duo application is tied to the security of your secret key (skey). Secure it as you would any sensitive credential. Don't share it with unauthorized individuals or email it to anyone under any circumstances!

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "action": "integration_skey_view",
  "isotimestamp": "2022-12-14 20:09:57",
  "object": "Example Integration Name",
  "timestamp": "2022-12-14 20:09:57",
  "username": "Homer Simpson"
}

Duo Admin Bypass Code Created

#
Severity
medium
Log types
Duo.Administrator
Reference
duo.com
Source
github.com/panther-labs/panther-analysis

A Duo administrator created an MFA bypass code for an application.

Detection logic

def rule(event):
    # Return True to match the log event and trigger an alert.
    return event.get("action", "") == "bypass_create"


def title(event):
    # If no 'dedup' function is defined, the return value of
    # this method will act as deduplication string.
    return (
        f"Duo: [{event.get('username', '<NO_USER_FOUND>')}] created "
        f"a MFA bypass code for [{event.get('object', '<NO_OBJECT_FOUND>')}]"
    )

Rule specification

AnalysisType: rule
Description: A Duo administrator created an MFA bypass code for an application.
DisplayName: "Duo Admin Bypass Code Created"
Enabled: true
Filename: duo_admin_bypass_code_created.py
Runbook: Confirm this was authorized and necessary behavior.
Reference: https://duo.com/docs/administration-users#generating-a-bypass-code
Severity: Medium
DedupPeriodMinutes: 60
LogTypes:
  - Duo.Administrator
RuleID: "Duo.Admin.Bypass.Code.Created"
Threshold: 1

Stages and Predicates

Fires on Duo.Administrator events when the condition below holds.

Condition

  • action is bypass_create

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
actioneq
  • bypass_create
field:"action" kind:eq value:"bypass_create"

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
username
object

Response runbook

Confirm this was authorized and necessary behavior.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "action": "bypass_create",
  "description": "{\"bypass\": \"\", \"count\": 1, \"valid_secs\": 3600, \"auto_generated\": true, \"remaining_uses\": 1, \"user_id\": \"D12345\", \"bypass_code_ids\": [\"A12345\"]}",
  "isotimestamp": "2022-12-14 21:17:39",
  "object": "target@example.io",
  "timestamp": "2022-12-14 21:17:39",
  "username": "Homer Simpson"
}

Duo Admin Bypass Code Viewed

#
Severity
medium
Log types
Duo.Administrator
Reference
duo.com
Source
github.com/panther-labs/panther-analysis

An administrator viewed the MFA bypass code for a user.

Detection logic

def rule(event):
    # Return True to match the log event and trigger an alert.
    return event.get("action", "") == "bypass_view"


def title(event):
    # If no 'dedup' function is defined, the return value
    # of this method will act as deduplication string.
    return (
        f"Duo: [{event.get('username', '<NO_USER_FOUND>')}] viewed "
        f"an MFA bypass code for [{event.get('object', '<NO_OBJECT_FOUND>')}]."
    )

Rule specification

AnalysisType: rule
Description: An administrator viewed the MFA bypass code for a user.
DisplayName: "Duo Admin Bypass Code Viewed"
Enabled: true
Filename: duo_admin_bypass_code_viewed.py
Reference: https://duo.com/docs/adminapi
Runbook: Confirm this behavior is authorized. The security of your Duo application is tied to the security of your secret key (skey). Secure it as you would any sensitive credential. You should not share it with unauthorized individuals or email it to anyone under any circumstances!
Severity: Medium
DedupPeriodMinutes: 60
LogTypes:
  - Duo.Administrator
RuleID: "Duo.Admin.Bypass.Code.Viewed"
Threshold: 1

Stages and Predicates

Fires on Duo.Administrator events when the condition below holds.

Condition

  • action is bypass_view

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
actioneq
  • bypass_view
field:"action" kind:eq value:"bypass_view"

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
username
object

Response runbook

Confirm this behavior is authorized. The security of your Duo application is tied to the security of your secret key (skey). Secure it as you would any sensitive credential. You should not share it with unauthorized individuals or email it to anyone under any circumstances!

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "action": "bypass_view",
  "description": "{\"user_id\": \"D1234\", \"bypass_code_id\": \"D5678\"}",
  "isotimestamp": "2022-12-14 21:17:54",
  "object": "target@example.io",
  "timestamp": "2022-12-14 21:17:54",
  "username": "Homer Simpson"
}

Duo Admin Create Admin

#
Severity
high
Log types
Duo.Administrator
Reference
duo.com
Source
github.com/panther-labs/panther-analysis

A new Duo Administrator was created.

Detection logic

from panther_duo_helpers import deserialize_administrator_log_event_description, duo_alert_context


def rule(event):
    return event.get("action") == "admin_create"


def title(event):
    event_description = deserialize_administrator_log_event_description(event)
    return (
        f"Duo: [{event.get('username', '<username_not_found>')}] "
        "created a new admin account: "
        f"[{event_description.get('name', '<name_not_found>')}] "
        f"[{event_description.get('email', '<email_not_found>')}]."
    )


def alert_context(event):
    return duo_alert_context(event)

Rule specification

AnalysisType: rule
Description: "A new Duo Administrator was created. "
DisplayName: "Duo Admin Create Admin"
Enabled: true
Filename: duo_admin_create_admin.py
Reference: https://duo.com/docs/administration-admins#add-an-administrator
Severity: High
DedupPeriodMinutes: 60
LogTypes:
  - Duo.Administrator
RuleID: "Duo.Admin.Create.Admin"
Threshold: 1

Stages and Predicates

Fires on Duo.Administrator events when the condition below holds.

Condition

  • action is admin_create

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
actioneq
  • admin_create
field:"action" kind:eq value:"admin_create"

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
action
description
username
timestamp

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "action": "admin_create",
  "description": "{\"name\": \"Homer Simpson\", \"phone\": null, \"is_temporary_password\": false, \"email\": \"homer.simpson@simpsons.com\", \"hardtoken\": null, \"role\": \"Owner\", \"status\": \"Pending Activation\", \"restricted_by_admin_units\": false, \"administrative_units\": \"\"}",
  "isotimestamp": "2023-01-17 16:47:54",
  "object": "Homer Simpson",
  "timestamp": "2023-01-17 16:47:54",
  "username": "Bart Simpson"
}

Duo Admin Lockout

#
Severity
medium
Log types
Duo.Administrator
Reference
duo.com
Source
github.com/panther-labs/panther-analysis

Alert when a duo administrator is locked out of their account.

Detection logic

import json


def rule(event):
    # Return True to match the log event and trigger an alert.
    return event.get("action", "") == "admin_lockout"


def title(event):
    # If no 'dedup' function is defined, the return value
    # of this method will act as deduplication string.
    try:
        desc = json.loads(event.get("description", {}))
        message = desc.get("message", "<NO_MESSAGE_FOUND>")[:-1]
    except ValueError:
        message = "Invalid Json"
    return (
        f"Duo Admin [{event.get('username', '<NO_USER_FOUND>')}] is "
        f"locked out. Reason: [{message}]."
    )

Rule specification

AnalysisType: rule
Description: Alert when a duo administrator is locked out of their account.
DisplayName: "Duo Admin Lockout"
Enabled: true
Filename: duo_admin_lockout.py
Reference: https://duo.com/docs/adminapi
Severity: Medium
DedupPeriodMinutes: 60
LogTypes:
  - Duo.Administrator
RuleID: "Duo.Admin.Lockout"
Threshold: 1

Stages and Predicates

Fires on Duo.Administrator events when the condition below holds.

Condition

  • action is admin_lockout

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
actioneq
  • admin_lockout
field:"action" kind:eq value:"admin_lockout"

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
username

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "action": "admin_lockout",
  "description": "\"message\": \"Admin temporarily locked out due to too many passcode attempts.\"",
  "isotimestamp": "2022-12-14 21:02:03",
  "timestamp": "2022-12-14 21:02:03",
  "username": "Homer Simpson"
}

Duo Admin Marked Push Fraudulent

#
Severity
medium
Log types
Duo.Administrator
Tags
Duo
Reference
duo.com
Source
github.com/panther-labs/panther-analysis

A Duo push was marked fraudulent by an admin.

Detection logic

from panther_duo_helpers import deserialize_administrator_log_event_description


def rule(event):
    event_description = deserialize_administrator_log_event_description(event)

    return (
        event.get("action") == "admin_2fa_error"
        and "fraudulent" in event_description.get("error", "").lower()
    )


def title(event):
    event_description = deserialize_administrator_log_event_description(event)
    admin_username = event.get("username", "Unknown")
    user_email = event_description.get("email", "Unknown")

    return f"Duo Admin [{admin_username}] denied due to an anomalous 2FA push for [{user_email}]"


def alert_context(event):
    event_description = deserialize_administrator_log_event_description(event)

    return {
        "reason": event_description.get("error", ""),
        "reporting_admin": event.get("username", ""),
        "user": event_description.get("email", ""),
        "ip_address": event_description.get("ip_address", ""),
    }

Rule specification

AnalysisType: rule
Filename: duo_admin_marked_push_fraudulent.py
RuleID: "DUO.Admin.Action.MarkedFraudulent"
DisplayName: "Duo Admin Marked Push Fraudulent"
Enabled: true
DedupPeriodMinutes: 15
LogTypes:
  - Duo.Administrator
Tags:
  - Duo
Severity: Medium
Description: A Duo push was marked fraudulent by an admin.
Reference: https://duo.com/docs/adminapi#administrator-logs
Runbook: Follow up with the administrator to determine reasoning for marking fraud.

Stages and Predicates

Fires on Duo.Administrator events when the condition below holds.

Condition

  • action is admin_2fa_error

This rule also runs imperative logic the parser cannot express as a filter. The conditions above are the structured part it could extract.

Alert deduplication
repeat matches within 15m group into one alert

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
actioneq
  • admin_2fa_error
field:"action" kind:eq value:"admin_2fa_error"

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

FieldSource
reporting_adminusername

Response runbook

Follow up with the administrator to determine reasoning for marking fraud.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "action": "admin_2fa_error",
  "description": "{\"ip_address\": \"12.12.12.12\", \"email\": \"example@example.io\", \"factor\": \"push\", \"error\": \"Login request reported as fraudulent.\"}",
  "isotimestamp": "2022-12-14 20:11:53",
  "timestamp": "2022-12-14 20:11:53",
  "username": "John P. Admin"
}

Duo Admin MFA Restrictions Updated

#
Severity
medium
Log types
Duo.Administrator
Reference
duo.com
Source
github.com/panther-labs/panther-analysis

Detects changes to allowed MFA factors administrators can use to log into the admin panel.

Detection logic

from panther_duo_helpers import duo_alert_context


def rule(event):
    return event.get("action") == "update_admin_factor_restrictions"


def title(event):
    return "Duo Admin MFA Restrictions Updated " f"by [{event.get('username','<user_not_found>')}]"


def alert_context(event):
    return duo_alert_context(event)

Rule specification

AnalysisType: rule
Description: Detects changes to allowed MFA factors administrators can use to log into the admin panel.
DisplayName: "Duo Admin MFA Restrictions Updated"
Enabled: true
Filename: duo_admin_mfa_restrictions_updated.py
Reference: https://duo.com/docs/essentials-overview
Severity: Medium
DedupPeriodMinutes: 60
LogTypes:
  - Duo.Administrator
RuleID: "Duo.Admin.MFA.Restrictions.Updated"
Threshold: 1

Stages and Predicates

Fires on Duo.Administrator events when the condition below holds.

Condition

  • action is update_admin_factor_restrictions

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
actioneq
  • update_admin_factor_restrictions
field:"action" kind:eq value:"update_admin_factor_restrictions"

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
action
description
username
timestamp

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "action": "update_admin_factor_restrictions",
  "description": "{\"allowed_factors\": \"Duo mobile passcodes, Hardware tokens, Duo push, Yubikey aes\"}",
  "isotimestamp": "2022-02-21 21:48:06",
  "timestamp": "2022-02-21 21:48:06",
  "username": "Homer Simpson"
}

Duo Admin New Admin API App Integration

#
Severity
high
Log types
Duo.Administrator
Reference
duo.com
Source
github.com/panther-labs/panther-analysis

Identifies creation of new Admin API integrations for Duo.

Detection logic

from panther_duo_helpers import deserialize_administrator_log_event_description, duo_alert_context


def rule(event):
    if event.get("action") == "integration_create":
        description = deserialize_administrator_log_event_description(event)
        integration_type = description.get("type")
        return integration_type == "Admin API"
    return False


def title(event):
    return (
        f"Duo: [{event.get('username', '<username_not_found>')}] "
        "created a new Admin API integration "
        f"to [{event.get('object', '<object_not_found>')}]"
    )


def alert_context(event):
    return duo_alert_context(event)

Rule specification

AnalysisType: rule
Description: Identifies creation of new Admin API integrations for Duo.
DisplayName: "Duo Admin New Admin API App Integration"
Enabled: true
Filename: duo_admin_new_admin_api_app_integration.py
Reference: https://duo.com/docs/adminapi#overview
Severity: High
DedupPeriodMinutes: 60
LogTypes:
  - Duo.Administrator
RuleID: "Duo.Admin.New.Admin.API.App.Integration"
Threshold: 1

Stages and Predicates

Fires on Duo.Administrator events when the condition below holds.

Condition

  • action is integration_create

This rule also runs imperative logic the parser cannot express as a filter. The conditions above are the structured part it could extract.

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
actioneq
  • integration_create
field:"action" kind:eq value:"integration_create"

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
action
description
username
timestamp
object

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "action": "integration_create",
  "description": "{\"greeting\": \"\", \"notes\": \"\", \"offline_auth_enabled\": 0, \"offline_max_days\": 0, \"offline_max_attempts\": 0, \"type\": \"Admin API\", \"raw_type\": \"adminapi\", \"name\": \"Admin API\", \"self_service_allowed\": false, \"username_normalization_policy\": \"None\", \"missing_web_referer_policy\": \"deny\", \"networks_for_api_access\": \"\", \"group_access\": \"\"}",
  "isotimestamp": "2021-11-30 17:15:33",
  "object": "Admin API",
  "timestamp": "2021-11-30 17:15:33",
  "username": "Homer Simpson"
}

Duo Admin Policy Updated

#
Severity
medium
Log types
Duo.Administrator
Reference
duo.com
Source
github.com/panther-labs/panther-analysis

A Duo Administrator updated a Policy, which governs how users authenticate.

Detection logic

from panther_duo_helpers import duo_alert_context


def rule(event):
    return event.get("action") == "policy_update"


def title(event):
    return (
        f"Duo: [{event.get('username', '<username_not_found>')}] "
        f"updated [{event.get('object', 'Duo Policy')}]."
    )


def alert_context(event):
    return duo_alert_context(event)

Rule specification

AnalysisType: rule
Description: A Duo Administrator updated a Policy, which governs how users authenticate.
DisplayName: "Duo Admin Policy Updated"
Enabled: true
Filename: duo_admin_policy_updated.py
Reference: https://duo.com/docs/policy#authenticators-policy-settings
Severity: Medium
DedupPeriodMinutes: 60
LogTypes:
  - Duo.Administrator
RuleID: "Duo.Admin.Policy.Updated"
Threshold: 1

Stages and Predicates

Fires on Duo.Administrator events when the condition below holds.

Condition

  • action is policy_update

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
actioneq
  • policy_update
field:"action" kind:eq value:"policy_update"

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
action
description
username
timestamp
object

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "action": "policy_update",
  "description": "{\"adaptive_auth_display_unit\": \"days\", \"trusted_mobile_endpoint_policy\": \"no action\", \"adaptive_auth_hours\": 0, \"admin_email\": \"homer.simpson@simpsons.com\", \"allow_factor_u2f\": false, \"device_certificate_policy\": \"no action\", \"allow_factor_phone\": false, \"local_trusted_sessions_display_val\": 0, \"allow_adaptive_auth\": false, \"local_trusted_sessions_display_unit\": \"days\", \"allow_factor_sms\": false}",
  "isotimestamp": "2022-02-21 21:48:48",
  "object": "Global Policy",
  "timestamp": "2022-02-21 21:48:48",
  "username": "Homer Simpson"
}

Duo Admin SSO SAML Requirement Disabled

#
Severity
medium
Log types
Duo.Administrator
Reference
duo.com
Source
github.com/panther-labs/panther-analysis

Detects when SAML Authentication for Administrators is marked as Disabled or Optional.

Detection logic

from panther_duo_helpers import deserialize_administrator_log_event_description, duo_alert_context


def rule(event):
    if event.get("action") == "admin_single_sign_on_update":
        description = deserialize_administrator_log_event_description(event)
        enforcement_status = description.get("enforcement_status", "required")
        return enforcement_status != "required"
    return False


def title(event):
    description = deserialize_administrator_log_event_description(event)
    return (
        f"Duo: [{event.get('username', '<username_not_found>')}] "
        "changed SAML authentication requirements for Administrators "
        f"to [{description.get('enforcement_status', '<enforcement_status_not_found>')}]"
    )


def alert_context(event):
    return duo_alert_context(event)

Rule specification

AnalysisType: rule
Description: Detects when SAML Authentication for Administrators is marked as Disabled or Optional.
DisplayName: "Duo Admin SSO SAML Requirement Disabled"
Enabled: true
Filename: duo_admin_sso_saml_requirement_disabled.py
Reference: https://duo.com/docs/sso#saml:~:text=Modify%20Authentication%20Sources
Severity: Medium
DedupPeriodMinutes: 60
LogTypes:
  - Duo.Administrator
RuleID: "Duo.Admin.SSO.SAML.Requirement.Disabled"
Threshold: 1

Stages and Predicates

Fires on Duo.Administrator events when the condition below holds.

Condition

  • action is admin_single_sign_on_update

This rule also runs imperative logic the parser cannot express as a filter. The conditions above are the structured part it could extract.

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
actioneq
  • admin_single_sign_on_update
field:"action" kind:eq value:"admin_single_sign_on_update"

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
action
description
username
timestamp

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "action": "admin_single_sign_on_update",
  "description": "{\"enforcement_status\": \"disabled\"}",
  "isotimestamp": "2021-10-12 21:29:22",
  "timestamp": "2021-10-12 21:29:22",
  "username": "Homer Simpson"
}

Duo Admin User MFA Bypass Enabled

#
Severity
medium
Log types
Duo.Administrator
Reference
duo.com
Source
github.com/panther-labs/panther-analysis

An Administrator enabled a user to authenticate without MFA.

Detection logic

from panther_duo_helpers import deserialize_administrator_log_event_description, duo_alert_context


def rule(event):
    if event.get("action") == "user_update":
        description = deserialize_administrator_log_event_description(event)
        if "status" in description:
            return description.get("status") == "Bypass"
    return False


def title(event):
    return (
        f"Duo: [{event.get('username', '<username_not_found>')}] "
        f"updated account [{event.get('object', '<object_not_found>')}] "
        "to not require two-factor authentication."
    )


def alert_context(event):
    return duo_alert_context(event)

Rule specification

AnalysisType: rule
Description: An Administrator enabled a user to authenticate without MFA.
DisplayName: "Duo Admin User MFA Bypass Enabled"
Enabled: true
Filename: duo_admin_user_mfa_bypass_enabled.py
Reference: https://duo.com/docs/policy#authentication-policy
Severity: Medium
DedupPeriodMinutes: 60
LogTypes:
  - Duo.Administrator
RuleID: "Duo.Admin.User.MFA.Bypass.Enabled"
Threshold: 1

Stages and Predicates

Fires on Duo.Administrator events when the condition below holds.

Condition

  • action is user_update

This rule also runs imperative logic the parser cannot express as a filter. The conditions above are the structured part it could extract.

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
actioneq
  • user_update
field:"action" kind:eq value:"user_update"

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
action
description
username
timestamp
object

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "action": "user_update",
  "description": "{\"status\": \"Bypass\"}",
  "isotimestamp": "2021-10-05 22:45:33",
  "object": "bart.simpson@simpsons.com",
  "timestamp": "2021-10-05 22:45:33",
  "username": "Homer Simpson"
}

Duo User Action Reported as Fraudulent

#
Severity
medium
Log types
Duo.Authentication
Tags
Duo
Reference
duo.com
Source
github.com/panther-labs/panther-analysis

Alert when a user reports a Duo action as fraudulent.

Detection logic

def rule(event):
    return event.get("result") == "fraud"


def title(event):
    user = event.deep_get("user", "name", default="Unknown")
    return f"A Duo action was marked as fraudulent by [{user}]"


def alert_context(event):
    return {
        "factor": event.get("factor"),
        "reason": event.get("reason"),
        "user": event.deep_get("user", "name", default=""),
        "os": event.deep_get("access_device", "os", default=""),
        "ip_access": event.deep_get("access_device", "ip", default=""),
        "ip_auth": event.deep_get("auth_device", "ip", default=""),
        "application": event.deep_get("application", "name", default=""),
    }

Rule specification

AnalysisType: rule
Filename: duo_user_action_fraudulent.py
RuleID: "DUO.User.Action.Fraudulent"
DisplayName: "Duo User Action Reported as Fraudulent"
Enabled: true
DedupPeriodMinutes: 15
LogTypes:
  - Duo.Authentication
Tags:
  - Duo
Severity: Medium
Description: >
  Alert when a user reports a Duo action as fraudulent.
Reference: https://duo.com/docs/adminapi#authentication-logs
Runbook: Follow up with the user to confirm.

Stages and Predicates

Fires on Duo.Authentication events when the condition below holds.

Condition

  • result is fraud
Alert deduplication
repeat matches within 15m group into one alert

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
resulteq
  • fraud
field:"result" kind:eq value:"fraud"

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

FieldSource
factor
reason
useruser.name
osaccess_device.os
ip_accessaccess_device.ip
ip_authauth_device.ip
applicationapplication.name

Response runbook

Follow up with the user to confirm.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "access_device": {
    "ip": "12.12.112.25",
    "os": "Mac OS X"
  },
  "application": {
    "key": "D12345",
    "name": "Slack"
  },
  "auth_device": {
    "ip": "12.12.12.12"
  },
  "event_type": "authentication",
  "factor": "duo_push",
  "reason": "user_marked_fraud",
  "result": "fraud",
  "user": {
    "name": "example@example.io"
  }
}

Duo User Auth Denied For Anomalous Push

#
Severity
medium
Log types
Duo.Authentication
Tags
Duo
Reference
duo.com
Source
github.com/panther-labs/panther-analysis

A Duo authentication was denied due to an anomalous 2FA push.

Detection logic

def rule(event):
    return event.get("reason") == "anomalous_push" and event.get("result") == "denied"


def title(event):
    user = event.deep_get("user", "name", default="Unknown")
    return f"Duo Auth denied due to an anomalous 2FA push for [{user}]"


def alert_context(event):
    return {
        "factor": event.get("factor"),
        "reason": event.get("reason"),
        "user": event.deep_get("user", "name", default=""),
        "os": event.deep_get("access_device", "os", default=""),
        "ip_access": event.deep_get("access_device", "ip", default=""),
        "ip_auth": event.deep_get("auth_device", "ip", default=""),
        "application": event.deep_get("application", "name", default=""),
    }

Rule specification

AnalysisType: rule
Filename: duo_user_anomalous_push.py
RuleID: "DUO.User.Denied.AnomalousPush"
DisplayName: "Duo User Auth Denied For Anomalous Push"
Enabled: true
DedupPeriodMinutes: 15
LogTypes:
  - Duo.Authentication
Tags:
  - Duo
Severity: Medium
Description: >
  A Duo authentication was denied due to an anomalous 2FA push.
Reference: https://duo.com/docs/adminapi#authentication-logs
Runbook: Follow up with the user to confirm they intended several pushes in quick succession.

Stages and Predicates

Fires on Duo.Authentication events when all of the conditions below hold.

Condition

  • reason is anomalous_push
  • result is denied
Alert deduplication
repeat matches within 15m group into one alert

Indicators

These rows show field, operator, and value matches.

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

FieldSource
factor
reason
useruser.name
osaccess_device.os
ip_accessaccess_device.ip
ip_authauth_device.ip
applicationapplication.name

Response runbook

Follow up with the user to confirm they intended several pushes in quick succession.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "access_device": {
    "ip": "12.12.112.25",
    "os": "Mac OS X"
  },
  "application": {
    "key": "D12345",
    "name": "Slack"
  },
  "auth_device": {
    "ip": "12.12.12.12"
  },
  "event_type": "authentication",
  "factor": "duo_push",
  "reason": "anomalous_push",
  "result": "denied",
  "user": {
    "name": "example@example.io"
  }
}

Duo User Bypass Code Used

#
Severity
low
Log types
Duo.Authentication
Tags
Duo
Reference
duo.com
Source
github.com/panther-labs/panther-analysis

A Duo user's bypass code was used to authenticate

Detection logic

def rule(event):
    return event.get("reason") == "bypass_user" and event.get("result") == "success"


def title(event):
    user = event.deep_get("user", "name", default="Unknown")
    return f"Bypass code for Duo User [{user}] used"


def alert_context(event):
    return {
        "factor": event.get("factor"),
        "reason": event.get("reason"),
        "user": event.deep_get("user", "name", default=""),
        "os": event.deep_get("access_device", "os", default=""),
        "ip_access": event.deep_get("access_device", "ip", default=""),
        "ip_auth": event.deep_get("auth_device", "ip", default=""),
        "application": event.deep_get("application", "name", default=""),
    }

Rule specification

AnalysisType: rule
Filename: duo_user_bypass_code_used.py
RuleID: "DUO.User.BypassCode.Used"
DisplayName: "Duo User Bypass Code Used"
Enabled: true
DedupPeriodMinutes: 5
LogTypes:
  - Duo.Authentication
Tags:
  - Duo
Severity: Low
Description: A Duo user's bypass code was used to authenticate
Reference: https://duo.com/docs/adminapi#authentication-logs
Runbook: Follow up with the user to confirm they used the bypass code themselves.

Stages and Predicates

Fires on Duo.Authentication events when all of the conditions below hold.

Condition

  • reason is bypass_user
  • result is success
Alert deduplication
repeat matches within 5m group into one alert

Indicators

These rows show field, operator, and value matches.

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

FieldSource
factor
reason
useruser.name
osaccess_device.os
ip_accessaccess_device.ip
ip_authauth_device.ip
applicationapplication.name

Response runbook

Follow up with the user to confirm they used the bypass code themselves.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "access_device": {
    "ip": "12.12.112.25",
    "os": "Mac OS X"
  },
  "application": {
    "key": "D12345",
    "name": "Slack"
  },
  "auth_device": {
    "ip": "12.12.12.12"
  },
  "event_type": "authentication",
  "factor": "duo_push",
  "reason": "bypass_user",
  "result": "success",
  "user": {
    "name": "example@example.io"
  }
}

Duo User Denied For Endpoint Error

#
Severity
medium
Log types
Duo.Authentication
Tags
Duo
Reference
duo.com
Source
github.com/panther-labs/panther-analysis

A Duo user's authentication was denied due to a suspicious error on the endpoint

Detection logic

def rule(event):
    endpoint_reasons = [
        "endpoint_is_not_in_management_system",
        "endpoint_failed_google_verification",
        "endpoint_is_not_trusted",
        "could_not_determine_if_endpoint_was_trusted",
        "invalid_device",
    ]
    return event.get("reason", "") in endpoint_reasons


def title(event):
    user = event.deep_get("user", "name", default="Unknown")
    reason = event.get("reason", "Unknown")
    return f"Duo User [{user}] encountered suspicious endpoint issue [{reason}]"


def alert_context(event):
    return {
        "factor": event.get("factor"),
        "reason": event.get("reason"),
        "user": event.deep_get("user", "name", default=""),
        "os": event.deep_get("access_device", "os", default=""),
        "ip_access": event.deep_get("access_device", "ip", default=""),
        "ip_auth": event.deep_get("auth_device", "ip", default=""),
        "application": event.deep_get("application", "name", default=""),
    }

Rule specification

AnalysisType: rule
Filename: duo_user_endpoint_failure_multi.py
RuleID: "DUO.User.Endpoint.Failure"
DisplayName: "Duo User Denied For Endpoint Error"
Enabled: true
DedupPeriodMinutes: 15
LogTypes:
  - Duo.Authentication
Tags:
  - Duo
Severity: Medium
Description: A Duo user's authentication was denied due to a suspicious error on the endpoint
Reference: https://duo.com/docs/adminapi#authentication-logs
Runbook: Follow up with the endpoint owner to see status. Follow up with user to verify attempts.

Stages and Predicates

Fires on Duo.Authentication events when the condition below holds.

Condition

  • reason is one of endpoint_is_not_in_management_system, endpoint_failed_google_verification, endpoint_is_not_trusted, could_not_determine_if_endpoint_was_trusted, invalid_device
Alert deduplication
repeat matches within 15m group into one alert

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
reasonin
  • could_not_determine_if_endpoint_was_trusted
  • endpoint_failed_google_verification
  • endpoint_is_not_in_management_system
  • endpoint_is_not_trusted
  • invalid_device
field:"reason" kind:in

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

FieldSource
factor
reason
useruser.name
osaccess_device.os
ip_accessaccess_device.ip
ip_authauth_device.ip
applicationapplication.name

Response runbook

Follow up with the endpoint owner to see status. Follow up with user to verify attempts.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "access_device": {
    "ip": "12.12.112.25",
    "os": "Mac OS X"
  },
  "application": {},
  "auth_device": {
    "ip": "12.12.12.12"
  },
  "event_type": "authentication",
  "factor": "duo_push",
  "reason": "endpoint_is_not_in_management_system",
  "result": "denied",
  "user": {
    "name": "example@example.io"
  }
}