Detection rules › Panther

Panther rules: google

Google Workspace Admin Custom Role

#
Severity
medium
Log types
GSuite.ActivityEvent
Tags
admin, administrator, google workspace, role
Reference
support.google.com
Source
github.com/panther-labs/panther-analysis

A Google Workspace administrator created a new custom administrator role.

Telemetry coverage

PlatformRecord / event type
Google WorkspaceCREATE_ROLE: Create Role

Detection logic

def rule(event):
    # Return True to match the log event and trigger an alert.
    # Create Alert if there is a custom role created under delegated admin settings
    return (
        event.get("type", "") == "DELEGATED_ADMIN_SETTINGS"
        and event.get("name", "") == "CREATE_ROLE"
    )


def title(event):
    # (Optional) Return a string which will be shown as the alert title.
    # If no 'dedup' function is defined, the return value of this method
    # will act as deduplication string.
    return (
        f"Google Workspace Administrator "
        f"[{event.get('actor',{}).get('email','NO_EMAIL_FOUND')}] "
        f"created a new admin role "
        f"[{event.get('parameters',{}).get('ROLE_NAME','NO_ROLE_NAME_FOUND')}]."
    )

Rule specification

AnalysisType: rule
Description: A Google Workspace administrator created a new custom administrator role.
DisplayName: "Google Workspace Admin Custom Role"
Enabled: true
Filename: google_workspace_admin_custom_role.py
Runbook: Please review this activity with the administrator and ensure this behavior was authorized.
Reference: https://support.google.com/a/answer/2406043?hl=en#:~:text=under%20the%20limit.-,Create%20a%20custom%20role,-Before%20you%20begin
Severity: Medium
Tags:
  - admin
  - administrator
  - google workspace
  - role
DedupPeriodMinutes: 60
LogTypes:
  - GSuite.ActivityEvent
RuleID: "Google.Workspace.Admin.Custom.Role"
SummaryAttributes:
  - name
  - type
Threshold: 1

Stages and Predicates

Fires on GSuite.ActivityEvent events when all of the conditions below hold.

Condition

  • type is DELEGATED_ADMIN_SETTINGS
  • name is CREATE_ROLE

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
emailactor.email
ROLE_NAMEparameters.ROLE_NAME

Response runbook

Please review this activity with the administrator and ensure this behavior was authorized.

Worked example

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

Sample Test Event
{
  "actor": {
    "callerType": "USER",
    "email": "example@example.io",
    "profileId": "123456"
  },
  "id": {
    "applicationName": "admin",
    "customerId": "D12345",
    "time": "2022-12-11 02:57:48.693000000",
    "uniqueQualifier": "-12456"
  },
  "ipAddress": "12.12.12.12",
  "kind": "admin#reports#activity",
  "name": "CREATE_ROLE",
  "parameters": {
    "ROLE_ID": "567890",
    "ROLE_NAME": "CustomAdminRoleName"
  },
  "type": "DELEGATED_ADMIN_SETTINGS"
}

Google Workspace Advanced Protection Program

#
Severity
medium
Log types
GSuite.ActivityEvent
Reference
support.google.com
Source
github.com/panther-labs/panther-analysis

Your organization's Google Workspace Advanced Protection Program settings were modified.

Telemetry coverage

Detection logic

def rule(event):
    # Return True to match the log event and trigger an alert.
    setting_name = (
        event.deep_get("parameters", "SETTING_NAME", default="NO_SETTING_NAME")
        .split("-")[0]
        .strip()
    )
    setting_alert_flag = "Advanced Protection Program Settings"
    return event.get("name") == "CREATE_APPLICATION_SETTING" and setting_name == setting_alert_flag


def title(event):
    # If no 'dedup' function is defined, the return value of this
    # method will act as deduplication string.
    setting = event.deep_get("parameters", "SETTING_NAME", default="NO_SETTING_NAME")
    setting_name = setting.split("-")[-1].strip()
    return (
        f"Google Workspace Advanced Protection Program settings have been updated to "
        f"[{setting_name}] by Google Workspace User "
        f"[{event.deep_get('actor', 'email', default='<NO_EMAIL_FOUND>')}]."
    )

Rule specification

AnalysisType: rule
Description: Your organization's Google Workspace Advanced Protection Program settings were modified.
DisplayName: "Google Workspace Advanced Protection Program"
Enabled: true
Filename: google_workspace_advanced_protection_program.py
Runbook: Confirm the changes made were authorized for your organization.
Reference: https://support.google.com/a/answer/9378686?hl=en
Severity: Medium
DedupPeriodMinutes: 60
LogTypes:
  - GSuite.ActivityEvent
