Detection rules › Panther

Panther rules: zendesk

Enabled Zendesk Support to Assume Users

#
Severity
medium
Log types
Zendesk.Audit
Tags
Zendesk, Lateral Movement:Use Alternate Authentication Material
Reference
support.zendesk.com
Source
github.com/panther-labs/panther-analysis

User enabled or disabled zendesk support user assumption.

MITRE ATT&CK coverage

TacticTechniques
Lateral Movement

Detection logic

USER_SUSPENSION_ACTIONS = {
    "create",
    "update",
}


def rule(event):
    return (
        event.get("source_type") == "account_setting"
        and event.get("action", "") in USER_SUSPENSION_ACTIONS
        and event.get("source_label", "").lower() in {"account assumption", "assumption duration"}
    )


def title(event):
    return f"A user [{event.udm('actor_user')}] updated zendesk support user assumption settings"

Rule specification

AnalysisType: rule
Filename: zendesk_user_assumption.py
RuleID: "Zendesk.UserAssumption"
DisplayName: "Enabled Zendesk Support to Assume Users"
Enabled: true
LogTypes:
  - Zendesk.Audit
Tags:
  - Zendesk
  - Lateral Movement:Use Alternate Authentication Material
Reports:
  MITRE ATT&CK:
    - TA0008:T1550
Severity: Medium
Description: User enabled or disabled zendesk support user assumption.
Runbook: >
  Investigate whether allowing zendesk support to assume users is necessary. If not, disable the feature.
Reference: https://support.zendesk.com/hc/en-us/articles/4408894200474-Assuming-end-users#:~:text=In%20Support%2C%20click%20the%20Customers,user%20in%20the%20information%20dialog
SummaryAttributes:
  - p_any_ip_addresses

Stages and Predicates

Fires on Zendesk.Audit events when all of the conditions below hold.

Condition

  • source_type is account_setting
  • action is one of create, update
  • source_label is one of account assumption, assumption duration (case-insensitive)

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
actionin
  • create
  • update
field:"action" kind:in
source_labelin
  • account assumption transforms: tolower
  • assumption duration transforms: tolower
field:"source_label" kind:in
source_typeeq
  • account_setting
field:"source_type" kind:eq value:"account_setting"

Output fields

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

Field
actor_user

Response runbook

Investigate whether allowing zendesk support to assume users is necessary. If not, disable the feature.

Worked example

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

Sample Test Event
{
  "action": "update",
  "action_label": "Updated",
  "actor_id": 123,
  "actor_name": "John Doe",
  "change_description": "Changed",
  "created_at": "2021-05-28T18:39:50Z",
  "id": 123456789123,
  "ip_address": "127.0.0.1",
  "p_log_type": "Zendesk.Audit",
  "source_id": 123,
  "source_label": "Account Assumption",
  "source_type": "account_setting",
  "url": "https://myzendek.zendesk.com/api/v2/audit_logs/111222333444.json"
}

Zendesk Account Owner Changed

#
Severity
high
Log types
Zendesk.Audit
Tags
Zendesk, Privilege Escalation:Valid Accounts
Reference
support.zendesk.com
Source
github.com/panther-labs/panther-analysis

Only one admin user can be the account owner. Ensure the change in ownership is expected.

MITRE ATT&CK coverage

TacticTechniques
Privilege Escalation

Detection logic

import re

from panther_zendesk_helpers import ZENDESK_CHANGE_DESCRIPTION

ZENDESK_OWNER_CHANGED = re.compile(
    r"Owner changed from (?P<old_owner>.+) to (?P<new_owner>[^$]+)", re.IGNORECASE
)


def rule(event):
    if event.get("action", "") == "update" and event.get("source_type", "") == "account":
        return event.get(ZENDESK_CHANGE_DESCRIPTION, "").lower().startswith("owner changed from ")
    return False


def title(event):
    old_owner = "<UNKNOWN_USER>"
    new_owner = "<UNKNOWN_USER>"
    matches = ZENDESK_OWNER_CHANGED.match(event.get(ZENDESK_CHANGE_DESCRIPTION, ""))
    if matches:
        old_owner = matches.group("old_owner")
        new_owner = matches.group("new_owner")
    return f"zendesk administrative owner changed from {old_owner} to {new_owner}"

Rule specification

AnalysisType: rule
Filename: zendesk_new_owner.py
RuleID: "Zendesk.AccountOwnerChanged"
DedupPeriodMinutes: 60
DisplayName: "Zendesk Account Owner Changed"
Enabled: true
LogTypes:
  - Zendesk.Audit
Severity: High
Tags:
  - Zendesk
  - Privilege Escalation:Valid Accounts
Reports:
  MITRE ATT&CK:
    - TA0004:T1078
