Detection rules › Panther
Panther rules: duo
Duo Admin App Integration Secret Key Viewed
#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
actionisintegration_skey_view
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| 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
#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
actionisbypass_create
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| 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
#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
actionisbypass_view
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| 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
#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
actionisadmin_create
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| 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
#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
actionisadmin_lockout
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| 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
#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
actionisadmin_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.
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| field:"action" kind:eq value:"admin_2fa_error" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
reporting_admin | username |
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
#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
actionisupdate_admin_factor_restrictions
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| 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
#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
actionisintegration_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.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| 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
#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
actionispolicy_update
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| 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
#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
actionisadmin_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.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| 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
#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
actionisuser_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.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| 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
#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
resultisfraud
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
result | eq |
| field:"result" kind:eq value:"fraud" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
factor | |
reason | |
user | user.name |
os | access_device.os |
ip_access | access_device.ip |
ip_auth | auth_device.ip |
application | application.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
#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
reasonisanomalous_pushresultisdenied
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
reason | eq |
| field:"reason" kind:eq value:"anomalous_push" |
result | eq |
| field:"result" kind:eq value:"denied" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
factor | |
reason | |
user | user.name |
os | access_device.os |
ip_access | access_device.ip |
ip_auth | auth_device.ip |
application | application.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
#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
reasonisbypass_userresultissuccess
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
reason | eq |
| field:"reason" kind:eq value:"bypass_user" |
result | eq |
| field:"result" kind:eq value:"success" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
factor | |
reason | |
user | user.name |
os | access_device.os |
ip_access | access_device.ip |
ip_auth | auth_device.ip |
application | application.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
#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
reasonis one ofendpoint_is_not_in_management_system,endpoint_failed_google_verification,endpoint_is_not_trusted,could_not_determine_if_endpoint_was_trusted,invalid_device
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
reason | in |
| field:"reason" kind:in |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
factor | |
reason | |
user | user.name |
os | access_device.os |
ip_access | access_device.ip |
ip_auth | auth_device.ip |
application | application.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"
}
}