RuleID: "Google.Workspace.Advanced.Protection.Program"
Threshold: 1

Stages and Predicates

Fires on GSuite.ActivityEvent events when the condition below holds.

Condition

  • name is CREATE_APPLICATION_SETTING

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
nameeq
  • CREATE_APPLICATION_SETTING
field:"name" kind:eq value:"CREATE_APPLICATION_SETTING"

Output fields

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

FieldSource
emailactor.email

Response runbook

Confirm the changes made were authorized for your organization.

Worked example

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

Sample Test Event
{
  "actor": {
    "callerType": "USER",
    "email": "example@example.io",
    "profileId": "12345"
  },
  "id": {
    "applicationName": "admin",
    "customerId": "D12345",
    "time": "2022-12-11 01:35:29.906000000",
    "uniqueQualifier": "-12345"
  },
  "ipAddress": "12.12.12.12",
  "kind": "admin#reports#activity",
  "name": "CREATE_APPLICATION_SETTING",
  "parameters": {
    "APPLICATION_EDITION": "standard",
    "APPLICATION_NAME": "Security",
    "NEW_VALUE": "ALLOWED_WITH_REMOTE_ACCESS",
    "ORG_UNIT_NAME": "Example IO",
    "SETTING_NAME": "Advanced Protection Program Settings - Allow security codes"
  },
  "type": "APPLICATION_SETTINGS"
}

Google Workspace Apps Marketplace Allowlist

#
Severity
medium
Log types
GSuite.ActivityEvent
Reference
support.google.com
Source
github.com/panther-labs/panther-analysis

Google Workspace Marketplace application allowlist settings were modified.

Detection logic

def rule(event):
    # Return True to match the log event and trigger an alert.
    setting_name = event.deep_get("parameters", "SETTING_NAME", default="<NO_SETTING_NAME>")
    old_val = event.deep_get("parameters", "OLD_VALUE", default="<NO_OLD_VALUE_FOUND>")
    new_val = event.deep_get("parameters", "NEW_VALUE", default="<NO_NEW_VALUE_FOUND>")
    return setting_name == "ENABLE_G_SUITE_MARKETPLACE" and old_val != new_val


def title(event):
    # (Optional) Return a string which will be shown as the alert title.
    # If no 'dedup' function is defined, the return value of this
    # method will act as deduplication string.
    value_dict = {
        "DEFAULT": "DEFAULT",
        "1": "Don't allow users to install and run apps from the Marketplace",
        "2": "Allow users to install and run any app from the Marketplace",
        "3": "Allow users to install and run only selected apps from the Marketplace",
    }
    old_val = event.deep_get("parameters", "OLD_VALUE", default="<NO_OLD_VALUE_FOUND>")
    new_val = event.deep_get("parameters", "NEW_VALUE", default="<NO_NEW_VALUE_FOUND>")
    actor = event.deep_get("actor", "email", default="<NO_EMAIL_FOUND>")
    return (
        f"Google Workspace User [{actor}] "
        f"made an application allowlist setting change from [{value_dict.get(str(old_val))}] "
        f"to [{value_dict.get(str(new_val))}]"
    )

Rule specification

AnalysisType: rule
Description: Google Workspace Marketplace application allowlist settings were modified.
DisplayName: "Google Workspace Apps Marketplace Allowlist"
Enabled: true
Filename: google_workspace_apps_marketplace_allowlist.py
Runbook: Confirm with the acting user that this change was authorized.
Reference: https://support.google.com/a/answer/6089179?hl=en
Severity: Medium
DedupPeriodMinutes: 60
LogTypes:
  - GSuite.ActivityEvent
RuleID: "Google.Workspace.Apps.Marketplace.Allowlist"
Threshold: 1

Stages and Predicates

Fires on GSuite.ActivityEvent events when all of the conditions below hold.

Condition

  • parameters.SETTING_NAME is ENABLE_G_SUITE_MARKETPLACE
  • parameters.OLD_VALUE differs from field parameters.NEW_VALUE

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
emailactor.email
OLD_VALUEparameters.OLD_VALUE
NEW_VALUEparameters.NEW_VALUE

Response runbook

Confirm with the acting user that this change was authorized.

Worked example

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

Sample Test Event
{
  "actor": {
    "callerType": "USER",
    "email": "example@example.io",
    "profileId": "12345"
  },
  "id": {
    "applicationName": "admin",
    "customerId": "D12345",
    "time": "2022-12-10 23:38:45.125000000",
    "uniqueQualifier": "-12345"
  },
  "ipAddress": "12.12.12.12",
  "kind": "admin#reports#activity",
  "name": "CHANGE_EMAIL_SETTING",
  "parameters": {
    "NEW_VALUE": "3",
    "OLD_VALUE": "2",
    "ORG_UNIT_NAME": "EXAMPLE IO",
    "SETTING_NAME": "ENABLE_G_SUITE_MARKETPLACE"
  },
  "type": "EMAIL_SETTINGS"
}

