Detection rules › Panther

Panther rules: dropbox

Dropbox Admin sign-in-as Session

#
Severity
medium
Entities
emails, ip_addresses, usernames
Log types
Dropbox.TeamEvent
Reference
help.dropbox.com
Source
github.com/panther-labs/panther-analysis

Alerts when an admin starts a sign-in-as session.

Detection logic

def rule(event):
    return event.deep_get("event_type", "_tag", default="") == "sign_in_as_session_start"


def title(event):
    actor = event.deep_get("actor", "admin", "email", default="<ACTOR_NOT_FOUND>")
    target = event.deep_get("context", "email", default="<TARGET_NOT_FOUND>")
    return f"Dropbox: Admin [{actor}] started a sign-in-as session as user [{target}]."

Rule specification

AnalysisType: rule
Description: "Alerts when an admin starts a sign-in-as session."
DisplayName: "Dropbox Admin sign-in-as Session"
Enabled: true
Filename: dropbox_admin_sign_in_as_session.py
Reference: https://help.dropbox.com/security/sign-in-as-user
Severity: Medium
DedupPeriodMinutes: 60
LogTypes:
  - Dropbox.TeamEvent
RuleID: "Dropbox.Admin.sign.in.as.Session"
Threshold: 1

Stages and Predicates

Fires on Dropbox.TeamEvent events when the condition below holds.

Condition

  • event_type._tag is sign_in_as_session_start

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.admin.email
emailcontext.email

Worked example

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

Sample Test Event
{
  "actor": {
    "_tag": "admin",
    "admin": {
      "_tag": "team_member",
      "account_id": "dbid:ABCDEFGHIJKLMONOPASQRST",
      "display_name": "senvironment",
      "email": "adminuser@company.com",
      "team_member_id": "dbmid:AAAwgqIsigitfNAUAhsbxzQKtIPBM7uGAgg"
    }
  },
  "context": {
    "_tag": "team_member",
    "account_id": "dbid:AAACjvKy90uezyOiLRadIuCy66dK5d1vGGw",
    "display_name": "Alice Bob",
    "email": "alice.bob@company.com",
    "team_member_id": "dbmid:AADSERs2cAsByYt8yQEDU4_qdNQiSdxgCl8"
  },
  "details": {
    ".tag": "sign_in_as_session_start_details"
  },
  "event_category": {
    "_tag": "logins"
  },
  "event_type": {
    "_tag": "sign_in_as_session_start",
    "description": "Started admin sign-in-as session"
  },
  "involve_non_team_member": false,
  "origin": {
    "access_method": {
      ".tag": "end_user",
      "end_user": {
        ".tag": "web",
        "session_id": "dbwsid:89515573818299775425117508904073133360"
      }
    },
    "geo_location": {
      "city": "Austin",
      "country": "US",
      "ip_address": "1.2.3.4",
      "region": "Texas"
    }
  },
  "p_any_emails": [
    "alice.bob@company.com",
    "adminuser@company.com"
  ],
  "p_any_ip_addresses": [
    "1.2.3.4"
  ],
  "p_any_usernames": [
    "Alice Bob",
    "senvironment"
  ],
  "p_event_time": "2023-04-19 19:20:02",
  "p_log_type": "Dropbox.TeamEvent",
  "p_parse_time": "2023-04-19 19:21:46.802",
  "p_row_id": "ca46ddc6518083b5a4cbbed517dc8f02",
  "p_schema_version": 0,
  "p_source_id": "b09c205e-42af-4933-8b18-b910985eb7fb",
  "p_source_label": "dropbox1",
  "timestamp": "2023-04-19 19:20:02"
}

Dropbox Document/Folder Ownership Transfer

#
Severity
medium
Entities
emails, ip_addresses, usernames
Log types
Dropbox.TeamEvent
Reference
help.dropbox.com
Source
github.com/panther-labs/panther-analysis

Dropbox ownership of a document or folder has been transferred.

Detection logic

def rule(event):
    return "Transferred ownership " in event.deep_get("event_type", "description", default="")


def title(event):
    actor = event.deep_get("actor", "user", "email", default="<EMAIL_NOT_FOUND>")
    previous_owner = event.deep_get(
        "details", "previous_owner_email", default="<PREVIOUS_OWNER_NOT_FOUND>"
    )
    new_owner = event.deep_get("details", "new_owner_email", default="<NEW_OWNER_NOT_FOUND>")
    assets = event.get("assets", [{}])
    asset = [a.get("display_name", "<ASSET_NOT_FOUND>") for a in assets]
    return (
        f"Dropbox: [{actor}] transferred ownership of [{asset}]"
        f"from [{previous_owner}] to [{new_owner}]."
    )