Description: Only one admin user can be the account owner. Ensure the change in ownership is expected.
Reference: https://support.zendesk.com/hc/en-us/articles/4408822084634-Changing-the-account-owner
SummaryAttributes:
  - p_any_ip_addresses

Stages and Predicates

Fires on Zendesk.Audit events when all of the conditions below hold.

Condition

  • action is update
  • source_type is account

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.

Worked example

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

Sample Test Event
{
  "action": "update",
  "action_label": "Updated",
  "actor_id": 123,
  "actor_name": "John Doe",
  "change_description": "Owner changed from Bob Cat to Mountain Lion",
  "created_at": "2021-05-28T18:39:50Z",
  "id": 123456789123,
  "ip_address": "127.0.0.1",
  "p_log_type": "Zendesk.Audit",
  "source_id": 123,
  "source_label": "Account: Account",
  "source_type": "account",
  "url": "https://myzendek.zendesk.com/api/v2/audit_logs/111222333444.json"
}

Zendesk API Token Created

#
Severity
high
Log types
Zendesk.Audit
Tags
Zendesk, Credential Access:Steal Application Access Token
Reference
support.zendesk.com
Source
github.com/panther-labs/panther-analysis

A user created a new API token to be used with Zendesk.

MITRE ATT&CK coverage

TacticTechniques
Credential Access

Detection logic

API_TOKEN_ACTIONS = {
    "create",
    "destroy",
}


def rule(event):
    return event.get("source_type") == "api_token" and event.get("action", "") in API_TOKEN_ACTIONS


def title(event):
    action = event.get("action", "<UNKNOWN_ACTION>")
    return f"[{event.get('p_log_type')}]: User [{event.udm('actor_user')}] {action} an api token"


def severity(event):
    if event.get("action", "") == "destroy":
        return "INFO"
    return "HIGH"

Rule specification

AnalysisType: rule
Filename: zendesk_new_api_token.py
RuleID: "Zendesk.NewAPIToken"
DedupPeriodMinutes: 60
DisplayName: "Zendesk API Token Created"
Enabled: true
LogTypes:
  - Zendesk.Audit
Severity: High
Tags:
  - Zendesk
  - Credential Access:Steal Application Access Token
Reports:
  MITRE ATT&CK:
    - TA0006:T1528
Description: A user created a new API token to be used with Zendesk.
Runbook: Validate the api token was created for valid use case, otherwise delete the token immediately.
Reference: https://support.zendesk.com/hc/en-us/articles/4408889192858-Managing-access-to-the-Zendesk-API#topic_bsw_lfg_mmb:~:text=enable%20token%20access.-,Generating%20API%20tokens,-To%20generate%20an
SummaryAttributes:
  - p_any_ip_addresses

Stages and Predicates

Fires on Zendesk.Audit events when all of the conditions below hold.

Condition

  • source_type is api_token
  • action is one of create, destroy

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.

Field
p_log_type
actor_user
action

Response runbook

Validate the api token was created for valid use case, otherwise delete the token immediately.

Worked example

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

Sample Test Event
{
  "action": "create",
  "action_label": "Created",
  "actor_id": 123,
  "actor_name": "John Doe",
  "change_description": "",
  "created_at": "2021-05-28T18:39:50Z",
  "id": 123456789123,
  "ip_address": "127.0.0.1",
  "p_log_type": "Zendesk.Audit",
  "source_id": 123,
  "source_label": "API token",
  "source_type": "api_token",
  "url": "https://myzendek.zendesk.com/api/v2/audit_logs/111222333444.json"
}

Zendesk Credit Card Redaction Off

#
Severity
high
Log types
Zendesk.Audit
Tags
Zendesk, Collection:Data from Information Repositories
Reference
support.zendesk.com
Source
github.com/panther-labs/panther-analysis

A user updated account setting that disabled credit card redaction.

MITRE ATT&CK coverage

Detection logic

from panther_zendesk_helpers import ZENDESK_CHANGE_DESCRIPTION

REDACTION_ACTIONS = {
    "create",
    "destroy",
}


def rule(event):
    return (
        event.get("source_type") == "account_setting"
        and event.get("action", "") in REDACTION_ACTIONS
        and event.get("source_label", "") == "Credit Card Redaction"
    )


def title(event):
    action = event.get(ZENDESK_CHANGE_DESCRIPTION, "<UNKNOWN_ACTION>")
    return f"User [{event.udm('actor_user')}] {action} credit card redaction"


def severity(event):
    if event.get(ZENDESK_CHANGE_DESCRIPTION, "").lower() != "disabled":
        return "INFO"
    return "HIGH"

Rule specification