Google Workspace Apps Marketplace New Domain Application

#
Severity
medium
Log types
GSuite.ActivityEvent
Reference
developers.google.com
Source
github.com/panther-labs/panther-analysis

A Google Workspace User configured a new domain application from the Google Workspace Apps Marketplace.

Telemetry coverage

Detection logic

def rule(event):
    # Return True to match the log event and trigger an alert.
    return (
        event.get("name") == "ADD_APPLICATION"
        and event.get("parameters", {}).get("APPLICATION_ENABLED", "<NO_APPLICATION_FOUND>")
        == "true"
    )


def title(event):
    # (Optional) Return a string which will be shown as the alert title.
    # If no 'dedup' function is defined, the return value of this method
    # will act as deduplication string.
    return (
        f"Google Workspace User [{event.get('actor',{}).get('email','<NO_EMAIL_PROVIDED>')}] "
        f"enabled a new Google Workspace Marketplace application "
        f"[{event.get('parameters',{}).get('APPLICATION_NAME','<NO_APPLICATION_NAME_FOUND>')}]"
    )

Rule specification

AnalysisType: rule
Description: A Google Workspace User configured a new domain application from the Google Workspace Apps Marketplace.
DisplayName: "Google Workspace Apps Marketplace New Domain Application"
Enabled: true
Filename: google_workspace_apps_marketplace_new_domain_application.py
Runbook: Confirm this was the intended behavior.
Reference: https://developers.google.com/workspace/marketplace/overview
Severity: Medium
DedupPeriodMinutes: 60
LogTypes:
  - GSuite.ActivityEvent
RuleID: "Google.Workspace.Apps.Marketplace.New.Domain.Application"
Threshold: 1

Stages and Predicates

Fires on GSuite.ActivityEvent events when all of the conditions below hold.

Condition

  • name is ADD_APPLICATION
  • parameters.APPLICATION_ENABLED is true

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
emailactor.email
APPLICATION_NAMEparameters.APPLICATION_NAME

Response runbook

Confirm this was the intended behavior.

Worked example

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

Sample Test Event
{
  "actor": {
    "callerType": "USER",
    "email": "example@example.io",
    "profileId": "12345"
  },
  "id": {
    "applicationName": "admin",
    "customerId": "D12345",
    "time": "2022-12-10 23:05:39.508000000",
    "uniqueQualifier": "-12345"
  },
  "kind": "admin#reports#activity",
  "name": "ADD_APPLICATION",
  "parameters": {
    "APPLICATION_ENABLED": "true",
    "APPLICATION_NAME": "DocuSign eSignature for Google",
    "APP_ID": "469176070494"
  },
  "type": "DOMAIN_SETTINGS"
}

Google Workspace Apps New Mobile App Installed

#
Severity
medium
Log types
GSuite.ActivityEvent
Reference
support.google.com
Source
github.com/panther-labs/panther-analysis

A new mobile application was added to your organization's mobile apps whitelist in Google Workspace Apps.

Telemetry coverage

Detection logic

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


def title(event):
    # If no 'dedup' function is defined, the return value of
    # this method will act as deduplication string.
    mobile_app_pkg_id = event.get("parameters", {}).get(
        "MOBILE_APP_PACKAGE_ID", "<NO_MOBILE_APP_PACKAGE_ID_FOUND>"
    )
    return (
        f"Google Workspace User [{event.get('actor',{}).get('email','<NO_EMAIL_FOUND>')}] "
        f"added application "
        f"[{mobile_app_pkg_id}] "
        f"to your org's mobile application allowlist for "
        f"[{event.get('parameters',{}).get('DEVICE_TYPE','<NO_DEVICE_TYPE_FOUND>')}]."
    )

Rule specification

AnalysisType: rule
Description: A new mobile application was added to your organization's mobile apps whitelist in Google Workspace Apps.
DisplayName: "Google Workspace Apps New Mobile App Installed"
Enabled: true
Filename: google_workspace_apps_new_mobile_app_installed.py
Runbook: https://admin.google.com/ac/apps/unified
Reference: https://support.google.com/a/answer/6089179?hl=en
Severity: Medium

DedupPeriodMinutes: 60
LogTypes:
  - GSuite.ActivityEvent
RuleID: "Google.Workspace.Apps.New.Mobile.App.Installed"
Threshold: 1