def severity(event):
    new_owner_domain = event.deep_get("details", "new_owner_email", default="@").split("@")[-1]
    previous_owner_domain = event.deep_get("details", "previous_owner_email", default="@").split(
        "@"
    )[-1]

    if new_owner_domain != previous_owner_domain:
        return "DEFAULT"
    return "LOW"

Rule specification

AnalysisType: rule
Description: "Dropbox ownership of a document or folder has been transferred."
DisplayName: "Dropbox Document/Folder Ownership Transfer"
Enabled: true
Filename: dropbox_ownership_transfer.py
Reference: https://help.dropbox.com/share/owner
Severity: Medium
DedupPeriodMinutes: 60
LogTypes:
  - Dropbox.TeamEvent
RuleID: "Dropbox.Ownership.Transfer"
Threshold: 1

Stages and Predicates

Fires on Dropbox.TeamEvent events when the condition below holds.

Condition

  • event_type.description contains "Transferred ownership "

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.user.email
previous_owner_emaildetails.previous_owner_email
new_owner_emaildetails.new_owner_email

Worked example

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

Sample Test Event
{
  "actor": {
    "_tag": "user",
    "user": {
      "_tag": "team_member",
      "account_id": "dbid:AAAAAAAAAAAAAAAAAAAAA",
      "display_name": "Alice Bob",
      "email": "alice.bob@company.io",
      "team_member_id": "dbmid:BBBBBBBBBBBBBBBBBBBBBB"
    }
  },
  "assets": [
    {
      ".tag": "folder",
      "display_name": "test1",
      "path": {
        "contextual": "/Alice Bob/test1",
        "namespace_relative": {
          "is_shared_namespace": true,
          "ns_id": "12345"
        }
      }
    }
  ],
  "context": {
    "_tag": "team_member",
    "account_id": "dbid:AAAAAAAAAAAAAAAAAAAAA",
    "display_name": "Alice Bob",
    "email": "alice.bob@company.io",
    "team_member_id": "dbmid:BBBBBBBBBBBBBBBBBBBBBB"
  },
  "details": {
    ".tag": "shared_folder_transfer_ownership_details",
    "new_owner_email": "david.davidson@company.io",
    "previous_owner_email": "alice.bob@company.io"
  },
  "event_category": {
    "_tag": "sharing"
  },
  "event_type": {
    "_tag": "shared_folder_transfer_ownership",
    "description": "Transferred ownership of shared folder to another member"
  },
  "involve_non_team_member": false,
  "origin": {
    "access_method": {
      ".tag": "end_user",
      "end_user": {
        ".tag": "web",
        "session_id": "dbwsid:237034608707419186011941491025532848312"
      }
    },
    "geo_location": {
      "city": "Austin",
      "country": "US",
      "ip_address": "1.2.3.4",
      "region": "Texas"
    }
  },
  "p_any_emails": [
    "alice.bob@company.io",
    "david.davidson@company.io"
  ],
  "p_any_ip_addresses": [
    "1.2.3.4"
  ],
  "p_any_usernames": [
    "Alice Bob",
    "company"
  ],
  "p_event_time": "2023-04-18 18:54:15",
  "p_log_type": "Dropbox.TeamEvent",
  "p_parse_time": "2023-04-18 18:56:47.418",
  "p_row_id": "0eb86fcfca9bb1cdce9defd217e1cd04",
  "p_schema_version": 0,
  "p_source_id": "b09c205e-42af-4933-8b18-b910985eb7fb",
  "p_source_label": "dropbox1",
  "participants": [
    {
      "user": {
        "_tag": "team_member",
        "account_id": "dbid:ABCD",
        "display_name": "company",
        "email": "david.davidson@company.io",
        "team_member_id": "dbmid:DEFG"
      }
    }
  ],
  "timestamp": "2023-04-18 18:54:15"
}

Dropbox External Share

#
Severity
low
Entities
emails, ip_addresses, usernames
Log types
Dropbox.TeamEvent
Reference
help.dropbox.com
Source
github.com/panther-labs/panther-analysis

Dropbox item shared externally

Detection logic

def rule(event):
    actor_domain = event.deep_get("actor", "user", "email", default="@").split("@")[-1]

    if event.deep_get("event_type", "_tag", default="") == "shared_content_add_member":
        participants = event.get("participants", [{}])
        for participant in participants:
            email = participant.get("user", {}).get("email", "@")
            if email.split("@")[-1] != actor_domain:
                return True
    return False