AnalysisType: rule
Filename: zendesk_sensitive_data_redaction.py
RuleID: "Zendesk.SensitiveDataRedactionOff"
DedupPeriodMinutes: 60
DisplayName: "Zendesk Credit Card Redaction Off"
Enabled: true
LogTypes:
  - Zendesk.Audit
Tags:
  - Zendesk
  - Collection:Data from Information Repositories
Reports:
  MITRE ATT&CK:
    - TA0009:T1213
Severity: High
Description: A user updated account setting that disabled credit card redaction.
Runbook: Re-enable credit card redaction.
Reference: https://support.zendesk.com/hc/en-us/articles/4408822124314-Automatically-redacting-credit-card-numbers-from-tickets
SummaryAttributes:
  - p_any_ip_addresses

Stages and Predicates

Fires on Zendesk.Audit events when all of the conditions below hold.

Condition

  • source_type is account_setting
  • action is one of create, destroy
  • source_label is Credit Card Redaction

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.

Field
actor_user

Response runbook

Re-enable credit card redaction.

Worked example

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

Sample Test Event
{
  "action": "create",
  "action_label": "Updated",
  "actor_id": 123,
  "actor_name": "John Doe",
  "change_description": "Disabled",
  "created_at": "2021-05-28T18:39:50Z",
  "id": 123456789123,
  "ip_address": "127.0.0.1",
  "p_log_type": "Zendesk.Audit",
  "source_id": 123,
  "source_label": "Credit Card Redaction",
  "source_type": "account_setting",
  "url": "https://myzendek.zendesk.com/api/v2/audit_logs/111222333444.json"
}

Zendesk Mobile App Access Modified

#
Severity
medium
Log types
Zendesk.Audit
Tags
Zendesk, Persistence:Valid Accounts
Reference
support.zendesk.com
Source
github.com/panther-labs/panther-analysis

A user updated account setting that enabled or disabled mobile app access.

MITRE ATT&CK coverage

TacticTechniques
Persistence

Detection logic

from panther_zendesk_helpers import ZENDESK_CHANGE_DESCRIPTION

MOBILE_APP_ACTIONS = {"create", "update"}


def rule(event):
    return (
        event.get("source_type") == "account_setting"
        and event.get("action", "") in MOBILE_APP_ACTIONS
        and event.get("source_label", "") == "Zendesk Support Mobile App Access"
    )


def title(event):
    action = event.get(ZENDESK_CHANGE_DESCRIPTION, "<UNKNOWN_ACTION>")
    return f"User [{event.udm('actor_user')}] {action} mobile app access"


def severity(event):
    if event.get(ZENDESK_CHANGE_DESCRIPTION, "").lower() == "disabled":
        return "INFO"
    return "MEDIUM"

Rule specification

AnalysisType: rule
Filename: zendesk_mobile_app_access.py
RuleID: "Zendesk.MobileAppAccessUpdated"
DedupPeriodMinutes: 60
DisplayName: "Zendesk Mobile App Access Modified"
Enabled: true
LogTypes:
  - Zendesk.Audit
Tags:
  - Zendesk
  - Persistence:Valid Accounts
Reports:
  MITRE ATT&CK:
    - TA0003:T1078
Severity: Medium
Description: A user updated account setting that enabled or disabled mobile app access.
Reference: https://support.zendesk.com/hc/en-us/articles/4408846407066-About-the-Zendesk-Support-mobile-app#:~:text=More%20settings.-,Configuring%20the%20mobile%20app,-Activate%20the%20new
SummaryAttributes:
  - p_any_ip_addresses

Stages and Predicates

Fires on Zendesk.Audit events when all of the conditions below hold.

Condition

  • source_type is account_setting
  • action is one of create, update
  • source_label is Zendesk Support Mobile App Access

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.

Field
actor_user

Worked example

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

Sample Test Event
{
  "action": "create",
  "action_label": "Updated",
  "actor_id": 123,
  "actor_name": "John Doe",
  "change_description": "Disabled",
  "created_at": "2021-05-28T18:39:50Z",
  "id": 123456789123,
  "ip_address": "127.0.0.1",
  "p_log_type": "Zendesk.Audit",
  "source_id": 123,
  "source_label": "Zendesk Support Mobile App Access",
  "source_type": "account_setting",
  "url": "https://myzendek.zendesk.com/api/v2/audit_logs/111222333444.json"
}

Zendesk User Role Changed

#
Severity
informational
Log types
Zendesk.Audit
Reference
support.zendesk.com
Source
github.com/panther-labs/panther-analysis

A user's Zendesk role was changed

Detection logic

import panther_event_type_helpers as event_type
from panther_zendesk_helpers import zendesk_get_roles


def rule(event):
    if event.get("source_type") == "user" and event.get("action") == "update":
        # admin roles have their own handling
        if (
            event.udm("event_type") != event_type.ADMIN_ROLE_ASSIGNED
            and "role changed" in event.get("change_description", "").lower()
        ):
            _, new_role = zendesk_get_roles(event)
            return bool(new_role)
    return False