Stages and Predicates

Fires on GSuite.ActivityEvent events when the condition below holds.

Condition

  • name is ADD_MOBILE_APPLICATION_TO_WHITELIST

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
nameeq
  • ADD_MOBILE_APPLICATION_TO_WHITELIST
field:"name" kind:eq value:"ADD_MOBILE_APPLICATION_TO_WHITELIST"

Output fields

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

FieldSource
emailactor.email
MOBILE_APP_PACKAGE_IDparameters.MOBILE_APP_PACKAGE_ID
DEVICE_TYPEparameters.DEVICE_TYPE

Response runbook

https://admin.google.com/ac/apps/unified

Worked example

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

Sample Test Event
{
  "actor": {
    "callerType": "USER",
    "email": "example@example.io",
    "profileId": "12345"
  },
  "id": {
    "applicationName": "admin",
    "customerId": "D12345",
    "time": "2022-12-10 22:55:38.478000000",
    "uniqueQualifier": "12345"
  },
  "ipAddress": "12.12.12.12",
  "kind": "admin#reports#activity",
  "name": "ADD_MOBILE_APPLICATION_TO_WHITELIST",
  "parameters": {
    "DEVICE_TYPE": "Android",
    "DISTRIBUTION_ENTITY_NAME": "/",
    "DISTRIBUTION_ENTITY_TYPE": "ORG_UNIT",
    "MOBILE_APP_PACKAGE_ID": "com.google.android.calculator"
  },
  "type": "MOBILE_SETTINGS"
}

Google Workspace Many Docs Downloaded

#
Status
Experimental
Severity
informational
Log types
GSuite.ActivityEvent
Tags
GSuite ActivityEvent
Reference
support.google.com
Source
github.com/panther-labs/panther-analysis

Checks whether a user has downloaded a large number of documents from Google Drive within a 5-minute period.

MITRE ATT&CK coverage

TacticTechniques
Exfiltration

Telemetry coverage

PlatformRecord / event type
Google Workspacedownload: Download

Rules detecting the same action

These rules filter on the same operation.

Detection logic

from panther_core import PantherEvent


def rule(event: PantherEvent) -> bool:
    if event.deep_get("id", "applicationName") != "drive":
        return False
    return event.get("name") == "download"


def alert_context(event: PantherEvent) -> dict:
    return {
        "actor": event.deep_get("actor", "email", default="<UNKNOWN ACTOR>"),
        "document_name": event.deep_get("parameters", "doc_title", default="<UNKNOWN DOCUMENT>"),
    }

Rule specification

AnalysisType: rule
Filename: google_workspace_many_docs_downloaded.py
RuleID: "Google.Workspace.ManyDocsDownloaded"
DisplayName: Google Workspace Many Docs Downloaded
Enabled: true
LogTypes:
  - GSuite.ActivityEvent
Severity: Info
CreateAlert: false
Reports:
  MITRE ATT&CK:
    - TA0010:T1567
Description: >
  Checks whether a user has downloaded a large number of documents from Google Drive
  within a 5-minute period.
DedupPeriodMinutes: 5
Threshold: 20
Reference: >
  https://support.google.com/drive/answer/2423534?hl=en&co=GENIE.Platform%3DDesktop
SummaryAttributes:
  - p_any_usernames
  - parameters:doc_title
Tags:
  - GSuite ActivityEvent
Status: Experimental

Stages and Predicates

Fires on GSuite.ActivityEvent events when all of the conditions below hold.

Condition

  • id.applicationName is drive
  • name is download
Alert cadence
alerts after 20 matches within 5m

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
actoractor.email
document_nameparameters.doc_title

Worked example

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

Sample Test Event
{
  "actor": {
    "email": "john@justice.org",
    "profileId": "112233445566778899001"
  },
  "id": {
    "applicationName": "drive",
    "customerId": "CUSTID",
    "time": "2025-03-21 21:29:49.364000000",
    "uniqueQualifier": "-1234567891234567890"
  },
  "ipAddress": "1.1.1.1",
  "kind": "admin#reports#activity",
  "name": "download",
  "parameters": {
    "billable": true,
    "doc_id": "123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0-a1B2c3D",
    "doc_title": "My Sensitive Document",
    "doc_type": "spreadsheet",
    "owner": "HR",
    "owner_is_shared_drive": true,
    "owner_is_team_drive": true,
    "owner_team_drive_id": "123456789aB_a1B2c3D",
    "primary_event": true,
    "shared_drive_id": "123456789aB_a1B2c3D",
    "team_drive_id": "123456789aB_a1B2c3D",
    "visibility": "shared_internally"
  },
  "type": "access"
}