def title(event):
    actor = event.deep_get("actor", "user", "email", default="<ACTOR_NOT_FOUND>")
    actor_domain = event.deep_get("actor", "user", "email", default="@").split("@")[-1]
    assets = [e.get("display_name", "") for e in event.get("assets", [{}])]
    participants = event.get("participants", [{}])
    external_participants = []
    for participant in participants:
        email = participant.get("user", {}).get("email", "")
        if email.split("@")[-1] != actor_domain:
            external_participants.append(email)
    return f"Dropbox: [{actor}] shared [{assets}] with external user [{external_participants}]."


def alert_context(event):
    actor_domain = event.deep_get("actor", "user", "email", default="@").split("@")[-1]
    external_participants = []
    participants = event.get("participants", [{}])
    for participant in participants:
        email = participant.get("user", {}).get("email", "")
        if email.split("@")[-1] != actor_domain:
            external_participants.append(email)
    return {"external_participants": external_participants}

Rule specification

AnalysisType: rule
Description: Dropbox item shared externally
DisplayName: "Dropbox External Share"
Enabled: true
Filename: dropbox_external_share.py
Reference: https://help.dropbox.com/share/share-outside-dropbox
Severity: Low
DedupPeriodMinutes: 60
LogTypes:
  - Dropbox.TeamEvent
RuleID: "Dropbox.External.Share"
Threshold: 1

Stages and Predicates

Fires on Dropbox.TeamEvent events when the condition below holds.

Condition

  • event_type._tag is shared_content_add_member

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.

FieldSource
emailactor.user.email

Worked example

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

Sample Test Event
{
  "actor": {
    "_tag": "user",
    "user": {
      "_tag": "team_member",
      "account_id": "dbid:AAACjvKy90uezyOiLRadIuCy66dK5d1vGGw",
      "display_name": "Alice Bob",
      "email": "alice.bob@company.com",
      "team_member_id": "dbmid:AADSERs2cAsByYt8yQEDU4_qdNQiSdxgCl8"
    }
  },
  "assets": [
    {
      ".tag": "file",
      "display_name": "paper1.paper",
      "file_id": "id:lUP4ZxMYmc4AAAAAAAAAaA",
      "path": {
        "contextual": "/pathtest/paper1.paper",
        "namespace_relative": {
          "is_shared_namespace": true,
          "ns_id": "3590048721",
          "relative_path": "/paper1.paper"
        }
      }
    }
  ],
  "context": {
    "_tag": "team_member",
    "account_id": "dbid:AAACjvKy90uezyOiLRadIuCy66dK5d1vGGw",
    "display_name": "Alice Bob",
    "email": "alice.bob@company.com",
    "team_member_id": "dbmid:AADSERs2cAsByYt8yQEDU4_qdNQiSdxgCl8"
  },
  "details": {
    ".tag": "shared_content_add_member_details",
    "shared_content_access_level": {
      ".tag": "viewer"
    }
  },
  "event_category": {
    "_tag": "sharing"
  },
  "event_type": {
    "_tag": "shared_content_add_member",
    "description": "Added users and/or groups to shared file/folder"
  },
  "involve_non_team_member": true,
  "origin": {
    "access_method": {
      ".tag": "end_user",
      "end_user": {
        ".tag": "web",
        "session_id": "dbwsid:237034608707419186011941491025532848312"
      }
    },
    "geo_location": {
      "city": "Austin",
      "country": "US",
      "ip_address": "1.2.3.4",
      "region": "Texas"
    }
  },
  "p_any_emails": [
    "david.davidson@david.co",
    "alice.bob@company.com"
  ],
  "p_any_ip_addresses": [
    "1.2.3.4"
  ],
  "p_any_usernames": [
    "Alice Bob",
    "david davidson"
  ],
  "p_event_time": "2023-04-18 22:31:03",
  "p_log_type": "Dropbox.TeamEvent",
  "p_parse_time": "2023-04-18 22:32:46.967",
  "p_row_id": "fe2163f14b45f3c1b9a49fd31799a504",
  "p_schema_version": 0,
  "p_source_id": "b09c205e-42af-4933-8b18-b910985eb7fb",
  "p_source_label": "dropbox1",
  "participants": [
    {
      "user": {
        "_tag": "non_team_member",
        "account_id": "dbid:AABbWylBrTJ3Je-M37jeWShWuMAFHchEsKM",
        "display_name": "david davidson",
        "email": "david.davidson@david.co"
      }
    }
  ],
  "timestamp": "2023-04-18 22:31:03"
}

Dropbox Linked Team Application Added