def title(event):
    old_role, new_role = zendesk_get_roles(event)
    return (
        f"Actor user [{event.udm('actor_user')}] changed [{event.udm('user')}] role from "
        f"{old_role} to {new_role}"
    )

Rule specification

AnalysisType: rule
Filename: zendesk_user_role.py
RuleID: "Zendesk.UserRoleChanged"
DedupPeriodMinutes: 60
DisplayName: "Zendesk User Role Changed"
Enabled: true
LogTypes:
  - Zendesk.Audit
Severity: Info
Description: A user's Zendesk role was changed
Reference: https://support.zendesk.com/hc/en-us/articles/4408824375450-Setting-roles-and-access-in-Zendesk-Admin-Center
SummaryAttributes:
  - p_any_ip_addresses

Stages and Predicates

Fires on Zendesk.Audit events when all of the conditions below hold.

Condition

  • source_type is user
  • action is update
  • event_type is not admin_role_assigned
  • change_description contains role changed (case-insensitive)

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.

Output fields

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

Field
actor_user
user

Worked example

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

Sample Test Event
{
  "action": "update",
  "action_label": "Updated",
  "actor_id": 123,
  "actor_name": "John Doe",
  "change_description": "Role changed from Administrator to End User",
  "created_at": "2021-05-28T18:39:50Z",
  "id": 123456789123,
  "ip_address": "127.0.0.1",
  "p_log_type": "Zendesk.Audit",
  "source_id": 123,
  "source_label": "Bob Cat",
  "source_type": "user",
  "url": "https://myzendek.zendesk.com/api/v2/audit_logs/111222333444.json"
}

Zendesk User Suspension Status Changed

#
Severity
high
Log types
Zendesk.Audit
Tags
Zendesk, Impact:Account Access Removal
Reference
support.zendesk.com
Source
github.com/panther-labs/panther-analysis

A user's Zendesk suspension status was changed.

MITRE ATT&CK coverage

TacticTechniques
Impact

Detection logic

from panther_zendesk_helpers import ZENDESK_CHANGE_DESCRIPTION

USER_SUSPENSION_ACTIONS = {
    "create",
    "update",
}


def rule(event):
    return (
        event.get("source_type") == "user_setting"
        and event.get("action", "") in USER_SUSPENSION_ACTIONS
        and "suspended" in event.get(ZENDESK_CHANGE_DESCRIPTION, "").lower()
    )


def title(event):
    suspension_status = event.get(ZENDESK_CHANGE_DESCRIPTION, "").lower()
    user = event.get("source_label", "<UNKNOWN_USER>").split(":")
    if len(user) > 1:
        user = user[1].strip()
    return f"Actor user [{event.udm('actor_user')}] {suspension_status} user [{user}]"


def severity(event):
    if event.get(ZENDESK_CHANGE_DESCRIPTION, "").lower() == "suspended":
        return "INFO"
    return "DEFAULT"

Rule specification

AnalysisType: rule
Filename: zendesk_user_suspension.py
RuleID: "Zendesk.UserSuspension"
DedupPeriodMinutes: 60
DisplayName: "Zendesk User Suspension Status Changed"
Enabled: true
LogTypes:
  - Zendesk.Audit
Tags:
  - Zendesk
  - Impact:Account Access Removal
Reports:
  MITRE ATT&CK:
    - TA0040:T1531
Severity: High
Description: A user's Zendesk suspension status was changed.
Runbook: Ensure the user's suspension status is appropriate.
Reference: https://support.zendesk.com/hc/en-us/articles/4408889293978-Suspending-a-user#:~:text=select%20Unsuspend%20access.-,Identifying%20suspended%20users,name%20on%20the%20Customers%20page
SummaryAttributes:
  - p_any_ip_addresses

Stages and Predicates

Fires on Zendesk.Audit events when all of the conditions below hold.

Condition

  • source_type is user_setting
  • action is one of create, 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.

Output fields

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

Field
actor_user

Response runbook

Ensure the user's suspension status is appropriate.

Worked example

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

Sample Test Event
{
  "action": "create",
  "action_label": "Updated",
  "actor_id": 123,
  "actor_name": "John Doe",
  "change_description": "Suspended",
  "created_at": "2021-05-28T18:39:50Z",
  "id": 123456789123,
  "ip_address": "127.0.0.1",
  "p_log_type": "Zendesk.Audit",
  "source_id": 123,
  "source_label": "Suspension state: Bob Cat",
  "source_type": "user_setting",
  "url": "https://myzendek.zendesk.com/api/v2/audit_logs/111222333444.json"
}