#
Severity
low
Log types
Dropbox.TeamEvent
Tags
dropbox
Reference
help.dropbox.com
Source
github.com/panther-labs/panther-analysis

An application was linked to your Dropbox Account

Detection logic

def rule(event):
    return all(
        [
            event.deep_get("event_type", "_tag", default="") == "app_link_team",
            event.deep_get("event_type", "description", default="") == "Linked app for team",
        ]
    )


def severity(event):
    # Anything involving non-team members should be High
    if event.get("involve_non_team_member", False):
        return "High"
    return "Low"


def get_actor_type():
    return (
        # Admin who performed the action
        "admin",
        # Anonymous actor
        "anonymous",
        # Application that performed the action
        "app",
        # Action performed by Dropbox
        "dropbox",
        # Action performed by reseller
        "reseller",
        # User who performed the action
        "user",
    )


def title(event):
    # This will be one of the types returned by get_actor_type;
    # find the intersection and use that for the key
    actor_key = set(tuple(event.get("actor", {}).keys())).intersection(get_actor_type())
    if len(actor_key) == 1:
        display_name = event.deep_get(
            "actor", tuple(actor_key)[0], "display_name", default="<Unknown>"
        )
    # Explicitly use "<Unknown>" if we find any length of keys != 1
    else:
        display_name = "<Unknown>"
    return f"Dropbox Team Member Linked App by [{display_name}]"


def user_details(event):
    details = {}
    for actor_key, actor_value in event.get("actor", {}).items():
        if actor_key == "_tag":
            continue
        for user_key, user_info in actor_value.items():
            if user_key in ("_tag", "display_name"):
                continue
            details[user_key] = user_info
    return details


def alert_context(event):
    additional_user_details = user_details(event)
    return {
        "additional_user_details": additional_user_details,
        "app_display_name": event.deep_get(
            "details", "app_info", "display_name", default="<Unknown app display name>"
        ),
        "ip_address": event.deep_get(
            "origin", "geo_location", "ip_address", default="<Unknown IP address>"
        ),
        "request_id": event.deep_get(
            "origin", "access_method", "request_id", default="<Unknown request ID>"
        ),
    }

Rule specification

AnalysisType: rule
Description: An application was linked to your Dropbox Account
DisplayName: "Dropbox Linked Team Application Added"
Enabled: true
Filename: dropbox_linked_team_application_added.py
Reference: https://help.dropbox.com/integrations/app-integrations
Runbook: >
  Ensure that the application is valid and not malicious. Verify that this is expected. If not, determine other actions taken by this user recently and reach out to the user.
  If the event involved a non-team member, consider disabling the user's access while investigating.
Severity: Low
Tags:
  - dropbox
DedupPeriodMinutes: 60
LogTypes:
  - Dropbox.TeamEvent
RuleID: "Dropbox.Linked.Team.Application.Added"
Threshold: 1

Stages and Predicates

Fires on Dropbox.TeamEvent events when all of the conditions below hold.

Condition

  • event_type._tag is app_link_team
  • event_type.description is Linked app for team

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
app_display_namedetails.app_info.display_name
ip_addressorigin.geo_location.ip_address
request_idorigin.access_method.request_id

Response runbook

Ensure that the application is valid and not malicious. Verify that this is expected. If not, determine other actions taken by this user recently and reach out to the user. If the event involved a non-team member, consider disabling the user's access while investigating.

Worked example

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

Sample Test Event
{
  "actor": {
    "_tag": "user",
    "user": {
      "_tag": "team_member",
      "account_id": "dbid:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "display_name": "user_name",
      "email": "user@domain.com",
      "team_member_id": "dbmid:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    }
  },
  "context": {
    "_tag": "team"
  },
  "details": {
    ".tag": "app_link_team_details",
    "app_info": {
      ".tag": "team_linked_app",
      "app_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "display_name": "dropbox-app-name"
    }
  },
  "event_category": {
    "_tag": "apps"
  },
  "event_type": {
    "_tag": "app_link_team",
    "description": "Linked app for team"
  },
  "involve_non_team_member": false,
  "origin": {
    "access_method": {
      ".tag": "api",
      "request_id": "dbarod:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    },
    "geo_location": {
      "city": "Los Angeles",
      "country": "US",
      "ip_address": "1.2.3.4",
      "region": "California"
    }
  },
  "timestamp": "2023-02-16 20:39:34"
}

Dropbox Many Deletes

#
Status
Deprecated
Severity
medium
Tags
Configuration Required
Reference
help.dropbox.com
Source
github.com/panther-labs/panther-analysis

Detects when a dropbox user deletes many documents.

Detection logic

def rule(_):
    return True


def title(event):
    return (
        f"Dropbox: [{event.get('user', '<user_not_found>')}] "
        f"has deleted [{event.get('delete_count', '<count_not_found>')}] "
        "documents from Dropbox."
    )


def alert_context(event):
    return event.to_dict()

Rule specification

AnalysisType: scheduled_rule
Description: Detects when a dropbox user deletes many documents.
DisplayName: "Dropbox Many Deletes"
Enabled: false
Status: Deprecated
Filename: dropbox_many_deletes.py
Reference: https://help.dropbox.com/delete-restore/delete-files
Severity: Medium
DedupPeriodMinutes: 60
RuleID: "Dropbox.Many.Deletes"
Threshold: 1
ScheduledQueries:
  - Dropbox Many Deletes
Tags:
  - Configuration Required

Stages and Predicates

Rule logic

This rule alerts on rows returned by its scheduled query Dropbox Many Deletes; its Python module (Detection logic above) shapes the alert rather than filtering.

Output fields

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

Field
user
delete_count

Worked example

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

Sample Test Event
{
  "delete_count": 4,
  "deleted_files": [
    "/test/test2.txt",
    "/test/test3.txt",
    "/test/test5.txt",
    "/test/test4.txt"
  ],
  "t_s": "2023-04-19 19:00:00",
  "user": "alice.bob@company.com"
}

Dropbox Many Deletes

#

Rule specification

AnalysisType: scheduled_query
Description: Dropbox Many Deletes
Enabled: false
SnowflakeQuery: |-
  SELECT
    actor:user:email AS user,
    ARRAY_AGG( DISTINCT assets[0]:path:contextual) AS deleted_files,
    ARRAY_SIZE(deleted_files) as delete_count,
    TIME_SLICE(p_event_time, 60, 'minute') as t_s
  FROM panther_logs.public.dropbox_teamevent
  WHERE p_occurs_since('1 day')
    AND event_type:_tag = 'file_delete'
  GROUP BY actor:user:email, t_s
  HAVING delete_count > 2
  ORDER BY delete_count DESC

DatabricksQuery: |-
  SELECT
    actor:user:email AS user,
    COLLECT_SET(assets[0]:path:contextual) AS deleted_files,
    SIZE(COLLECT_SET(assets[0]:path:contextual)) AS delete_count,
    DATE_TRUNC('hour', p_event_time) AS t_s
  FROM panther_logs.dropbox_teamevent
  WHERE p_occurs_since('1 day')
    AND event_type:_tag = 'file_delete'
  GROUP BY actor:user:email, DATE_TRUNC('hour', p_event_time)
  HAVING delete_count > 2
  ORDER BY delete_count DESC
QueryName: "Dropbox Many Deletes"
Schedule:
  RateMinutes: 1440
  TimeoutMinutes: 3

Stages and Predicates

Stage 1: source

Table
panther_logs.public.dropbox_teamevent

Stage 2: filter

  • event_type:_tag is file_delete
Grouped by
actor:user:email, t_s
Window
1d

Stage 3: having

  • delete_count is greater than 2

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
useractor:user:email
deleted_filesARRAY_AGG ( DISTINCT assets [ 0 ] : path:contextual )
delete_countARRAY_SIZE ( deleted_files )
t_sTIME_SLICE ( p_event_time , 60 , 'minute' )

Dropbox Many Deletes

#
Status
Experimental
Severity
medium
Group by
actor.user.email
Log types
Dropbox.TeamEvent
Tags
Dropbox, Impact, Data Destruction
Reference
help.dropbox.com
Source
github.com/panther-labs/panther-analysis

Detects when a Dropbox user deletes more than 10 distinct files within 60 minutes. This may indicate accidental or malicious bulk deletion of team files. The threshold should be tuned to your environment.

MITRE ATT&CK coverage

TacticTechniques
Impact

Detection logic

def rule(event):
    return event.deep_get("event_type", "_tag", default="") == "file_delete"


def title(event):
    user = event.deep_get("actor", "user", "email", default="<UNKNOWN_USER>")
    return f"Dropbox: User [{user}] deleted many files"


def dedup(event):
    return event.deep_get("actor", "user", "email", default="")


def unique(event):
    assets = event.get("assets", [])
    if assets:
        return assets[0].get("path", {}).get("contextual") or None
    return None


def severity(event):
    if event.get("involve_non_team_member", False):
        return "HIGH"
    return "DEFAULT"


def alert_context(event):
    assets = event.get("assets", [])
    file_path = assets[0].get("path", {}).get("contextual", "") if assets else ""
    return {
        "user": event.deep_get("actor", "user", "email"),
        "file_path": file_path,
        "involve_non_team_member": event.get("involve_non_team_member"),
        "ip_address": event.deep_get("origin", "geo_location", "ip_address"),
    }

Rule specification

AnalysisType: rule
Filename: dropbox_many_deletes.py
RuleID: "Dropbox.BulkDeletes"
DisplayName: "Dropbox Many Deletes"
Status: Experimental
Enabled: false
Severity: Medium
DedupPeriodMinutes: 60
Threshold: 11
LogTypes:
  - Dropbox.TeamEvent
Description: >
  Detects when a Dropbox user deletes more than 10 distinct files within 60 minutes.
  This may indicate accidental or malicious bulk deletion of team files.
  The threshold should be tuned to your environment.
Reference: https://help.dropbox.com/delete-restore/delete-files
Reports:
  MITRE ATT&CK:
    - TA0040:T1485
Tags:
  - Dropbox
  - Impact
  - Data Destruction
Runbook: |
  1. Query Dropbox.TeamEvent for all file_delete events by actor:user:email in the 2 hours around this alert to identify the full list of assets[0]:path:contextual values deleted
  2. Check involve_non_team_member and origin:geo_location:ip_address to determine if the deletions originated from an unexpected user or location, and assess whether the deleted files were shared with external parties
  3. Search for other suspicious activity by this user in the past 24 hours, including bulk downloads prior to deletion, external sharing events, or login anomalies

Stages and Predicates

Fires on Dropbox.TeamEvent events when the condition below holds.

Condition

  • event_type._tag is file_delete
Alert cadence
alerts after 11 matches within 1h

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
useractor.user.email
involve_non_team_member
ip_addressorigin.geo_location.ip_address

Response runbook

1. Query Dropbox.TeamEvent for all file_delete events by actor:user:email in the 2 hours around this alert to identify the full list of assets[0]:path:contextual values deleted

2. Check involve_non_team_member and origin:geo_location:ip_address to determine if the deletions originated from an unexpected user or location, and assess whether the deleted files were shared with external parties

3. Search for other suspicious activity by this user in the past 24 hours, including bulk downloads prior to deletion, external sharing events, or login anomalies

Worked example

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

Sample Test Event
{
  "actor": {
    "user": {
      "_tag": "team_member",
      "email": "alice@company.com"
    }
  },
  "assets": [
    {
      "path": {
        "contextual": "/alice/report.pdf"
      }
    }
  ],
  "event_type": {
    "_tag": "file_delete"
  },
  "involve_non_team_member": false,
  "origin": {
    "geo_location": {
      "ip_address": "1.2.3.4"
    }
  }
}

Dropbox Many Downloads

#
Status
Deprecated
Severity
medium
Tags
Configuration Required
Reference
learn.dropbox.com
Source
github.com/panther-labs/panther-analysis

Detects when a dropbox user downloads many documents.

Detection logic

def rule(_):
    return True


def title(event):
    return (
        f"Dropbox: [{event.get('user', '<user_not_found>')}] "
        f"has downloaded [{event.get('download_count', '<count_not_found>')}] "
        "documents from Dropbox."
    )


def alert_context(event):
    return event.to_dict()

Rule specification

AnalysisType: scheduled_rule
Description: Detects when a dropbox user downloads many documents.
DisplayName: "Dropbox Many Downloads"
Enabled: false
Status: Deprecated
Filename: dropbox_many_downloads.py
Reference: https://learn.dropbox.com/video-library/upload-and-download-files
Severity: Medium
DedupPeriodMinutes: 60
RuleID: "Dropbox.Many.Downloads"
Threshold: 1
ScheduledQueries:
  - Dropbox Many Downloads
Tags:
  - Configuration Required

Stages and Predicates

Rule logic

This rule alerts on rows returned by its scheduled query Dropbox Many Downloads; its Python module (Detection logic above) shapes the alert rather than filtering.

Output fields

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

Field
user
download_count

Worked example

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

Sample Test Event
{
  "download_count": 4,
  "downloaded_files": [
    "/test/test2.txt",
    "/test/test3.txt",
    "/test/test5.txt",
    "/test/test4.txt"
  ],
  "t_s": "2023-04-19 19:00:00",
  "user": "alice.bob@company.com"
}

Dropbox Many Downloads

#
Source
github.com/panther-labs/panther-analysis

Dropbox Many Downloads

Rule specification

AnalysisType: scheduled_query
Description: Dropbox Many Downloads
Enabled: false
SnowflakeQuery: |-
  SELECT
    actor:user:email AS user,
    ARRAY_AGG( DISTINCT assets[0]:path:contextual) AS downloaded_files,
    ARRAY_SIZE(downloaded_files) as download_count,
    TIME_SLICE(p_event_time, 60, 'minute') as t_s
  FROM panther_logs.public.dropbox_teamevent
  WHERE p_occurs_since('1 day')
    AND event_type:_tag = 'file_download'
  GROUP BY actor:user:email, t_s
  HAVING download_count > 10
  ORDER BY download_count DESC

DatabricksQuery: |-
  SELECT
    actor:user:email AS user,
    COLLECT_SET(assets[0]:path:contextual) AS downloaded_files,
    SIZE(COLLECT_SET(assets[0]:path:contextual)) AS download_count,
    DATE_TRUNC('hour', p_event_time) AS t_s
  FROM panther_logs.dropbox_teamevent
  WHERE p_occurs_since('1 day')
    AND event_type:_tag = 'file_download'
  GROUP BY actor:user:email, DATE_TRUNC('hour', p_event_time)
  HAVING download_count > 10
  ORDER BY download_count DESC
QueryName: "Dropbox Many Downloads"
Schedule:
  RateMinutes: 1440
  TimeoutMinutes: 3

Stages and Predicates

Stage 1: source

Table
panther_logs.public.dropbox_teamevent

Stage 2: filter

  • event_type:_tag is file_download
Grouped by
actor:user:email, t_s
Window
1d

Stage 3: having

  • download_count is greater than 10

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
useractor:user:email
downloaded_filesARRAY_AGG ( DISTINCT assets [ 0 ] : path:contextual )
download_countARRAY_SIZE ( downloaded_files )
t_sTIME_SLICE ( p_event_time , 60 , 'minute' )

Dropbox Many Downloads

#
Status
Experimental
Severity
medium
Group by
actor.user.email
Log types
Dropbox.TeamEvent
Tags
Dropbox, Exfiltration
Reference
learn.dropbox.com
Source
github.com/panther-labs/panther-analysis

Detects when a Dropbox user downloads more than 10 distinct files within 60 minutes. This may indicate data exfiltration or unauthorized bulk access to team files. The threshold should be tuned to your environment.

MITRE ATT&CK coverage

TacticTechniques
Exfiltration

Detection logic

def rule(event):
    return event.deep_get("event_type", "_tag", default="") == "file_download"


def title(event):
    user = event.deep_get("actor", "user", "email", default="<UNKNOWN_USER>")
    return f"Dropbox: User [{user}] downloaded many files"


def dedup(event):
    return event.deep_get("actor", "user", "email", default="")


def unique(event):
    assets = event.get("assets", [])
    if assets:
        return assets[0].get("path", {}).get("contextual") or None
    return None


def severity(event):
    if event.get("involve_non_team_member", False):
        return "HIGH"
    return "DEFAULT"


def alert_context(event):
    assets = event.get("assets", [])
    file_path = assets[0].get("path", {}).get("contextual", "") if assets else ""
    return {
        "user": event.deep_get("actor", "user", "email"),
        "file_path": file_path,
        "involve_non_team_member": event.get("involve_non_team_member"),
        "ip_address": event.deep_get("origin", "geo_location", "ip_address"),
    }

Rule specification

AnalysisType: rule
Filename: dropbox_many_downloads.py
RuleID: "Dropbox.BulkDownloads"
DisplayName: "Dropbox Many Downloads"
Status: Experimental
Enabled: false
Severity: Medium
DedupPeriodMinutes: 60
Threshold: 11
LogTypes:
  - Dropbox.TeamEvent
Description: >
  Detects when a Dropbox user downloads more than 10 distinct files within 60 minutes.
  This may indicate data exfiltration or unauthorized bulk access to team files.
  The threshold should be tuned to your environment.
Reference: https://learn.dropbox.com/video-library/upload-and-download-files
Reports:
  MITRE ATT&CK:
    - TA0010:T1567
Tags:
  - Dropbox
  - Exfiltration
Runbook: |
  1. Query Dropbox.TeamEvent for all file_download events by actor:user:email in the 2 hours around this alert to identify the full list of assets[0]:path:contextual values downloaded
  2. Check involve_non_team_member and the origin:geo_location:ip_address to determine if the downloads originated from an external user or an unexpected location
  3. Search for other suspicious activity by this user in the past 24 hours, including external sharing events, ownership transfers, or logins from new devices or locations

Stages and Predicates

Fires on Dropbox.TeamEvent events when the condition below holds.

Condition

  • event_type._tag is file_download
Alert cadence
alerts after 11 matches within 1h

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
useractor.user.email
involve_non_team_member
ip_addressorigin.geo_location.ip_address

Response runbook

1. Query Dropbox.TeamEvent for all file_download events by actor:user:email in the 2 hours around this alert to identify the full list of assets[0]:path:contextual values downloaded

2. Check involve_non_team_member and the origin:geo_location:ip_address to determine if the downloads originated from an external user or an unexpected location

3. Search for other suspicious activity by this user in the past 24 hours, including external sharing events, ownership transfers, or logins from new devices or locations

Worked example

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

Sample Test Event
{
  "actor": {
    "user": {
      "_tag": "team_member",
      "email": "alice@company.com"
    }
  },
  "assets": [
    {
      "path": {
        "contextual": "/alice/report.pdf"
      }
    }
  ],
  "event_type": {
    "_tag": "file_download"
  },
  "involve_non_team_member": false,
  "origin": {
    "geo_location": {
      "ip_address": "1.2.3.4"
    }
  }
}

Dropbox User Disabled 2FA

#
Severity
low
Entities
emails, ip_addresses, usernames
Log types
Dropbox.TeamEvent
Reference
help.dropbox.com
Source
github.com/panther-labs/panther-analysis

Dropbox user has disabled 2fa login

Detection logic

def rule(event):
    return all(
        [
            event.deep_get("details", ".tag", default="") == "tfa_change_status_details",
            event.deep_get("details", "new_value", ".tag") == "disabled",
        ]
    )


def title(event):
    actor = event.deep_get("actor", "user", "email", default="<EMAIL_NOT_FOUND>")
    target = event.deep_get("context", "email", default="<TARGET_NOT_FOUND>")
    return f"Dropbox: [{actor}] disabled 2FA for [{target}]."

Rule specification

AnalysisType: rule
Description: Dropbox user has disabled 2fa login
DisplayName: "Dropbox User Disabled 2FA"
Enabled: true
Filename: dropbox_user_disabled_2fa.py
Reference: https://help.dropbox.com/account-access/enable-two-step-verification
Severity: Low
DedupPeriodMinutes: 60
LogTypes:
  - Dropbox.TeamEvent
RuleID: "Dropbox.User.Disabled.2FA"
Threshold: 1

Stages and Predicates

Fires on Dropbox.TeamEvent events when all of the conditions below hold.

Condition

  • details..tag is tfa_change_status_details
  • details.new_value..tag is disabled

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.user.email
emailcontext.email

Worked example

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

Sample Test Event
{
  "actor": {
    "_tag": "user",
    "user": {
      "_tag": "team_member",
      "account_id": "dbid:AAAAAAAAAAAAAAAA",
      "display_name": "Alice Bob",
      "email": "alice.bob@company.io",
      "team_member_id": "dbmid:AABBBBBBBBBBBBBBBBBBBBBBB"
    }
  },
  "context": {
    "_tag": "team_member",
    "account_id": "dbid:AAAAAAAAAAAAAAAA",
    "display_name": "Alice Bob",
    "email": "alice.bob@company.io",
    "team_member_id": "dbmid:AABBBBBBBBBBBBBBBBBBBBBBB"
  },
  "details": {
    ".tag": "tfa_change_status_details",
    "new_value": {
      ".tag": "disabled"
    },
    "previous_value": {
      ".tag": "authenticator"
    },
    "used_rescue_code": true
  },
  "event_category": {
    "_tag": "tfa"
  },
  "event_type": {
    "_tag": "tfa_change_status",
    "description": "Enabled/disabled/changed two-step verification setting"
  },
  "involve_non_team_member": false,
  "origin": {
    "access_method": {
      ".tag": "end_user",
      "end_user": {
        ".tag": "web",
        "session_id": "dbwsid:237034608707419186011941491025532848312"
      }
    },
    "geo_location": {
      "city": "Austin",
      "country": "US",
      "ip_address": "1.2.3.4",
      "region": "Texas"
    }
  },
  "p_any_emails": [
    "alice.bob@company.io"
  ],
  "p_any_ip_addresses": [
    "1.2.3.4"
  ],
  "p_any_usernames": [
    "Alice Bob"
  ],
  "p_event_time": "2023-04-18 18:16:27",
  "p_log_type": "Dropbox.TeamEvent",
  "p_parse_time": "2023-04-18 18:18:46.808",
  "p_row_id": "0eb86fcfca9bb1cdce9defd217b8ac03",
  "p_schema_version": 0,
  "p_source_id": "b09c205e-42af-4933-8b18-b910985eb7fb",
  "p_source_label": "dropbox1",
  "timestamp": "2023-04-18 18:16:27"
}