Detection rules › Panther
Panther rules: databricks
Databricks Access to Multiple Workspaces
#Detects users accessing 5 or more distinct workspaces within 24 hours, which may indicate lateral movement, reconnaissance, or compromised credentials.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Lateral Movement |
Detection logic
from panther_databricks_helpers import SYSTEM_USERS, databricks_alert_context
def rule(event):
# Filter out system users and unknown
user = event.deep_get("userIdentity", "email", default="")
if user in SYSTEM_USERS or user in ("", "unknown"):
return False
# Must have workspace context
return event.get("workspaceId") is not None
def dedup(event):
user = event.deep_get("userIdentity", "email", default="unknown")
return user
def unique(event):
workspace = event.get("workspaceId", "unknown")
return workspace
def title(event):
user = event.deep_get("userIdentity", "email", default="Unknown User")
return f"User accessing multiple workspaces: {user} (≥5 workspaces/day)"
def alert_context(event):
return databricks_alert_context(event)
Rule specification
AnalysisType: rule
Filename: databricks_access_to_multiple_workspaces.py
RuleID: "Databricks.Audit.AccessToMultipleWorkspaces"
DisplayName: "Databricks Access to Multiple Workspaces"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Lateral Movement
- Reconnaissance
Reports:
MITRE ATT&CK:
- TA0008:T1021 # Remote Services
Severity: Medium
Threshold: 5
DedupPeriodMinutes: 1440
Description: >
Detects users accessing 5 or more distinct workspaces within 24 hours, which may indicate
lateral movement, reconnaissance, or compromised credentials.
Runbook: |
1. Query audit logs for all workspace access by this user in the past 7 days to establish normal patterns
2. Check if the user performed unusual actions (data downloads, permission changes) across multiple workspaces
3. Find all users accessing multiple workspaces in the past 30 days to establish baseline
Reference: https://github.com/andyweaves/system-tables-audit-logs/blob/main/resources/queries_and_alerts.json
SummaryAttributes:
- actor
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
userIdentity.emailis not one ofSystem-UseruserIdentity.emailis not one of"",unknownworkspaceIdis present
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
userIdentity.email | eq | System-User | excludes:userIdentity.email field:"userIdentity.email" value:"System-User" |
userIdentity.email | in | unknown | excludes:userIdentity.email field:"userIdentity.email" value:"unknown" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
workspaceId | is_not_null | field:"workspaceId" kind:is_not_null |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
email | userIdentity.email |
Response runbook
1. Query audit logs for all workspace access by this user in the past 7 days to establish normal patterns
2. Check if the user performed unusual actions (data downloads, permission changes) across multiple workspaces
3. Find all users accessing multiple workspaces in the past 30 days to establish baseline
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "login",
"response": {
"statusCode": 200
},
"serviceName": "accounts",
"timestamp": 1704067200000,
"userIdentity": {
"email": "user@example.com"
},
"workspaceId": "1234567890123456"
}
Databricks Access Token Revoked
#Detects revocation of Databricks access tokens. Token revocation may be routine credential rotation or could indicate an attacker covering their tracks after using a compromised token.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Stealth |
Detection logic
from panther_databricks_helpers import databricks_alert_context
def rule(event):
if event.get("serviceName") != "accounts":
return False
return event.get("actionName") == "revokeDbToken"
def title(event):
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
token_id = event.deep_get("requestParams", "tokenId", default="Unknown Token")
return f"Access token revoked: {token_id} by {actor}"
def dedup(event):
token_id = event.deep_get("requestParams", "tokenId", default="unknown")
return f"token_revoked_{token_id}"
def alert_context(event):
return databricks_alert_context(
event,
additional_fields={
"token_id": event.deep_get("requestParams", "tokenId"),
},
)
Rule specification
AnalysisType: rule
Filename: databricks_access_token_revoked.py
RuleID: "Databricks.Audit.AccessTokenRevoked"
DisplayName: "Databricks Access Token Revoked"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Defense Evasion
Reports:
MITRE ATT&CK:
- TA0005:T1070 # Indicator Removal
Severity: Info
Description: >
Detects revocation of Databricks access tokens. Token revocation may be routine
credential rotation or could indicate an attacker covering their tracks after
using a compromised token.
Runbook: |
1. Verify the token revocation was intentional and authorized
2. Check if the token was recently created or used from unusual IPs
3. Look for preceding suspicious activity associated with this token
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/tree/main/base/detections/behavioral
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
serviceNameisaccountsactionNameisrevokeDbToken
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
actionName | eq |
| field:"actionName" kind:eq value:"revokeDbToken" |
serviceName | eq |
| field:"serviceName" kind:eq value:"accounts" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
tokenId | requestParams.tokenId |
email | userIdentity.email |
Response runbook
1. Verify the token revocation was intentional and authorized
2. Check if the token was recently created or used from unusual IPs
3. Look for preceding suspicious activity associated with this token
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "revokeDbToken",
"requestParams": {
"tokenId": "dapi-abc123def456"
},
"response": {
"statusCode": 200
},
"serviceName": "accounts",
"userIdentity": {
"email": "user@example.com"
}
}
Databricks Account Admin Privileged Role Assignment
#Detects when account-level admin privileges are granted in Databricks through direct role assignments or administrative group membership. Account admins have extensive control across all workspaces and should be carefully monitored. Successful grants are elevated to HIGH severity.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Privilege Escalation |
Detection logic
from panther_databricks_helpers import (
ADMIN_PRIVILEGE_ACTIONS,
databricks_alert_context,
extract_group_identifier,
extract_target_principal,
get_principal_type,
is_admin_privilege_action,
)
REMOVAL_ACTIONS = ["removeAdmin", "removePrincipalFromGroup"]
def rule(event):
# Must be account-level event
if event.get("auditLevel") != "ACCOUNT_LEVEL":
return False
# Exclude privilege removals — this rule detects grants only
if event.get("actionName") in REMOVAL_ACTIONS:
return False
return is_admin_privilege_action(event)
def severity(event):
status_code = event.deep_get("response", "statusCode")
return "HIGH" if status_code == 200 else "MEDIUM"
def title(event):
action = event.get("actionName", "Unknown Action")
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
target = extract_target_principal(event) or "Unknown Principal"
status_code = event.deep_get("response", "statusCode")
status = "Granted" if status_code == 200 else "Attempted to grant"
# Check if it's direct admin action or group-based
if action in ADMIN_PRIVILEGE_ACTIONS["direct"]:
return f"{status} account admin privileges to {target} by {actor}"
group = extract_group_identifier(event)
return f"{status} admin group membership ({group}) to {target} by {actor}"
def dedup(event):
target_principal = extract_target_principal(event) or "unknown"
return f"account_admin_privilege_assignment_{target_principal}"
def alert_context(event):
target_principal = extract_target_principal(event)
principal_type = get_principal_type(target_principal) if target_principal else "Unknown"
return databricks_alert_context(
event,
additional_fields={
"privilege_scope": "ACCOUNT_LEVEL",
"target_principal": target_principal,
"principal_type": principal_type,
"target_group": extract_group_identifier(event),
},
)
Rule specification
AnalysisType: rule
Filename: databricks_account_admin_privileged_role_assignment.py
RuleID: "Databricks.Audit.AccountAdminPrivilegedRoleAssignment"
DisplayName: "Databricks Account Admin Privileged Role Assignment"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Privilege Escalation
- Persistence
Reports:
MITRE ATT&CK:
- TA0004:T1098 # Account Manipulation
- TA0003:T1136 # Create Account
Severity: Medium
Description: >
Detects when account-level admin privileges are granted in Databricks through direct role
assignments or administrative group membership. Account admins have extensive control across
all workspaces and should be carefully monitored. Successful grants are elevated to HIGH severity.
Runbook: |
1. Query audit logs for all account-level administrative actions by the target principal in the 24 hours after this privilege grant
2. Check if the target principal accessed multiple workspaces or performed bulk operations in the 6 hours after receiving admin rights
3. Find all account admin grants in the past 90 days to identify unusual patterns or privilege escalation chains
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/blob/main/base/detections/event-based/account_admin_privileged_role_assignment.py
SummaryAttributes:
- actor
- target_principal
- principal_type
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
auditLevelisACCOUNT_LEVELactionNameis not one ofremoveAdmin,removePrincipalFromGroup
This rule also runs imperative logic the parser cannot express as a filter. The conditions above are the structured part it could extract.
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
actionName | in | removeAdmin, removePrincipalFromGroup | excludes:actionName field:"actionName" value:"removeAdmin" field:"actionName" value:"removePrincipalFromGroup" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
auditLevel | eq |
| field:"auditLevel" kind:eq value:"ACCOUNT_LEVEL" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
email | userIdentity.email |
Response runbook
1. Query audit logs for all account-level administrative actions by the target principal in the 24 hours after this privilege grant
2. Check if the target principal accessed multiple workspaces or performed bulk operations in the 6 hours after receiving admin rights
3. Find all account admin grants in the past 90 days to identify unusual patterns or privilege escalation chains
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"accountId": "12345678-1234-1234-1234-123456789012",
"actionName": "setAccountAdmin",
"auditLevel": "ACCOUNT_LEVEL",
"requestParams": {
"targetUserName": "newadmin@example.com"
},
"response": {
"statusCode": 200
},
"serviceName": "accounts",
"sourceIPAddress": "198.51.100.1",
"timestamp": 1234567890000,
"userIdentity": {
"email": "superadmin@example.com"
}
}
Databricks Account-Level Configuration Changes
#Detects configuration changes at the Databricks account level, including account settings, metastore configurations, and SSO settings. Account-level changes affect all workspaces and should be monitored for unauthorized modifications.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Detection logic
from panther_databricks_helpers import databricks_alert_context, is_config_change
def rule(event):
# Must be account-level audit event
if event.get("auditLevel") != "ACCOUNT_LEVEL":
return False
# Account settings changes
if is_config_change(event, config_category="account"):
return True
# SSO configuration changes
if is_config_change(event, config_category="sso"):
return True
return False
def title(event):
action = event.get("actionName", "Unknown Action")
service = event.get("serviceName", "Unknown Service")
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
status_code = event.deep_get("response", "statusCode")
status = "Success" if status_code == 200 else "Failed"
return f"Account-level configuration change ({service}.{action}) by {actor} - {status}"
def dedup(event):
service = event.get("serviceName", "unknown")
action = event.get("actionName", "unknown")
return f"account_config_change_{service}_{action}"
def alert_context(event):
return databricks_alert_context(
event,
additional_fields={
"change_scope": "ACCOUNT_LEVEL",
"request_params": event.get("requestParams"),
},
)
Rule specification
AnalysisType: rule
Filename: databricks_config_changes_account_level.py
RuleID: "Databricks.Audit.ConfigChangesAccountLevel"
DisplayName: "Databricks Account-Level Configuration Changes"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Persistence
Reports:
MITRE ATT&CK:
- TA0003:T1098 # Account Manipulation
Severity: Info
Description: >
Detects configuration changes at the Databricks account level, including account settings,
metastore configurations, and SSO settings. Account-level changes affect all workspaces and
should be monitored for unauthorized modifications.
Runbook: |
1. Query audit logs for all account-level changes by the actor in the 24 hours around this event
2. Check if this configuration change affected multiple workspaces based on activity in the 6 hours after the change
3. Find all account-level configuration changes in the past 30 days to identify patterns
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/blob/main/base/detections/event-based/configuration_changes_account_level.py
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
auditLevelisACCOUNT_LEVELany of:
serviceNameisworkspaceall of:
serviceNameis notworkspaceserviceNameisaccounts
all of:
serviceNameis notworkspaceserviceNameis notaccountsserviceNameisssoConfigBackend
serviceNameisworkspaceall of:
serviceNameis notworkspaceserviceNameisaccounts
all of:
serviceNameis notworkspaceserviceNameis notaccountsserviceNameisssoConfigBackend
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 |
|---|---|---|---|
auditLevel | eq |
| field:"auditLevel" kind:eq value:"ACCOUNT_LEVEL" |
serviceName | eq |
| field:"serviceName" kind:eq |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
serviceName | |
actionName | |
email | userIdentity.email |
Response runbook
1. Query audit logs for all account-level changes by the actor in the 24 hours around this event
2. Check if this configuration change affected multiple workspaces based on activity in the 6 hours after the change
3. Find all account-level configuration changes in the past 30 days to identify patterns
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "updateAccountSettings",
"auditLevel": "ACCOUNT_LEVEL",
"requestParams": {
"setting": "defaultWorkspaceRegion",
"value": "us-west-2"
},
"response": {
"statusCode": 200
},
"serviceName": "accounts",
"sourceIPAddress": "198.51.100.1",
"timestamp": 1234567890000,
"userIdentity": {
"email": "admin@example.com"
}
}
Databricks Attempted Logon From Denied IP
#Detects blocked login attempts from IP addresses explicitly denied by workspace IP access control policies. This excludes known service agents and telemetry operations. While these attempts were successfully blocked, they may indicate reconnaissance or unauthorized access attempts.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Detection logic
from panther_databricks_helpers import databricks_alert_context, filter_noise
def rule(event):
if event.get("serviceName") != "accounts":
return False
if event.get("actionName") != "IpAccessDenied":
return False
# Filter out system noise using helper
if filter_noise(event):
return False
return True
def title(event):
user = event.deep_get("userIdentity", "email", default="Unknown User")
source_ip = event.get("sourceIPAddress", "Unknown IP")
workspace = event.get("workspaceId", "Unknown Workspace")
return (
f"Blocked login attempt from denied IP {source_ip} for user {user} to workspace {workspace}"
)
def dedup(event):
source_ip = event.get("sourceIPAddress", "unknown")
workspace = event.get("workspaceId", "unknown")
return f"denied_ip_login_{workspace}_{source_ip}"
def alert_context(event):
return databricks_alert_context(
event, additional_fields={"path": event.deep_get("requestParams", "path")}
)
Rule specification
AnalysisType: rule
Filename: databricks_attempted_logon_from_denied_ip.py
RuleID: "Databricks.Audit.AttemptedLogonFromDeniedIP"
DisplayName: "Databricks Attempted Logon From Denied IP"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Initial Access
- Reconnaissance
Reports:
MITRE ATT&CK:
- TA0001:T1078 # Valid Accounts
Severity: Info
Description: >
Detects blocked login attempts from IP addresses explicitly denied by workspace IP access
control policies. This excludes known service agents and telemetry operations. While these
attempts were successfully blocked, they may indicate reconnaissance or unauthorized access attempts.
Runbook: |
1. Count all login attempts from the source IP (sourceIPAddress) in the 1 hour before and after this blocked attempt
2. Check if the source IP is associated with known VPN services, cloud providers, or threat intelligence feeds
3. Find all successful and failed login attempts for this user in the 24 hours around the alert to identify credential stuffing patterns
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/blob/main/base/detections/event-based/attempted_logon_from_denied_ip.py
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
serviceNameisaccountsactionNameisIpAccessDenieduserAgentdoes not containDatabricks-Service/driveruserAgentdoes not containDatabricks-RuntimeuserAgentdoes not containDelta-Sharing-SparkStructuredStreaminguserAgentdoes not containRawDBHttpClientuserAgentdoes not containmlflow-pythonuserAgentdoes not containobsSDK-scalauserAgentdoes not containwsfsuserAgentdoes not containfeature-storerequestParams.pathdoes not contain/telemetryrequestParams.pathdoes not contain/delta-commitrequestParams.pathdoes not contain/healthrequestParams.pathdoes not contain/metricsrequestParams.pathdoes not contain/status
This rule also runs imperative logic the parser cannot express as a filter. The conditions above are the structured part it could extract.
Exclusions
The rule actively suppresses these predicates.
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
actionName | eq |
| field:"actionName" kind:eq value:"IpAccessDenied" |
serviceName | eq |
| field:"serviceName" kind:eq value:"accounts" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
sourceIPAddress | |
email | userIdentity.email |
workspaceId |
Response runbook
1. Count all login attempts from the source IP (sourceIPAddress) in the 1 hour before and after this blocked attempt
2. Check if the source IP is associated with known VPN services, cloud providers, or threat intelligence feeds
3. Find all successful and failed login attempts for this user in the 24 hours around the alert to identify credential stuffing patterns
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "IpAccessDenied",
"requestParams": {
"path": "/login"
},
"response": {
"statusCode": 403
},
"serviceName": "accounts",
"sourceIPAddress": "192.0.2.100",
"timestamp": 1234567890000,
"userAgent": "Mozilla/5.0",
"userIdentity": {
"email": "user@example.com"
},
"workspaceId": "1234567890123456"
}
Databricks Data Downloads From Control Plane
#Detects high volume data downloads from the control plane which may indicate data exfiltration. Monitors download actions including query results, notebooks, and models.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Exfiltration |
Detection logic
from panther_databricks_helpers import DOWNLOAD_ACTIONS, databricks_alert_context
def rule(event):
action = event.get("actionName")
if action not in DOWNLOAD_ACTIONS:
return False
# Exclude source exports
if action == "workspaceExport":
export_format = event.deep_get("requestParams", "workspaceExportFormat")
if export_format == "SOURCE":
return False
# Exclude arrows format
if action == "downloadQueryResult":
file_type = event.deep_get("requestParams", "fileType")
if file_type == "arrows":
return False
return True
def dedup(event):
user = event.deep_get("userIdentity", "email", default="unknown")
return f"data_download_{user}"
def title(event):
user = event.deep_get("userIdentity", "email", default="Unknown User")
action = event.get("actionName", "download")
return f"High volume data downloads by {user} ({action})"
def alert_context(event):
return databricks_alert_context(event)
Rule specification
AnalysisType: rule
Filename: databricks_data_downloads_from_control_plane.py
RuleID: "Databricks.Audit.DataDownloadsFromControlPlane"
DisplayName: "Databricks Data Downloads From Control Plane"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Exfiltration
Reports:
MITRE ATT&CK:
- TA0010:T1567 # Exfiltration Over Web Service
Severity: Medium
Threshold: 21
DedupPeriodMinutes: 60
Description: >
Detects high volume data downloads from the control plane which may indicate
data exfiltration. Monitors download actions including query results, notebooks, and models.
Runbook: |
1. Query audit logs for all download actions by this user in the past 24 hours to calculate total volume
2. Check if the downloaded data contains sensitive classifications or PII in the 6 hours around this alert
3. Find all users with high download rates in the past 30 days to establish baseline
Reference: https://github.com/andyweaves/system-tables-audit-logs/blob/main/resources/queries_and_alerts.json
SummaryAttributes:
- actor
Stages and Predicates
Fires on Databricks.Audit events when the condition below holds.
Condition
actionNameis one ofdownloadPreviewResults,downloadLargeResults,filesGet,getModelVersionDownloadUri,getModelVersionSignedDownloadUri
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 |
|---|---|---|---|
actionName | in |
| field:"actionName" kind:in |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
email | userIdentity.email |
actionName |
Response runbook
1. Query audit logs for all download actions by this user in the past 24 hours to calculate total volume
2. Check if the downloaded data contains sensitive classifications or PII in the 6 hours around this alert
3. Find all users with high download rates in the past 30 days to establish baseline
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "downloadPreviewResults",
"response": {
"statusCode": 200
},
"serviceName": "sql",
"timestamp": 1704067200000,
"userIdentity": {
"email": "user@example.com"
}
}
Databricks Data Movement with Explicit Credentials
#Detects creation or modification of storage credentials, connections, and external locations that could facilitate data exfiltration. These operations establish direct paths to external storage and may indicate data movement preparation. Mount point creation is covered separately by Databricks.Audit.MountPointCreation.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Exfiltration |
Detection logic
from panther_databricks_helpers import DATA_MOVEMENT_CREDENTIAL_ACTIONS, databricks_alert_context
def rule(event):
action = event.get("actionName")
# mount is covered by databricks_mount_point_creation; skip it here
return action in DATA_MOVEMENT_CREDENTIAL_ACTIONS and action != "mount"
def title(event):
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
action = event.get("actionName", "unknown")
workspace = event.get("workspaceId", "Unknown Workspace")
return f"Data movement credential operation: {action} in workspace {workspace} by {actor}"
def dedup(event):
actor = event.deep_get("userIdentity", "email", default="unknown")
action = event.get("actionName", "unknown")
return f"data_movement_cred_{actor}_{action}"
def alert_context(event):
return databricks_alert_context(
event,
additional_fields={
"request_params": event.get("requestParams"),
},
)
Rule specification
AnalysisType: rule
Filename: databricks_data_movement_explicit_credentials.py
RuleID: "Databricks.Audit.DataMovementExplicitCredentials"
DisplayName: "Databricks Data Movement with Explicit Credentials"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Exfiltration
Reports:
MITRE ATT&CK:
- TA0010:T1537 # Transfer Data to Cloud Account
Severity: Info
Description: >
Detects creation or modification of storage credentials, connections, and external
locations that could facilitate data exfiltration. These operations establish direct
paths to external storage and may indicate data movement preparation. Mount point
creation is covered separately by Databricks.Audit.MountPointCreation.
Runbook: |
1. Verify the credential/connection creation was part of an approved workflow
2. Check the target storage location for sensitivity
3. Monitor for subsequent data access through the new credential
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/tree/main/base/detections/behavioral
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
actionNameis one ofmount,createStorageCredential,updateStorageCredential,createConnection,updateConnectionactionNameis notmount
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
actionName | in |
| field:"actionName" kind:in |
actionName | ne |
| field:"actionName" kind:ne value:"mount" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
actionName | |
workspaceId | |
email | userIdentity.email |
Response runbook
1. Verify the credential/connection creation was part of an approved workflow
2. Check the target storage location for sensitivity
3. Monitor for subsequent data access through the new credential
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "createStorageCredential",
"requestParams": {
"name": "external-s3-cred"
},
"response": {
"statusCode": 200
},
"serviceName": "unityCatalog",
"userIdentity": {
"email": "admin@example.com"
}
}
Databricks Delta Sharing IP Access Failures
#Detects blocked Delta Sharing access attempts due to IP access list restrictions, which may indicate unauthorized access attempts from unexpected locations.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Detection logic
from panther_databricks_helpers import databricks_alert_context
# Keywords that indicate IP-based access denials
IP_DENIAL_KEYWORDS = ["address", "network", "cidr", "allowlist", "blocklist"]
def rule(event):
if event.get("serviceName") != "deltaSharingAccess":
return False
status_code = event.deep_get("response", "statusCode")
if status_code not in [403, 401]:
return False
error_message = event.deep_get("response", "errorMessage", default="").lower()
return any(keyword in error_message for keyword in IP_DENIAL_KEYWORDS)
def title(event):
recipient = event.deep_get("requestParams", "recipientName", default="Unknown Recipient")
source_ip = event.get("sourceIPAddress", "Unknown IP")
return f"Delta Sharing access blocked from {source_ip} for recipient {recipient}"
def alert_context(event):
return databricks_alert_context(event)
Rule specification
AnalysisType: rule
Filename: databricks_delta_sharing_ip_access_failures.py
RuleID: "Databricks.Audit.DeltaSharingIPAccessFailures"
DisplayName: "Databricks Delta Sharing IP Access Failures"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Delta Sharing
- Initial Access
Reports:
MITRE ATT&CK:
- TA0001:T1078 # Valid Accounts
Severity: Medium
Description: >
Detects blocked Delta Sharing access attempts due to IP access list restrictions,
which may indicate unauthorized access attempts from unexpected locations.
Runbook: |
1. Query audit logs for all Delta Sharing access attempts from this IP in the past 24 hours
2. Check if the source IP is associated with known threats or unexpected geographic locations
3. Find all Delta Sharing access failures in the past 7 days to identify patterns
Reference: https://github.com/andyweaves/system-tables-audit-logs/blob/main/resources/queries_and_alerts.json
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
serviceNameisdeltaSharingAccessresponse.statusCodeis one of403,401any of:
response.errorMessagecontainsaddressresponse.errorMessagecontainsnetworkresponse.errorMessagecontainscidrresponse.errorMessagecontainsallowlistresponse.errorMessagecontainsblocklist
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
response.errorMessage | contains |
| field:"response.errorMessage" kind:contains |
response.statusCode | in |
| field:"response.statusCode" kind:in |
serviceName | eq |
| field:"serviceName" kind:eq value:"deltaSharingAccess" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
sourceIPAddress | |
recipientName | requestParams.recipientName |
Response runbook
1. Query audit logs for all Delta Sharing access attempts from this IP in the past 24 hours
2. Check if the source IP is associated with known threats or unexpected geographic locations
3. Find all Delta Sharing access failures in the past 7 days to identify patterns
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "getShare",
"response": {
"errorMessage": "IP address not allowed",
"statusCode": 403
},
"serviceName": "deltaSharingAccess",
"sourceIPAddress": "203.0.113.50",
"timestamp": 1704067200000
}
Databricks Delta Sharing Recipient Without IP ACLs
#Detects creation of Delta Sharing recipients without IP access list restrictions, which could allow unauthorized data access from any location.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
Detection logic
from panther_databricks_helpers import databricks_alert_context
def rule(event):
if event.get("actionName") != "createRecipient":
return False
# Check if IP access list is configured
# Alert if IP ACL is missing, empty string, or empty list (all falsy)
ip_access_list = event.deep_get("requestParams", "ipAccessList")
return not ip_access_list
def title(event):
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
recipient = event.deep_get("requestParams", "name", default="Unknown Recipient")
return f"Delta Sharing recipient created without IP ACLs: {recipient} by {actor}"
def alert_context(event):
return databricks_alert_context(
event,
additional_fields={"recipient_name": event.deep_get("requestParams", "name")},
)
Rule specification
AnalysisType: rule
Filename: databricks_delta_sharing_recipient_without_ip_acls.py
RuleID: "Databricks.Audit.DeltaSharingRecipientWithoutIPACLs"
DisplayName: "Databricks Delta Sharing Recipient Without IP ACLs"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Delta Sharing
- Defense Evasion
Reports:
MITRE ATT&CK:
- TA0005:T1562 # Impair Defenses
Severity: Medium
Description: >
Detects creation of Delta Sharing recipients without IP access list restrictions,
which could allow unauthorized data access from any location.
Runbook: |
1. Query audit logs for all Delta Sharing recipient creations in the past 30 days
2. Check if this recipient has accessed shared data in the 24 hours after creation
3. Find all recipients without IP ACLs to establish security posture
Reference: https://github.com/andyweaves/system-tables-audit-logs/blob/main/resources/queries_and_alerts.json
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
actionNameiscreateRecipientrequestParams.ipAccessListis empty
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
actionName | eq |
| field:"actionName" kind:eq value:"createRecipient" |
requestParams.ipAccessList | is_null | field:"requestParams.ipAccessList" kind:is_null |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
name | requestParams.name |
email | userIdentity.email |
Response runbook
1. Query audit logs for all Delta Sharing recipient creations in the past 30 days
2. Check if this recipient has accessed shared data in the 24 hours after creation
3. Find all recipients without IP ACLs to establish security posture
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "createRecipient",
"requestParams": {
"name": "external-partner"
},
"response": {
"statusCode": 200
},
"serviceName": "deltaSharingControl",
"timestamp": 1704067200000,
"userIdentity": {
"email": "admin@example.com"
}
}
Databricks Destructive Activities
#Detects high volume destructive activities by a single user which may indicate malicious data destruction, ransomware, or insider threats.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Impact |
Detection logic
from panther_databricks_helpers import SYSTEM_USERS, databricks_alert_context
# Destructive action prefixes/names to match
DESTRUCTIVE_PREFIXES = ["delete", "drop", "trash", "destroy", "purge"]
def rule(event):
action = event.get("actionName", "").lower()
# Exclude system users
user = event.deep_get("userIdentity", "email", default="")
if user in SYSTEM_USERS:
return False
# Exclude non-destructive actions that contain "delete" as substring
if action.startswith("undelete") or action.startswith("restore"):
return False
# Check for destructive action prefixes
return any(action.startswith(prefix) for prefix in DESTRUCTIVE_PREFIXES)
def dedup(event):
user = event.deep_get("userIdentity", "email", default="unknown")
return f"destructive_{user}"
def title(event):
user = event.deep_get("userIdentity", "email", default="Unknown User")
action = event.get("actionName", "delete")
return f"High volume destructive activities by {user} (>50/day, action: {action})"
def alert_context(event):
return databricks_alert_context(event)
Rule specification
AnalysisType: rule
Filename: databricks_destructive_activities.py
RuleID: "Databricks.Audit.DestructiveActivities"
DisplayName: "Databricks Destructive Activities"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Impact
Reports:
MITRE ATT&CK:
- TA0040:T1485 # Data Destruction
Severity: Medium
Threshold: 50
DedupPeriodMinutes: 1440
Description: >
Detects high volume destructive activities by a single user which may indicate
malicious data destruction, ransomware, or insider threats.
Runbook: |
1. Query audit logs for all delete actions by this user in the past 7 days to identify patterns
2. Check if deleted resources can be recovered or if backups exist
3. Find all users with high deletion rates in the past 30 days to establish baseline
Reference: https://github.com/andyweaves/system-tables-audit-logs/blob/main/resources/queries_and_alerts.json
SummaryAttributes:
- actor
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
userIdentity.emailis not one ofSystem-UseractionNamedoes not start withundeleteactionNamedoes not start withrestoreany of:
actionNamestarts withdeleteactionNamestarts withdropactionNamestarts withtrashactionNamestarts withdestroyactionNamestarts withpurge
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
actionName | starts_with | restore | excludes:actionName field:"actionName" value:"restore" |
actionName | starts_with | undelete | excludes:actionName field:"actionName" value:"undelete" |
userIdentity.email | eq | System-User | excludes:userIdentity.email field:"userIdentity.email" value:"System-User" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
actionName | starts_with |
| field:"actionName" kind:starts_with |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
email | userIdentity.email |
actionName |
Response runbook
1. Query audit logs for all delete actions by this user in the past 7 days to identify patterns
2. Check if deleted resources can be recovered or if backups exist
3. Find all users with high deletion rates in the past 30 days to establish baseline
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "deleteTable",
"response": {
"statusCode": 200
},
"serviceName": "unityCatalog",
"timestamp": 1704067200000,
"userIdentity": {
"email": "user@example.com"
}
}
Databricks Employee Logon
#Detects when a Databricks employee successfully logs into a workspace using GENIE_AUTH authentication. This is typically for legitimate support purposes but should be tracked for awareness.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Detection logic
from panther_databricks_helpers import (
databricks_alert_context,
is_databricks_employee_auth,
is_login_action,
)
def rule(event):
if event.get("serviceName") != "accounts":
return False
if not is_login_action(event):
return False
if not is_databricks_employee_auth(event):
return False
# Check for successful response
status_code = event.deep_get("response", "statusCode")
if status_code != 200:
return False
# Check for workspace-level audit
if event.get("auditLevel") != "WORKSPACE_LEVEL":
return False
return True
def title(event):
user = event.deep_get("userIdentity", "email", default="Unknown User")
workspace = event.get("workspaceId", "Unknown Workspace")
return f"Databricks employee logged into workspace {workspace} as {user}"
def alert_context(event):
return databricks_alert_context(
event,
additional_fields={"auth_method": event.deep_get("requestParams", "authentication_method")},
)
Rule specification
AnalysisType: rule
Filename: databricks_employee_logon.py
RuleID: "Databricks.Audit.EmployeeLogon"
DisplayName: "Databricks Employee Logon"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Initial Access
Reports:
MITRE ATT&CK:
- TA0001:T1078 # Valid Accounts
Severity: Info
Description: >
Detects when a Databricks employee successfully logs into a workspace using GENIE_AUTH authentication.
This is typically for legitimate support purposes but should be tracked for awareness.
Runbook: |
1. Query Databricks audit logs for all actions by this Databricks employee (userIdentity.email) in the 24 hours before and after the alert
2. Check if there are open support cases or authorized maintenance windows that would explain this employee access
3. Find all other Databricks employee logins to this workspace in the past 30 days to identify patterns
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/blob/main/base/detections/event-based/databricks_employee_logon.py
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
serviceNameisaccountsactionNameis one ofaadBrowserLogin,aadTokenLogin,certLogin,jwtLogin,loginrequestParams.authentication_methodisGENIE_AUTHresponse.statusCodeis200auditLevelisWORKSPACE_LEVEL
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
actionName | in |
| field:"actionName" kind:in |
auditLevel | eq |
| field:"auditLevel" kind:eq value:"WORKSPACE_LEVEL" |
requestParams.authentication_method | eq |
| field:"requestParams.authentication_method" kind:eq value:"GENIE_AUTH" |
response.statusCode | eq |
| field:"response.statusCode" kind:eq value:"200" |
serviceName | eq |
| field:"serviceName" kind:eq value:"accounts" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
workspaceId | |
email | userIdentity.email |
Response runbook
1. Query Databricks audit logs for all actions by this Databricks employee (userIdentity.email) in the 24 hours before and after the alert
2. Check if there are open support cases or authorized maintenance windows that would explain this employee access
3. Find all other Databricks employee logins to this workspace in the past 30 days to identify patterns
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"accountId": "12345678-1234-1234-1234-123456789012",
"actionName": "login",
"auditLevel": "WORKSPACE_LEVEL",
"requestId": "req-123",
"requestParams": {
"authentication_method": "GENIE_AUTH"
},
"response": {
"statusCode": 200
},
"serviceName": "accounts",
"sessionId": "session-123",
"sourceIPAddress": "198.51.100.1",
"timestamp": 1234567890000,
"userAgent": "Mozilla/5.0",
"userIdentity": {
"email": "support@databricks.com"
},
"version": "2.0",
"workspaceId": "1234567890123456"
}
Databricks Global Init Script Changes
#Detects modifications to global initialization scripts which run on all clusters at startup. These scripts can be used for persistence or to execute malicious code across the environment. All script creations, updates, and deletions are monitored.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Execution | |
| Persistence |
Detection logic
from panther_databricks_helpers import databricks_alert_context
def rule(event):
return event.get("serviceName") == "globalInitScripts"
def title(event):
action = event.get("actionName", "Unknown Action")
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
script_name = event.deep_get("requestParams", "name", default="Unknown Script")
return f"Global init script {action}: {script_name} by {actor}"
def dedup(event):
script_name = event.deep_get("requestParams", "name", default="unknown")
script_id = event.deep_get("requestParams", "script_id", default="unknown")
return f"global_init_script_{script_id}_{script_name}"
def alert_context(event):
return databricks_alert_context(
event,
additional_fields={
"script_name": event.deep_get("requestParams", "name"),
"script_id": event.deep_get("requestParams", "script_id"),
"script_enabled": event.deep_get("requestParams", "enabled"),
"script_sha256": event.deep_get("requestParams", "script-SHA256"),
},
)
Rule specification
AnalysisType: rule
Filename: databricks_global_init_script_changes.py
RuleID: "Databricks.Audit.GlobalInitScriptChanges"
DisplayName: "Databricks Global Init Script Changes"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Persistence
- Execution
Reports:
MITRE ATT&CK:
- TA0003:T1037 # Boot or Logon Initialization Scripts
- TA0002:T1059 # Command and Scripting Interpreter
Severity: Info
Description: >
Detects modifications to global initialization scripts which run on all clusters at startup.
These scripts can be used for persistence or to execute malicious code across the environment.
All script creations, updates, and deletions are monitored.
Runbook: |
1. Query audit logs for all global init script changes by this actor in the past 30 days
2. Check if new clusters were created shortly after the script modification in the 6 hours after this change
3. Find all script modifications across all workspaces in the past 7 days to identify coordinated changes
Reference: https://github.com/andyweaves/system-tables-audit-logs/blob/main/resources/queries_and_alerts.json
SummaryAttributes:
- actor
- script_name
- action
Stages and Predicates
Fires on Databricks.Audit events when the condition below holds.
Condition
serviceNameisglobalInitScripts
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
serviceName | eq |
| field:"serviceName" kind:eq value:"globalInitScripts" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
actionName | |
name | requestParams.name |
email | userIdentity.email |
Response runbook
1. Query audit logs for all global init script changes by this actor in the past 30 days
2. Check if new clusters were created shortly after the script modification in the 6 hours after this change
3. Find all script modifications across all workspaces in the past 7 days to identify coordinated changes
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "create",
"requestParams": {
"enabled": "true",
"name": "security-monitoring",
"script-SHA256": "abc123def456",
"script_id": "script-123"
},
"response": {
"statusCode": 200
},
"serviceName": "globalInitScripts",
"sourceIPAddress": "198.51.100.1",
"timestamp": 1704067200000,
"userIdentity": {
"email": "admin@example.com"
},
"workspaceId": "1234567890123456"
}
Databricks Group Created
#Detects creation of user groups in Databricks. Group creation may be part of normal administration or could indicate privilege escalation preparation by creating a group that will later receive elevated permissions.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Detection logic
from panther_databricks_helpers import databricks_alert_context, extract_group_identifier
def rule(event):
if event.get("serviceName") != "accounts":
return False
return event.get("actionName") == "createGroup"
def title(event):
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
group = extract_group_identifier(event) or event.deep_get(
"requestParams", "groupName", default="Unknown Group"
)
return f"Group created: {group} by {actor}"
def dedup(event):
group = extract_group_identifier(event) or "unknown"
return f"group_created_{group}"
def alert_context(event):
return databricks_alert_context(
event,
additional_fields={
"group_name": extract_group_identifier(event),
},
)
Rule specification
AnalysisType: rule
Filename: databricks_group_created.py
RuleID: "Databricks.Audit.GroupCreated"
DisplayName: "Databricks Group Created"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Persistence
Reports:
MITRE ATT&CK:
- TA0003:T1136 # Create Account
Severity: Info
Description: >
Detects creation of user groups in Databricks. Group creation may be part of normal
administration or could indicate privilege escalation preparation by creating a group
that will later receive elevated permissions.
Runbook: |
1. Verify the group creation was part of an approved workflow
2. Check if the group was subsequently granted admin or elevated permissions
3. Review group membership additions following creation
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/tree/main/base/detections/behavioral
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
serviceNameisaccountsactionNameiscreateGroup
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
actionName | eq |
| field:"actionName" kind:eq value:"createGroup" |
serviceName | eq |
| field:"serviceName" kind:eq value:"accounts" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
email | userIdentity.email |
Response runbook
1. Verify the group creation was part of an approved workflow
2. Check if the group was subsequently granted admin or elevated permissions
3. Review group membership additions following creation
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "createGroup",
"requestParams": {
"targetGroupName": "data-engineers"
},
"response": {
"statusCode": 200
},
"serviceName": "accounts",
"userIdentity": {
"email": "admin@example.com"
}
}
Databricks Group Deleted
#Detects group deletions in Databricks accounts. While often part of normal cleanup processes, unauthorized group deletions could indicate access control dismantling. Successful deletions are elevated to HIGH severity.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Impact |
Detection logic
from panther_databricks_helpers import (
databricks_alert_context,
extract_group_identifier,
should_alert_on_group_change,
)
def rule(event):
return should_alert_on_group_change(event, change_type="delete")
def severity(event):
status_code = event.deep_get("response", "statusCode")
return "HIGH" if status_code == 200 else "LOW"
def title(event):
group_id = extract_group_identifier(event) or "Unknown Group"
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
status_code = event.deep_get("response", "statusCode")
status = "Success" if status_code == 200 else "Failed"
return f"Group deletion {status}: {group_id} by {actor}"
def dedup(event):
group_id = extract_group_identifier(event) or "unknown"
return f"group_deleted_{group_id}"
def alert_context(event):
group_id = extract_group_identifier(event)
return databricks_alert_context(event, additional_fields={"group_id": group_id})
Rule specification
AnalysisType: rule
Filename: databricks_group_deleted.py
RuleID: "Databricks.Audit.GroupDeleted"
DisplayName: "Databricks Group Deleted"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Impact
Reports:
MITRE ATT&CK:
- TA0040:T1531 # Account Access Removal
Severity: Low
Description: >
Detects group deletions in Databricks accounts. While often part of normal cleanup processes,
unauthorized group deletions could indicate access control dismantling. Successful deletions
are elevated to HIGH severity.
Runbook: |
1. Query audit logs for all members who were in this group in the 24 hours before deletion
2. Check if this group had admin privileges or access to sensitive resources based on past 30 days of activity
3. Find all group deletions by this actor in the past 30 days to identify bulk deletion patterns
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/blob/main/base/detections/event-based/group_deleted.py
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
userAgentdoes not containDatabricks-Service/driveruserAgentdoes not containDatabricks-RuntimeuserAgentdoes not containDelta-Sharing-SparkStructuredStreaminguserAgentdoes not containRawDBHttpClientuserAgentdoes not containmlflow-pythonuserAgentdoes not containobsSDK-scalauserAgentdoes not containwsfsuserAgentdoes not containfeature-storerequestParams.pathdoes not contain/telemetryrequestParams.pathdoes not contain/delta-commitrequestParams.pathdoes not contain/healthrequestParams.pathdoes not contain/metricsrequestParams.pathdoes not contain/statusserviceNameisaccounts
This rule also runs imperative logic the parser cannot express as a filter. The conditions above are the structured part it could extract.
Exclusions
The rule actively suppresses these predicates.
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
serviceName | eq |
| field:"serviceName" kind:eq value:"accounts" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
email | userIdentity.email |
Response runbook
1. Query audit logs for all members who were in this group in the 24 hours before deletion
2. Check if this group had admin privileges or access to sensitive resources based on past 30 days of activity
3. Find all group deletions by this actor in the past 30 days to identify bulk deletion patterns
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "removeGroup",
"requestParams": {
"targetGroupId": "group-123"
},
"response": {
"statusCode": 200
},
"serviceName": "accounts",
"sourceIPAddress": "198.51.100.1",
"timestamp": 1234567890000,
"userIdentity": {
"email": "admin@example.com"
}
}
Databricks High Priority Configuration Changes
#Detects high-priority security configuration changes including audit logging modifications, IP access list changes, and security-critical workspace settings. Severity is elevated for successful changes to high-risk settings.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Defense Impairment |
Detection logic
from panther_databricks_helpers import (
databricks_alert_context,
get_config_key_value,
is_critical_config_change,
)
def rule(event):
if not is_critical_config_change(event):
return False
# Verbose audit logging disabled is handled by a dedicated rule
config_key, config_value = get_config_key_value(event)
if config_key == "enableVerboseAuditLogs" and config_value == "false":
return False
return True
def severity(event):
status_code = event.deep_get("response", "statusCode")
success = status_code == 200
action = event.get("actionName", "Unknown Action")
# Determine severity from action type
# Success Failure
# IP access list deleted HIGH MEDIUM
# Other critical configs MEDIUM LOW
if action == "deleteIpAccessList":
return "HIGH" if success else "MEDIUM"
return "MEDIUM" if success else "LOW"
def title(event):
action = event.get("actionName", "Unknown Action")
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
status_code = event.deep_get("response", "statusCode")
status = "Successfully" if status_code == 200 else "Attempted to"
# IP access list changes
if "IpAccessList" in action:
return f"{status} {action} by {actor}"
# Workspace configuration edits
config_key, config_value = get_config_key_value(event)
if config_key:
return f"{status} modify {config_key} to {config_value} by {actor}"
return f"Critical configuration change by {actor}"
def dedup(event):
config_key, _ = get_config_key_value(event)
# IP access list events don't have workspaceConfKeys, so fall back to actionName
key = config_key or event.get("actionName", "unknown")
return f"critical_config_change_{key}"
def alert_context(event):
config_key, config_value = get_config_key_value(event)
return databricks_alert_context(
event,
additional_fields={
"config_key": config_key,
"config_value": config_value,
"change_category": (
"IP Access List"
if "IpAccessList" in event.get("actionName", "")
else "Workspace Configuration"
),
},
)
Rule specification
AnalysisType: rule
Filename: databricks_config_changes_high_priority.py
RuleID: "Databricks.Audit.ConfigChangesHighPriority"
DisplayName: "Databricks High Priority Configuration Changes"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Defense Evasion
- Persistence
Reports:
MITRE ATT&CK:
- TA0005:T1562.008 # Impair Defenses: Disable Cloud Logs
- TA0003:T1098 # Account Manipulation
Severity: Medium
Description: >
Detects high-priority security configuration changes including audit logging modifications,
IP access list changes, and security-critical workspace settings. Severity is elevated for
successful changes to high-risk settings.
Runbook: |
1. Query audit logs for all configuration changes by the actor in the 24 hours before and after this critical change
2. Check if this configuration change has been performed by this actor in the past 90 days to establish legitimacy
3. Find all other high-risk configuration changes (IP access lists, audit settings) across all workspaces in the past 7 days
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/blob/main/base/detections/event-based/configuration_changes_high_priority.py
SummaryAttributes:
- actor
- config_key
- change_category
Stages and Predicates
Fires on Databricks.Audit events when the condition below holds.
Condition
actionNameis one ofcreateIpAccessList,updateIpAccessList,deleteIpAccessList
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 |
|---|---|---|---|
actionName | in |
| field:"actionName" kind:in |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
actionName | |
email | userIdentity.email |
Response runbook
1. Query audit logs for all configuration changes by the actor in the 24 hours before and after this critical change
2. Check if this configuration change has been performed by this actor in the past 90 days to establish legitimacy
3. Find all other high-risk configuration changes (IP access lists, audit settings) across all workspaces in the past 7 days
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "workspaceConfEdit",
"requestParams": {
"workspaceConfKeys": "enableVerboseAuditLogs",
"workspaceConfValues": "true"
},
"response": {
"statusCode": 200
},
"serviceName": "workspace",
"sourceIPAddress": "198.51.100.1",
"timestamp": 1234567890000,
"userIdentity": {
"email": "admin@example.com"
}
}
Databricks Install Library on All Clusters
#Detects use of the deprecated installLibraryOnAllClusters action. This anti-pattern can introduce security risks by installing potentially malicious libraries across the entire environment without proper review or controls.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Execution | |
| Persistence |
Detection logic
from panther_databricks_helpers import databricks_alert_context
def rule(event):
return event.get("actionName") == "installLibraryOnAllClusters"
def title(event):
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
workspace = event.get("workspaceId", "Unknown Workspace")
return f"Library installed on all clusters in workspace {workspace} by {actor}"
def alert_context(event):
return databricks_alert_context(
event, additional_fields={"library_config": event.get("requestParams")}
)
Rule specification
AnalysisType: rule
Filename: databricks_install_library_all_clusters.py
RuleID: "Databricks.Audit.InstallLibraryAllClusters"
DisplayName: "Databricks Install Library on All Clusters"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Execution
- Persistence
Reports:
MITRE ATT&CK:
- TA0002:T1203 # Exploitation for Client Execution
- TA0003:T1543 # Create or Modify System Process
Severity: Medium
Description: >
Detects use of the deprecated installLibraryOnAllClusters action. This anti-pattern can
introduce security risks by installing potentially malicious libraries across the entire
environment without proper review or controls.
Runbook: |
1. Query audit logs for the library installation details and identify what library was installed
2. Check if this library has been used in notebook or job execution in the 24 hours after installation
3. Find all library installations by this user in the past 30 days to identify patterns
Reference: https://github.com/andyweaves/system-tables-audit-logs/blob/main/resources/queries_and_alerts.json
SummaryAttributes:
- actor
- workspace_id
Stages and Predicates
Fires on Databricks.Audit events when the condition below holds.
Condition
actionNameisinstallLibraryOnAllClusters
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
actionName | eq |
| field:"actionName" kind:eq value:"installLibraryOnAllClusters" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
workspaceId | |
email | userIdentity.email |
Response runbook
1. Query audit logs for the library installation details and identify what library was installed
2. Check if this library has been used in notebook or job execution in the 24 hours after installation
3. Find all library installations by this user in the past 30 days to identify patterns
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "installLibraryOnAllClusters",
"requestParams": {
"library": {
"pypi": {
"package": "suspicious-package"
}
}
},
"response": {
"statusCode": 200
},
"serviceName": "clusters",
"sourceIPAddress": "198.51.100.1",
"timestamp": 1704067200000,
"userIdentity": {
"email": "developer@example.com"
},
"workspaceId": "1234567890123456"
}
Databricks Long-Lifetime Token Generated
#Detects generation of personal access tokens (PATs) with lifetime exceeding 72 hours. Long-lived tokens increase the risk of credential theft and unauthorized access if compromised. Tokens with lifetime >90 days are elevated to MEDIUM, >1 year to HIGH severity.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Lateral Movement |
Detection logic
from panther_databricks_helpers import databricks_alert_context
def _token_duration_hours(event):
"""Calculate token lifetime in hours, or None if not determinable."""
try:
token_expiration = int(event.deep_get("requestParams", "tokenExpirationTime", default=0))
event_time_ms = event.get("timestamp", 0)
if not token_expiration or not event_time_ms:
return None
return (token_expiration - event_time_ms) / (1000 * 3600)
except (ValueError, TypeError):
return None
def rule(event):
if event.get("actionName") != "generateDbToken":
return False
duration = _token_duration_hours(event)
return duration is not None and duration > 72
def severity(event):
duration = _token_duration_hours(event)
if duration is None:
return "LOW"
# Severity
# >1 year (8760h) HIGH
# >90 days (2160h) MEDIUM
# otherwise LOW
if duration > 8760:
return "HIGH"
if duration > 2160:
return "MEDIUM"
return "LOW"
def title(event):
actor = event.deep_get("userIdentity", "email", default="Unknown User")
duration = _token_duration_hours(event)
if duration is not None:
return f"Long-lifetime token ({int(duration / 24)} days) generated by {actor}"
return f"Long-lifetime token generated by {actor}"
def alert_context(event):
duration = _token_duration_hours(event)
duration_days = duration / 24 if duration is not None else None
return databricks_alert_context(
event,
additional_fields={
"token_expiration_time": event.deep_get("requestParams", "tokenExpirationTime"),
"token_hash": event.deep_get("requestParams", "tokenHash"),
"token_duration_hours": duration,
"token_duration_days": duration_days,
},
)
Rule specification
AnalysisType: rule
Filename: databricks_long_lifetime_token_generated.py
RuleID: "Databricks.Audit.LongLifetimeTokenGenerated"
DisplayName: "Databricks Long-Lifetime Token Generated"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Credential Access
- Persistence
Reports:
MITRE ATT&CK:
- TA0006:T1550 # Use Alternate Authentication Material
- TA0003:T1098 # Account Manipulation
Severity: Low
Description: >
Detects generation of personal access tokens (PATs) with lifetime exceeding 72 hours.
Long-lived tokens increase the risk of credential theft and unauthorized access if compromised.
Tokens with lifetime >90 days are elevated to MEDIUM, >1 year to HIGH severity.
Runbook: |
1. Query audit logs for all token generation by this user in the past 30 days to identify patterns
2. Check if the generated token has been used for API calls in the 24 hours after creation
3. Find all other long-lifetime tokens (>72 hours) created in the past 90 days to establish baseline
Reference: https://github.com/andyweaves/system-tables-audit-logs/blob/main/resources/queries_and_alerts.json
SummaryAttributes:
- actor
- token_duration_days
Stages and Predicates
Fires on Databricks.Audit events when the condition below holds.
Condition
actionNameisgenerateDbToken
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 |
|---|---|---|---|
actionName | eq |
| field:"actionName" kind:eq value:"generateDbToken" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
email | userIdentity.email |
Response runbook
1. Query audit logs for all token generation by this user in the past 30 days to identify patterns
2. Check if the generated token has been used for API calls in the 24 hours after creation
3. Find all other long-lifetime tokens (>72 hours) created in the past 90 days to establish baseline
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "generateDbToken",
"requestParams": {
"tokenExpirationTime": "1704672000000",
"tokenHash": "abc123def456"
},
"response": {
"statusCode": 200
},
"serviceName": "accounts",
"sourceIPAddress": "198.51.100.1",
"timestamp": 1704067200000,
"userIdentity": {
"email": "user@example.com"
},
"workspaceId": "1234567890123456"
}
Databricks Metastore Admin Privilege Granted
#Detects when metastore admin privileges are granted in Databricks through direct metastore ownership changes or addition to metastore admin groups. Metastore admins have extensive control over data access and governance policies in Unity Catalog.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation |
Detection logic
from panther_databricks_helpers import databricks_alert_context, is_metastore_admin_action
def rule(event):
# Use helper to check for metastore admin actions
if not is_metastore_admin_action(event):
return False
action = event.get("actionName", "")
# For group membership changes, only alert on additions (not removals)
# and ensure it's at the account level
if action != "updateMetastore":
# Exclude removals
if action == "removePrincipalFromGroup":
return False
# Must be account-level event
if event.get("serviceName") != "accounts":
return False
return True
def title(event):
action = event.get("actionName", "Unknown Action")
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
if action == "updateMetastore":
new_owner = event.deep_get("requestParams", "owner", default="Unknown Owner")
return f"Metastore ownership changed to {new_owner} by {actor}"
target_group = event.deep_get("requestParams", "targetGroupName", default="Unknown Group")
principal = event.deep_get("requestParams", "principal") or event.deep_get(
"requestParams", "targetUserName", default="Unknown Principal"
)
return f"Principal {principal} added to metastore admin group {target_group} by {actor}"
def dedup(event):
action = event.get("actionName", "unknown")
if action == "updateMetastore":
metastore_id = event.deep_get("requestParams", "metastoreId", default="unknown")
return f"metastore_admin_{metastore_id}"
principal = event.deep_get("requestParams", "principal") or event.deep_get(
"requestParams", "targetUserName", default="unknown"
)
return f"metastore_admin_group_{principal}"
def alert_context(event):
return databricks_alert_context(
event,
additional_fields={
"new_owner": event.deep_get("requestParams", "owner"),
"target_group": event.deep_get("requestParams", "targetGroupName"),
"principal": event.deep_get("requestParams", "principal")
or event.deep_get("requestParams", "targetUserName"),
},
)
Rule specification
AnalysisType: rule
Filename: databricks_metastore_admin_privilege_granted.py
RuleID: "Databricks.Audit.MetastoreAdminPrivilegeGranted"
DisplayName: "Databricks Metastore Admin Privilege Granted"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Privilege Escalation
- Unity Catalog
Reports:
MITRE ATT&CK:
- TA0004:T1098 # Account Manipulation
Severity: Medium
Description: >
Detects when metastore admin privileges are granted in Databricks through direct metastore
ownership changes or addition to metastore admin groups. Metastore admins have extensive
control over data access and governance policies in Unity Catalog.
Runbook: |
1. Query Unity Catalog audit logs for all metastore operations by the target principal in the 24 hours after this privilege grant
2. Check if the target principal accessed sensitive catalogs or tables in the 6 hours after receiving admin rights
3. Find all metastore admin grants in the past 90 days to identify unusual patterns
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/blob/main/base/detections/event-based/metastore_admin_privilege_granted.py
Stages and Predicates
Fires on Databricks.Audit events when any of the conditions below holds.
Condition
any of:
all of:
actionNameisupdateMetastorerequestParamscontainsowner
actionNameis notupdateMetastore
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 |
|---|---|---|---|
actionName | eq |
| field:"actionName" kind:eq value:"updateMetastore" |
actionName | ne |
| field:"actionName" kind:ne value:"updateMetastore" |
requestParams | contains |
| field:"requestParams" kind:contains value:"owner" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
owner | requestParams.owner |
email | userIdentity.email |
principal | requestParams.principal |
targetGroupName | requestParams.targetGroupName |
Response runbook
1. Query Unity Catalog audit logs for all metastore operations by the target principal in the 24 hours after this privilege grant
2. Check if the target principal accessed sensitive catalogs or tables in the 6 hours after receiving admin rights
3. Find all metastore admin grants in the past 90 days to identify unusual patterns
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "updateMetastore",
"requestParams": {
"metastoreId": "metastore-123",
"owner": "newadmin@example.com"
},
"response": {
"statusCode": 200
},
"serviceName": "unityCatalog",
"sourceIPAddress": "198.51.100.1",
"timestamp": 1234567890000,
"userIdentity": {
"email": "admin@example.com"
}
}
Databricks MFA Key Change
#Detects addition or deletion of MFA keys on Databricks accounts. MFA key deletion may indicate an attacker weakening account security, while unexpected additions may indicate enrollment of attacker-controlled authenticators.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Detection logic
from panther_databricks_helpers import MFA_ACTIONS, databricks_alert_context
def rule(event):
if event.get("serviceName") != "accounts":
return False
action = event.get("actionName")
return action in MFA_ACTIONS["add"] or action in MFA_ACTIONS["delete"]
def title(event):
action = event.get("actionName", "unknown")
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
change_type = "added" if action in MFA_ACTIONS["add"] else "deleted"
return f"MFA key {change_type} by {actor}"
def dedup(event):
actor = event.deep_get("userIdentity", "email", default="unknown")
action = event.get("actionName", "unknown")
return f"mfa_key_change_{actor}_{action}"
def alert_context(event):
return databricks_alert_context(
event,
additional_fields={
"change_type": "add" if event.get("actionName") in MFA_ACTIONS["add"] else "delete",
},
)
Rule specification
AnalysisType: rule
Filename: databricks_mfa_key_change.py
RuleID: "Databricks.Audit.MFAKeyChange"
DisplayName: "Databricks MFA Key Change"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Credential Access
Reports:
MITRE ATT&CK:
- TA0006:T1556 # Modify Authentication Process
Severity: Info
Description: >
Detects addition or deletion of MFA keys on Databricks accounts. MFA key deletion
may indicate an attacker weakening account security, while unexpected additions may
indicate enrollment of attacker-controlled authenticators.
Runbook: |
1. Verify the actor intended to modify their MFA settings
2. For deletions, check if a replacement key was added
3. For additions, verify the new key was enrolled by the legitimate user
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/tree/main/base/detections/behavioral
Stages and Predicates
Fires on Databricks.Audit events when the condition below holds.
Condition
serviceNameisaccounts
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 |
|---|---|---|---|
serviceName | eq |
| field:"serviceName" kind:eq value:"accounts" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
email | userIdentity.email |
Response runbook
1. Verify the actor intended to modify their MFA settings
2. For deletions, check if a replacement key was added
3. For additions, verify the new key was enrolled by the legitimate user
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "mfaAddKey",
"response": {
"statusCode": 200
},
"serviceName": "accounts",
"userIdentity": {
"email": "user@example.com"
}
}
Databricks Mount Point Creation
#Detects creation of legacy mount points in Databricks. Mount points are deprecated in favor of Unity Catalog external locations and can pose security risks by bypassing access controls. This anti-pattern should be avoided in modern Databricks deployments.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Lateral Movement | |
| Collection |
Detection logic
from panther_databricks_helpers import databricks_alert_context
def rule(event):
return event.get("actionName") == "mount"
def title(event):
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
mount_point = event.deep_get("requestParams", "mountPoint", default="Unknown Mount")
workspace = event.get("workspaceId", "Unknown Workspace")
return f"Mount point created: {mount_point} in workspace {workspace} by {actor}"
def dedup(event):
mount_point = event.deep_get("requestParams", "mountPoint", default="unknown")
workspace = event.get("workspaceId", "unknown")
return f"mount_point_{workspace}_{mount_point}"
def alert_context(event):
return databricks_alert_context(
event,
additional_fields={
"mount_point": event.deep_get("requestParams", "mountPoint"),
"mount_config": event.get("requestParams"),
},
)
Rule specification
AnalysisType: rule
Filename: databricks_mount_point_creation.py
RuleID: "Databricks.Audit.MountPointCreation"
DisplayName: "Databricks Mount Point Creation"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Collection
- Lateral Movement
Reports:
MITRE ATT&CK:
- TA0009:T1074 # Data Staged
- TA0008:T1021 # Remote Services
Severity: Info
Description: >
Detects creation of legacy mount points in Databricks. Mount points are deprecated in favor
of Unity Catalog external locations and can pose security risks by bypassing access controls.
This anti-pattern should be avoided in modern Databricks deployments.
Runbook: |
1. Query audit logs for all mount operations by this user in the past 30 days to identify patterns
2. Check if data was accessed through this mount point in the 24 hours after creation
3. Find all mount point creations across workspaces in the past 90 days to establish baseline
Reference: https://github.com/andyweaves/system-tables-audit-logs/blob/main/resources/queries_and_alerts.json
SummaryAttributes:
- actor
- mount_point
- workspace_id
Stages and Predicates
Fires on Databricks.Audit events when the condition below holds.
Condition
actionNameismount
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
actionName | eq |
| field:"actionName" kind:eq value:"mount" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
mountPoint | requestParams.mountPoint |
workspaceId | |
email | userIdentity.email |
Response runbook
1. Query audit logs for all mount operations by this user in the past 30 days to identify patterns
2. Check if data was accessed through this mount point in the 24 hours after creation
3. Find all mount point creations across workspaces in the past 90 days to establish baseline
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "mount",
"requestParams": {
"mountPoint": "/mnt/data-lake",
"source": "s3://my-bucket/data"
},
"response": {
"statusCode": 200
},
"serviceName": "dbfs",
"sourceIPAddress": "198.51.100.1",
"timestamp": 1704067200000,
"userIdentity": {
"email": "user@example.com"
},
"workspaceId": "1234567890123456"
}
Databricks Non-SSO Login Detected
#Detects successful logins that bypass SSO (SAML). In organizations that enforce SSO, non-SAML logins may indicate credential compromise, misconfigured service accounts, or unauthorized access methods.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Detection logic
from panther_databricks_helpers import databricks_alert_context, is_login_action
def rule(event):
if not is_login_action(event):
return False
if event.deep_get("response", "statusCode") != 200:
return False
# Alert on logins that bypass SSO (SAML).
# If authentication_method is missing, fall back to checking the action name.
auth_method = event.deep_get("requestParams", "authentication_method", default="")
if auth_method:
return auth_method != "BROWSER_BYO_IDP_SAML"
# No auth_method field: treat non-SAML login actions as non-SSO
return event.get("actionName") not in ("samlLogin",)
def title(event):
user = event.deep_get("userIdentity", "email", default="Unknown User")
auth_method = event.deep_get("requestParams", "authentication_method", default="Unknown")
action = event.get("actionName", "login")
return f"Non-SSO login by {user} via {auth_method} ({action})"
def dedup(event):
user = event.deep_get("userIdentity", "email", default="unknown")
return f"non_sso_login_{user}"
def alert_context(event):
return databricks_alert_context(
event,
additional_fields={
"authentication_method": event.deep_get("requestParams", "authentication_method"),
},
)
Rule specification
AnalysisType: rule
Filename: databricks_non_sso_login.py
RuleID: "Databricks.Audit.NonSSOLogin"
DisplayName: "Databricks Non-SSO Login Detected"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Initial Access
Reports:
MITRE ATT&CK:
- TA0001:T1078 # Valid Accounts
Severity: Info
Description: >
Detects successful logins that bypass SSO (SAML). In organizations that enforce SSO,
non-SAML logins may indicate credential compromise, misconfigured service accounts,
or unauthorized access methods.
Runbook: |
1. Verify whether the user is expected to use non-SSO authentication
2. Check if this is a service account or automation that legitimately bypasses SSO
3. Review the authentication_method in alert context
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/tree/main/base/detections/behavioral
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
actionNameis one ofaadBrowserLogin,aadTokenLogin,certLogin,jwtLogin,loginresponse.statusCodeis200any of:
all of:
requestParams.authentication_methodis presentrequestParams.authentication_methodis notBROWSER_BYO_IDP_SAML
all of:
requestParams.authentication_methodis emptyactionNameis not one ofsamlLogin
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
actionName | in |
| field:"actionName" kind:in |
requestParams.authentication_method | is_not_null | field:"requestParams.authentication_method" kind:is_not_null | |
requestParams.authentication_method | is_null | field:"requestParams.authentication_method" kind:is_null | |
requestParams.authentication_method | ne |
| field:"requestParams.authentication_method" kind:ne value:"BROWSER_BYO_IDP_SAML" |
response.statusCode | eq |
| field:"response.statusCode" kind:eq value:"200" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
email | userIdentity.email |
authentication_method | requestParams.authentication_method |
actionName |
Response runbook
1. Verify whether the user is expected to use non-SSO authentication
2. Check if this is a service account or automation that legitimately bypasses SSO
3. Review the authentication_method in alert context
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "tokenLogin",
"requestParams": {
"authentication_method": "TOKEN"
},
"response": {
"statusCode": 200
},
"serviceName": "accounts",
"userIdentity": {
"email": "user@example.com"
}
}
Databricks Potential Privilege Escalation
#Detects potential privilege escalation through high volume permission modifications (≥25 per hour) by the same user. Monitors various permission-related actions across account, workspace, and Unity Catalog.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation |
Detection logic
from panther_databricks_helpers import PRIVILEGE_MODIFICATION_ACTIONS, databricks_alert_context
def rule(event):
return event.get("actionName") in PRIVILEGE_MODIFICATION_ACTIONS
def dedup(event):
user = event.deep_get("userIdentity", "email", default="unknown")
return f"priv_escalation_{user}"
def title(event):
user = event.deep_get("userIdentity", "email", default="Unknown User")
return f"Potential privilege escalation by {user} (>25 permission changes/hour)"
def alert_context(event):
return databricks_alert_context(event)
Rule specification
AnalysisType: rule
Filename: databricks_potential_privilege_escalation.py
RuleID: "Databricks.Audit.PotentialPrivilegeEscalation"
DisplayName: "Databricks Potential Privilege Escalation"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Privilege Escalation
Reports:
MITRE ATT&CK:
- TA0004:T1078 # Valid Accounts
Severity: High
Threshold: 25
DedupPeriodMinutes: 60
Description: >
Detects potential privilege escalation through high volume permission modifications (≥25 per hour) by the same user.
Monitors various permission-related actions across account, workspace, and Unity Catalog.
Runbook: |
1. Query audit logs for all permission modifications by this user in the past 24 hours
2. Check if the user performed high-privilege actions immediately after the permission changes
3. Find all users with high permission modification rates in the past 7 days
Reference: https://github.com/andyweaves/system-tables-audit-logs/blob/main/resources/queries_and_alerts.json
SummaryAttributes:
- actor
Stages and Predicates
Fires on Databricks.Audit events when the condition below holds.
Condition
actionNameis one ofaddPrincipalToGroup,removePrincipalFromGroup,addPrincipalsToGroup,setAdmin,removeAdmin
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
actionName | in |
| field:"actionName" kind:in |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
email | userIdentity.email |
Response runbook
1. Query audit logs for all permission modifications by this user in the past 24 hours
2. Check if the user performed high-privilege actions immediately after the permission changes
3. Find all users with high permission modification rates in the past 7 days
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "addPrincipalToGroup",
"requestParams": {
"targetGroupName": "developers"
},
"serviceName": "accounts",
"timestamp": 1704067200000,
"userIdentity": {
"email": "admin@example.com"
}
}
Databricks Principal Removed From Group
#Detects when principals (users or service principals) are removed from groups in Databricks accounts. This is often legitimate administrative activity but should be monitored for unauthorized membership changes.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Detection logic
from panther_databricks_helpers import databricks_alert_context
def rule(event):
if event.get("serviceName") != "accounts":
return False
return event.get("actionName") == "removePrincipalFromGroup"
def title(event):
target_user = event.deep_get("requestParams", "targetUserName", default="Unknown User")
target_group = event.deep_get("requestParams", "targetGroupName", default="Unknown Group")
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
return f"Principal {target_user} removed from group {target_group} by {actor}"
def dedup(event):
target_user = event.deep_get("requestParams", "targetUserName", default="unknown")
target_group = event.deep_get("requestParams", "targetGroupName", default="unknown")
return f"principal_removed_{target_group}_{target_user}"
def alert_context(event):
return databricks_alert_context(
event,
additional_fields={
"target_user": event.deep_get("requestParams", "targetUserName"),
"target_group": event.deep_get("requestParams", "targetGroupName"),
},
)
Rule specification
AnalysisType: rule
Filename: databricks_principal_removed_from_group.py
RuleID: "Databricks.Audit.PrincipalRemovedFromGroup"
DisplayName: "Databricks Principal Removed From Group"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Persistence
Reports:
MITRE ATT&CK:
- TA0003:T1098 # Account Manipulation
Severity: Info
Description: >
Detects when principals (users or service principals) are removed from groups in Databricks accounts.
This is often legitimate administrative activity but should be monitored for unauthorized membership changes.
Runbook: |
1. Query audit logs for all group membership changes by the actor in the 24 hours around this event
2. Check if the removed principal had active sessions or API calls in the 1 hour before removal
3. Find all group membership removals for this group in the past 30 days to identify patterns
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/blob/main/base/detections/event-based/principal_removed_from_group.py
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
serviceNameisaccountsactionNameisremovePrincipalFromGroup
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
actionName | eq |
| field:"actionName" kind:eq value:"removePrincipalFromGroup" |
serviceName | eq |
| field:"serviceName" kind:eq value:"accounts" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
targetUserName | requestParams.targetUserName |
targetGroupName | requestParams.targetGroupName |
email | userIdentity.email |
Response runbook
1. Query audit logs for all group membership changes by the actor in the 24 hours around this event
2. Check if the removed principal had active sessions or API calls in the 1 hour before removal
3. Find all group membership removals for this group in the past 30 days to identify patterns
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "removePrincipalFromGroup",
"requestParams": {
"targetGroupName": "developers",
"targetUserName": "user@example.com"
},
"response": {
"statusCode": 200
},
"serviceName": "accounts",
"sourceIPAddress": "198.51.100.1",
"timestamp": 1234567890000,
"userIdentity": {
"email": "admin@example.com"
}
}
Databricks Repeated Access to Secrets
#Detects repeated secret access (≥10 times in 60 minutes) which may indicate credential harvesting or unauthorized secret enumeration.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Detection logic
from panther_databricks_helpers import SYSTEM_USERS, databricks_alert_context
def rule(event):
if event.get("actionName") != "getSecret":
return False
# Filter out system users
user = event.deep_get("userIdentity", "email", default="")
return user not in SYSTEM_USERS
def dedup(event):
user = event.deep_get("userIdentity", "email", default="unknown")
return f"secret_access_{user}"
def title(event):
user = event.deep_get("userIdentity", "email", default="Unknown User")
scope = event.deep_get("requestParams", "scope", default="Unknown Scope")
return f"Repeated secret access by {user} in scope {scope}"
def alert_context(event):
return databricks_alert_context(
event,
additional_fields={
"secret_scope": event.deep_get("requestParams", "scope"),
"secret_key": event.deep_get("requestParams", "key"),
},
)
Rule specification
AnalysisType: rule
Filename: databricks_repeated_access_to_secrets.py
RuleID: "Databricks.Audit.RepeatedAccessToSecrets"
DisplayName: "Databricks Repeated Access to Secrets"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Credential Access
- Collection
Reports:
MITRE ATT&CK:
- TA0006:T1555 # Credentials from Password Stores
Severity: Medium
Threshold: 10
DedupPeriodMinutes: 60
Description: >
Detects repeated secret access (≥10 times in 60 minutes) which may indicate credential
harvesting or unauthorized secret enumeration.
Runbook: |
1. Query audit logs for all secret access by this user in the past 24 hours to identify patterns
2. Check if the accessed secrets were used in API calls or notebook execution in the 6 hours after access
3. Find all users with high secret access rates in the past 7 days to establish baseline
Reference: https://github.com/andyweaves/system-tables-audit-logs/blob/main/resources/queries_and_alerts.json
SummaryAttributes:
- actor
- secret_scope
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
actionNameisgetSecretuserIdentity.emailis not one ofSystem-User
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
userIdentity.email | eq | System-User | excludes:userIdentity.email field:"userIdentity.email" value:"System-User" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
actionName | eq |
| field:"actionName" kind:eq value:"getSecret" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
email | userIdentity.email |
scope | requestParams.scope |
Response runbook
1. Query audit logs for all secret access by this user in the past 24 hours to identify patterns
2. Check if the accessed secrets were used in API calls or notebook execution in the 6 hours after access
3. Find all users with high secret access rates in the past 7 days to establish baseline
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "getSecret",
"requestParams": {
"key": "api-token",
"scope": "production-keys"
},
"response": {
"statusCode": 200
},
"serviceName": "secrets",
"timestamp": 1704067200000,
"userIdentity": {
"email": "user@example.com"
}
}
Databricks Repeated Failed Login Attempts
#Detects repeated failed login attempts within a 60-minute window, which may indicate credential stuffing, brute force attacks, or compromised credentials.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Credential Access |
Detection logic
from panther_databricks_helpers import databricks_alert_context, is_login_action
def rule(event):
if not is_login_action(event):
return False
# Check for failure status codes
status_code = event.deep_get("response", "statusCode")
return status_code in [401, 403]
def title(event):
user = event.deep_get("userIdentity", "email") or event.deep_get(
"requestParams", "user", default="Unknown User"
)
source_ip = event.get("sourceIPAddress", "Unknown IP")
action = event.get("actionName", "login")
return f"Repeated failed login attempts: {user} from {source_ip} ({action})"
def dedup(event):
user = (
event.deep_get("userIdentity", "email")
or event.deep_get("requestParams", "user")
or "unknown"
)
return f"failed_login_{user}"
def alert_context(event):
return databricks_alert_context(
event,
additional_fields={
"login_user": event.deep_get("userIdentity", "email")
or event.deep_get("requestParams", "user"),
"login_action": event.get("actionName"),
"error_message": event.deep_get("response", "errorMessage"),
},
)
Rule specification
AnalysisType: rule
Filename: databricks_repeated_failed_login_attempts.py
RuleID: "Databricks.Audit.RepeatedFailedLoginAttempts"
DisplayName: "Databricks Repeated Failed Login Attempts"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Credential Access
- Initial Access
Reports:
MITRE ATT&CK:
- TA0006:T1110 # Brute Force
- TA0001:T1078 # Valid Accounts
Severity: Medium
Threshold: 5
DedupPeriodMinutes: 60
Description: >
Detects repeated failed login attempts within a 60-minute window, which may indicate
credential stuffing, brute force attacks, or compromised credentials.
Runbook: |
1. Count all failed and successful login attempts for this user in the 6 hours around this alert
2. Check if the source IPs match known VPNs, proxies, or are from unexpected geographic locations
3. Find if there were successful logins from different IPs immediately after the failed attempts
Reference: https://github.com/andyweaves/system-tables-audit-logs/blob/main/resources/queries_and_alerts.json
SummaryAttributes:
- login_user
- source_ip
- login_action
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
actionNameis one ofaadBrowserLogin,aadTokenLogin,certLogin,jwtLogin,loginresponse.statusCodeis one of401,403
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
actionName | in |
| field:"actionName" kind:in |
response.statusCode | in |
| field:"response.statusCode" kind:in |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
email | userIdentity.email |
sourceIPAddress | |
actionName |
Response runbook
1. Count all failed and successful login attempts for this user in the 6 hours around this alert
2. Check if the source IPs match known VPNs, proxies, or are from unexpected geographic locations
3. Find if there were successful logins from different IPs immediately after the failed attempts
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "samlLogin",
"response": {
"errorMessage": "Invalid credentials",
"statusCode": 401
},
"serviceName": "accounts",
"sourceIPAddress": "203.0.113.50",
"timestamp": 1704067200000,
"userIdentity": {
"email": "user@example.com"
}
}
Databricks SSO Configuration Changed
#Detects modifications to single sign-on (SSO) configurations in Databricks. While SSO changes may be part of planned identity provider updates, unauthorized modifications could indicate attempts to tamper with authentication mechanisms. Successful changes are elevated to MEDIUM severity.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Detection logic
from panther_databricks_helpers import databricks_alert_context
def rule(event):
if event.get("serviceName") != "ssoConfigBackend":
return False
return event.get("actionName") in ["create", "update"]
def severity(event):
status_code = event.deep_get("response", "statusCode")
return "MEDIUM" if status_code == 200 else "LOW"
def title(event):
action = event.get("actionName", "Unknown Action")
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
sso_status = event.deep_get("requestParams", "status", default="Unknown Status")
return f"SSO configuration {action}d by {actor} - Status: {sso_status}"
def dedup(event):
action = event.get("actionName", "unknown")
return f"sso_config_{action}"
def alert_context(event):
return databricks_alert_context(
event,
additional_fields={
"sso_status": event.deep_get("requestParams", "status"),
"sso_config": event.deep_get("requestParams", "config"),
},
)
Rule specification
AnalysisType: rule
Filename: databricks_sso_config_changed.py
RuleID: "Databricks.Audit.SSOConfigChanged"
DisplayName: "Databricks SSO Configuration Changed"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Credential Access
Reports:
MITRE ATT&CK:
- TA0006:T1556 # Modify Authentication Process
Severity: Low
Description: >
Detects modifications to single sign-on (SSO) configurations in Databricks. While SSO changes
may be part of planned identity provider updates, unauthorized modifications could indicate
attempts to tamper with authentication mechanisms. Successful changes are elevated to MEDIUM severity.
Runbook: |
1. Query audit logs for all SSO configuration changes in the 48 hours before and after this event
2. Check if there were successful logins using the new SSO configuration in the 6 hours after the change
3. Find all authentication failures or unusual login patterns in the 24 hours after the SSO change
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/blob/main/base/detections/event-based/sso_config_changed.py
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
serviceNameisssoConfigBackendactionNameis one ofcreate,update
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
actionName | in |
| field:"actionName" kind:in |
serviceName | eq |
| field:"serviceName" kind:eq value:"ssoConfigBackend" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
actionName | |
email | userIdentity.email |
status | requestParams.status |
Response runbook
1. Query audit logs for all SSO configuration changes in the 48 hours before and after this event
2. Check if there were successful logins using the new SSO configuration in the 6 hours after the change
3. Find all authentication failures or unusual login patterns in the 24 hours after the SSO change
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "create",
"requestParams": {
"config": {
"provider": "okta"
},
"status": "enabled"
},
"response": {
"statusCode": 200
},
"serviceName": "ssoConfigBackend",
"sourceIPAddress": "198.51.100.1",
"timestamp": 1234567890000,
"userIdentity": {
"email": "admin@example.com"
}
}
Databricks Terms of Service Changes
#Detects Terms of Service acceptance or distribution events for compliance tracking. These events should be monitored for audit and governance purposes.
Detection logic
from panther_databricks_helpers import databricks_alert_context
def rule(event):
return event.get("actionName") in ["acceptTos", "sendTos"]
def title(event):
action = event.get("actionName", "Unknown Action")
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
if action == "acceptTos":
return f"Terms of Service accepted by {actor}"
return f"Terms of Service distributed by {actor}"
def alert_context(event):
return databricks_alert_context(event)
Rule specification
AnalysisType: rule
Filename: databricks_terms_of_service_changes.py
RuleID: "Databricks.Audit.TermsOfServiceChanges"
DisplayName: "Databricks Terms of Service Changes"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Compliance
Severity: Info
Description: >
Detects Terms of Service acceptance or distribution events for compliance tracking.
These events should be monitored for audit and governance purposes.
Runbook: |
1. Query audit logs for all TOS-related events in the past 90 days to establish baseline
2. Check if this TOS acceptance aligns with expected onboarding or policy update timelines
3. Find all TOS events for this user to verify compliance history
Reference: https://github.com/andyweaves/system-tables-audit-logs/blob/main/resources/queries_and_alerts.json
Stages and Predicates
Fires on Databricks.Audit events when the condition below holds.
Condition
actionNameis one ofacceptTos,sendTos
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
actionName | in |
| field:"actionName" kind:in |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
email | userIdentity.email |
Response runbook
1. Query audit logs for all TOS-related events in the past 90 days to establish baseline
2. Check if this TOS acceptance aligns with expected onboarding or policy update timelines
3. Find all TOS events for this user to verify compliance history
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "acceptTos",
"response": {
"statusCode": 200
},
"serviceName": "accounts",
"sourceIPAddress": "198.51.100.1",
"timestamp": 1704067200000,
"userIdentity": {
"email": "newuser@example.com"
}
}
Databricks TruffleHog Scan Detected
#Detects TruffleHog secret scanning activity in Databricks. TruffleHog is a tool used to scan repositories and systems for exposed credentials and secrets. While it can be used legitimately for security audits, unauthorized scanning may indicate credential harvesting attempts. External IP sources are elevated to HIGH severity.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Credential Access | |
| Collection |
Detection logic
import ipaddress
from panther_databricks_helpers import databricks_alert_context, filter_noise
def rule(event):
# Filter out system noise
if filter_noise(event):
return False
# Check user agent for TruffleHog signature
user_agent = event.get("userAgent", "")
return "TruffleHog" in user_agent
def title(event):
source_ip = event.get("sourceIPAddress", "Unknown IP")
user = event.deep_get("userIdentity", "email", default="Unknown User")
return f"TruffleHog secret scan detected from {source_ip} (User: {user})"
def severity(event):
source_ip = event.get("sourceIPAddress", "")
# Lower severity for scans from private IPs (internal testing)
if source_ip:
try:
if ipaddress.ip_address(source_ip).is_private:
return "MEDIUM"
except ValueError:
# Invalid IP format, treat as public (HIGH severity)
pass
return "HIGH"
def alert_context(event):
return databricks_alert_context(
event,
additional_fields={
"token_id": event.deep_get("requestParams", "tokenId"),
"user_agent_full": event.get("userAgent"),
},
)
Rule specification
AnalysisType: rule
Filename: databricks_trufflehog_scan_detected.py
RuleID: "Databricks.Audit.TrufflehogScanDetected"
DisplayName: "Databricks TruffleHog Scan Detected"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Collection
- Credential Access
Reports:
MITRE ATT&CK:
- TA0006:T1552 # Unsecured Credentials
- TA0009:T1213 # Data from Information Repositories
Severity: Medium
Description: >
Detects TruffleHog secret scanning activity in Databricks. TruffleHog is a tool used to scan
repositories and systems for exposed credentials and secrets. While it can be used legitimately
for security audits, unauthorized scanning may indicate credential harvesting attempts. External
IP sources are elevated to HIGH severity.
Runbook: |
1. Query audit logs for all secret access attempts (getSecret action) by this user in the 24 hours before and after the TruffleHog scan
2. Check if the source IP (sourceIPAddress) matches known security scanning tools or is from an unexpected geographic location
3. Find all other unusual secret access patterns from this IP or user in the past 7 days
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/blob/main/base/detections/event-based/trufflehog_scan_detected.py
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
userAgentdoes not containDatabricks-Service/driveruserAgentdoes not containDatabricks-RuntimeuserAgentdoes not containDelta-Sharing-SparkStructuredStreaminguserAgentdoes not containRawDBHttpClientuserAgentdoes not containmlflow-pythonuserAgentdoes not containobsSDK-scalauserAgentdoes not containwsfsuserAgentdoes not containfeature-storerequestParams.pathdoes not contain/telemetryrequestParams.pathdoes not contain/delta-commitrequestParams.pathdoes not contain/healthrequestParams.pathdoes not contain/metricsrequestParams.pathdoes not contain/statususerAgentcontainsTruffleHog
This rule also runs imperative logic the parser cannot express as a filter. The conditions above are the structured part it could extract.
Exclusions
The rule actively suppresses these predicates.
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
userAgent | contains |
| field:"userAgent" kind:contains value:"TruffleHog" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
sourceIPAddress | |
email | userIdentity.email |
Response runbook
1. Query audit logs for all secret access attempts (getSecret action) by this user in the 24 hours before and after the TruffleHog scan
2. Check if the source IP (sourceIPAddress) matches known security scanning tools or is from an unexpected geographic location
3. Find all other unusual secret access patterns from this IP or user in the past 7 days
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "login",
"requestParams": {
"tokenId": "token-123"
},
"serviceName": "accounts",
"sourceIPAddress": "203.0.113.50",
"timestamp": 1234567890000,
"userAgent": "TruffleHog/3.0",
"userIdentity": {
"email": "scanner@external.com"
}
}
Databricks User Account Created
#Detects creation of new user accounts in Databricks. Account creation may be part of normal onboarding or could indicate an attacker establishing persistence.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Detection logic
from panther_databricks_helpers import databricks_alert_context
def rule(event):
if event.get("serviceName") != "accounts":
return False
action = event.get("actionName")
return action in ("createUser", "addUser")
def title(event):
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
target = event.deep_get("requestParams", "targetUserName", default="Unknown User")
endpoint = event.deep_get("requestParams", "endpoint", default="")
source = f" via {endpoint}" if endpoint else ""
return f"User account created: {target} by {actor}{source}"
def dedup(event):
target = event.deep_get("requestParams", "targetUserName", default="unknown")
return f"user_created_{target}"
def alert_context(event):
return databricks_alert_context(
event,
additional_fields={
"target_user": event.deep_get("requestParams", "targetUserName"),
"endpoint": event.deep_get("requestParams", "endpoint"),
},
)
Rule specification
AnalysisType: rule
Filename: databricks_user_account_created.py
RuleID: "Databricks.Audit.UserAccountCreated"
DisplayName: "Databricks User Account Created"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Persistence
Reports:
MITRE ATT&CK:
- TA0003:T1136 # Create Account
Severity: Info
Description: >
Detects creation of new user accounts in Databricks. Account creation may be part of
normal onboarding or could indicate an attacker establishing persistence.
Runbook: |
1. Verify the account creation was part of an approved onboarding workflow
2. Check if the new account was immediately granted elevated privileges
3. Review the actor who created the account
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/tree/main/base/detections/behavioral
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
serviceNameisaccountsactionNameis one ofcreateUser,addUser
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
actionName | in |
| field:"actionName" kind:in |
serviceName | eq |
| field:"serviceName" kind:eq value:"accounts" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
targetUserName | requestParams.targetUserName |
email | userIdentity.email |
Response runbook
1. Verify the account creation was part of an approved onboarding workflow
2. Check if the new account was immediately granted elevated privileges
3. Review the actor who created the account
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "createUser",
"requestParams": {
"targetUserName": "newuser@example.com"
},
"response": {
"statusCode": 200
},
"serviceName": "accounts",
"userIdentity": {
"email": "admin@example.com"
}
}
Databricks User Account Deleted
#Detects user account deletions in Databricks. While often part of normal offboarding processes, unauthorized deletions could indicate malicious activity or insider threats. Successful deletions are elevated to HIGH severity.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Impact |
Detection logic
from panther_databricks_helpers import databricks_alert_context
def rule(event):
if event.get("serviceName") != "accounts":
return False
if event.get("actionName") != "delete":
return False
# Only match user deletions, not other account-level deletes
# Primary check: targetUserName field exists
# Fallback: endpoint contains "/users/" or starts with "users"
if event.deep_get("requestParams", "targetUserName") is not None:
return True
endpoint = event.deep_get("requestParams", "endpoint", default="").lower()
return "/users/" in endpoint or endpoint.startswith("users")
def severity(event):
status_code = event.deep_get("response", "statusCode")
return "HIGH" if status_code == 200 else "LOW"
def title(event):
target_user = event.deep_get("requestParams", "targetUserName", default="Unknown User")
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
status_code = event.deep_get("response", "statusCode")
status = "Success" if status_code == 200 else "Failed"
return f"User account deletion {status}: {target_user} by {actor}"
def dedup(event):
target_user = event.deep_get("requestParams", "targetUserName", default="unknown")
return f"user_deleted_{target_user}"
def alert_context(event):
return databricks_alert_context(
event,
additional_fields={
"target_user": event.deep_get("requestParams", "targetUserName"),
"endpoint": event.deep_get("requestParams", "endpoint"),
},
)
Rule specification
AnalysisType: rule
Filename: databricks_user_account_deleted.py
RuleID: "Databricks.Audit.UserAccountDeleted"
DisplayName: "Databricks User Account Deleted"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Impact
Reports:
MITRE ATT&CK:
- TA0040:T1531 # Account Access Removal
Severity: Low
Description: >
Detects user account deletions in Databricks. While often part of normal offboarding processes,
unauthorized deletions could indicate malicious activity or insider threats. Successful deletions
are elevated to HIGH severity.
Runbook: |
1. Query audit logs for all actions performed by the deleted user (requestParams.targetUserName) in the 48 hours before deletion
2. Check if there were any privilege escalation or suspicious actions by this user in the 7 days before deletion
3. Find all user deletions by this actor in the past 30 days to identify bulk deletion patterns
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/blob/main/base/detections/event-based/user_account_deleted.py
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
serviceNameisaccountsactionNameisdeleteany of:
requestParams.targetUserNameis presentrequestParams.endpointcontains/users/requestParams.endpointstarts withusers
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 | Source |
|---|---|
targetUserName | requestParams.targetUserName |
email | userIdentity.email |
Response runbook
1. Query audit logs for all actions performed by the deleted user (requestParams.targetUserName) in the 48 hours before deletion
2. Check if there were any privilege escalation or suspicious actions by this user in the 7 days before deletion
3. Find all user deletions by this actor in the past 30 days to identify bulk deletion patterns
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "delete",
"requestParams": {
"endpoint": "/users",
"targetUserName": "user@example.com"
},
"response": {
"statusCode": 200
},
"serviceName": "accounts",
"sourceIPAddress": "198.51.100.1",
"timestamp": 1234567890000,
"userIdentity": {
"email": "admin@example.com"
}
}
Databricks User Password Changed
#Detects password change events on Databricks accounts. May indicate legitimate password rotation or an unauthorized reset following account compromise.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Privilege Escalation |
Detection logic
from panther_databricks_helpers import databricks_alert_context
def rule(event):
if event.get("serviceName") != "accounts":
return False
return event.get("actionName") == "changePassword"
def title(event):
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
status_code = event.deep_get("response", "statusCode")
status = "Success" if status_code == 200 else "Failed"
return f"Password changed by {actor} - {status}"
def dedup(event):
actor = event.deep_get("userIdentity", "email", default="unknown")
return f"password_changed_{actor}"
def alert_context(event):
return databricks_alert_context(event)
Rule specification
AnalysisType: rule
Filename: databricks_user_password_changed.py
RuleID: "Databricks.Audit.UserPasswordChanged"
DisplayName: "Databricks User Password Changed"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Credential Access
Reports:
MITRE ATT&CK:
- TA0006:T1098 # Account Manipulation
Severity: Info
Description: >
Detects password change events on Databricks accounts. May indicate legitimate
password rotation or an unauthorized reset following account compromise.
Runbook: |
1. Verify the password change was initiated by the legitimate account owner
2. Check for preceding suspicious activity (failed logins, MFA changes)
3. If unauthorized, force password reset and revoke active sessions
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/tree/main/base/detections/behavioral
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
serviceNameisaccountsactionNameischangePassword
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
actionName | eq |
| field:"actionName" kind:eq value:"changePassword" |
serviceName | eq |
| field:"serviceName" kind:eq value:"accounts" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
email | userIdentity.email |
Response runbook
1. Verify the password change was initiated by the legitimate account owner
2. Check for preceding suspicious activity (failed logins, MFA changes)
3. If unauthorized, force password reset and revoke active sessions
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "changePassword",
"response": {
"statusCode": 200
},
"serviceName": "accounts",
"userIdentity": {
"email": "user@example.com"
}
}
Databricks User Role Modified
#Detects when user roles are modified or users are added to administrative groups in Databricks. This is often legitimate administrative activity but should be monitored for unauthorized changes.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation |
Detection logic
from panther_databricks_helpers import databricks_alert_context
def rule(event):
if event.get("serviceName") != "accounts":
return False
return event.get("actionName") in ["addUserToAdminGroup", "modifyUserRole"]
def title(event):
action = event.get("actionName", "Unknown Action")
actor = event.deep_get("userIdentity", "email", default="Unknown User")
return f"User role modified: {action} by {actor}"
def alert_context(event):
return databricks_alert_context(
event, additional_fields={"request_params": event.get("requestParams")}
)
Rule specification
AnalysisType: rule
Filename: databricks_user_role_modified.py
RuleID: "Databricks.Audit.UserRoleModified"
DisplayName: "Databricks User Role Modified"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Privilege Escalation
Reports:
MITRE ATT&CK:
- TA0004:T1098 # Account Manipulation
Severity: Info
Description: >
Detects when user roles are modified or users are added to administrative groups in Databricks.
This is often legitimate administrative activity but should be monitored for unauthorized changes.
Runbook: |
1. Query audit logs for all role modifications by the actor (userIdentity.email) in the 24 hours before and after this change
2. Check if the target user has performed new actions using the modified role in the 6 hours after the change
3. Find all role modifications for this target user in the past 90 days to establish baseline
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/blob/main/base/detections/event-based/user_role_modified.py
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
serviceNameisaccountsactionNameis one ofaddUserToAdminGroup,modifyUserRole
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
actionName | in |
| field:"actionName" kind:in |
serviceName | eq |
| field:"serviceName" kind:eq value:"accounts" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
actionName | |
email | userIdentity.email |
Response runbook
1. Query audit logs for all role modifications by the actor (userIdentity.email) in the 24 hours before and after this change
2. Check if the target user has performed new actions using the modified role in the 6 hours after the change
3. Find all role modifications for this target user in the past 90 days to establish baseline
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "addUserToAdminGroup",
"requestParams": {
"targetGroupName": "admins",
"targetUserName": "user@example.com"
},
"serviceName": "accounts",
"sourceIPAddress": "198.51.100.1",
"timestamp": 1234567890000,
"userIdentity": {
"email": "admin@example.com"
}
}
Databricks Verbose Audit Logging Disabled
#Detects when verbose audit logging is disabled in a Databricks workspace. Disabling verbose audit logging significantly reduces the visibility of security-relevant events and is a common technique used by attackers to hide malicious activity. Successful disabling is elevated to CRITICAL severity.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
Detection logic
from panther_databricks_helpers import databricks_alert_context
def rule(event):
if event.get("serviceName") != "workspace":
return False
if event.get("actionName") != "workspaceConfEdit":
return False
# Check if the configuration key is for verbose audit logs
conf_key = event.deep_get("requestParams", "workspaceConfKeys")
if conf_key != "enableVerboseAuditLogs":
return False
# Check if verbose logging is being disabled
conf_value = event.deep_get("requestParams", "workspaceConfValues")
return conf_value == "false"
def severity(event):
status_code = event.deep_get("response", "statusCode")
return "CRITICAL" if status_code == 200 else "HIGH"
def title(event):
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
status_code = event.deep_get("response", "statusCode")
status = "Successfully disabled" if status_code == 200 else "Attempted to disable"
return f"Verbose audit logging {status} by {actor}"
def alert_context(event):
conf_value = event.deep_get("requestParams", "workspaceConfValues")
return databricks_alert_context(
event,
additional_fields={
"config_key": event.deep_get("requestParams", "workspaceConfKeys"),
"config_value": conf_value,
"config_status": "Disabled" if conf_value == "false" else "Enabled",
},
)
Rule specification
AnalysisType: rule
Filename: databricks_verbose_audit_logging_disabled.py
RuleID: "Databricks.Audit.VerboseAuditLoggingDisabled"
DisplayName: "Databricks Verbose Audit Logging Disabled"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Defense Evasion
Reports:
MITRE ATT&CK:
- TA0005:T1562.008 # Impair Defenses: Disable or Modify Cloud Logs
Severity: High
Description: >
Detects when verbose audit logging is disabled in a Databricks workspace. Disabling verbose
audit logging significantly reduces the visibility of security-relevant events and is a common
technique used by attackers to hide malicious activity. Successful disabling is elevated to
CRITICAL severity.
Runbook: |
1. Query audit logs for all actions by the actor (userIdentity.email) in the 6 hours before and after disabling audit logging
2. Check if there were suspicious data access, deletion, or privilege escalation actions in the 24 hours around this change
3. Find all other high-risk configuration changes by this actor in the past 7 days
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/blob/main/base/detections/event-based/verbose_audit_logging_disabled.py
SummaryAttributes:
- actor
- source_ip
- config_status
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
serviceNameisworkspaceactionNameisworkspaceConfEditrequestParams.workspaceConfKeysisenableVerboseAuditLogsrequestParams.workspaceConfValuesisfalse
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
actionName | eq |
| field:"actionName" kind:eq value:"workspaceConfEdit" |
requestParams.workspaceConfKeys | eq |
| field:"requestParams.workspaceConfKeys" kind:eq value:"enableVerboseAuditLogs" |
requestParams.workspaceConfValues | eq |
| field:"requestParams.workspaceConfValues" kind:eq value:"false" |
serviceName | eq |
| field:"serviceName" kind:eq value:"workspace" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
email | userIdentity.email |
Response runbook
1. Query audit logs for all actions by the actor (userIdentity.email) in the 6 hours before and after disabling audit logging
2. Check if there were suspicious data access, deletion, or privilege escalation actions in the 24 hours around this change
3. Find all other high-risk configuration changes by this actor in the past 7 days
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "workspaceConfEdit",
"auditLevel": "WORKSPACE_LEVEL",
"requestParams": {
"workspaceConfKeys": "enableVerboseAuditLogs",
"workspaceConfValues": "false"
},
"response": {
"statusCode": 200
},
"serviceName": "workspace",
"sourceIPAddress": "198.51.100.1",
"timestamp": 1234567890000,
"userAgent": "Mozilla/5.0",
"userIdentity": {
"email": "admin@example.com"
}
}
Databricks Workspace Admin Privileged Role Assignment
#Detects when workspace-level admin privileges are granted in Databricks through direct role assignments or administrative group membership. This simplified version detects direct admin grants and additions to admin groups. For nested group resolution (detecting when groups are added to admin groups), consider implementing a correlation rule. Successful grants to the system 'admins' group are elevated to HIGH severity.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Privilege Escalation |
Detection logic
from panther_databricks_helpers import (
databricks_alert_context,
extract_group_identifier,
extract_target_principal,
get_principal_type,
is_admin_privilege_action,
)
REMOVAL_ACTIONS = ["removeAdmin", "removePrincipalFromGroup"]
def rule(event):
# Only match workspace-level events to avoid overlap with the
# account-level admin privilege rule
if event.get("auditLevel") != "WORKSPACE_LEVEL":
return False
# Exclude privilege removals — this rule detects grants only
if event.get("actionName") in REMOVAL_ACTIONS:
return False
return is_admin_privilege_action(event)
def severity(event):
status_code = event.deep_get("response", "statusCode")
return "HIGH" if status_code == 200 else "MEDIUM"
def title(event):
action = event.get("actionName", "Unknown Action")
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
target = extract_target_principal(event) or "Unknown Principal"
workspace = event.get("workspaceId", "Unknown Workspace")
status_code = event.deep_get("response", "statusCode")
status = "Granted" if status_code == 200 else "Attempted to grant"
# Check if it's direct admin action or group-based
if action in ["setAdmin", "addAdmin"]:
return (
f"{status} workspace admin privileges to {target}"
f" in workspace {workspace} by {actor}"
)
group = extract_group_identifier(event)
return (
f"{status} admin group membership ({group}) to {target}"
f" in workspace {workspace} by {actor}"
)
def dedup(event):
target_principal = extract_target_principal(event) or "unknown"
workspace = event.get("workspaceId", "unknown")
return f"workspace_admin_privilege_{workspace}_{target_principal}"
def alert_context(event):
target_principal = extract_target_principal(event)
principal_type = get_principal_type(target_principal) if target_principal else "Unknown"
group = extract_group_identifier(event)
return databricks_alert_context(
event,
additional_fields={
"privilege_scope": "WORKSPACE_LEVEL",
"target_principal": target_principal,
"principal_type": principal_type,
"target_group": group,
"is_system_admins_group": group.lower() == "admins" if group else False,
"detection_note": (
"Direct grants only - nested group resolution requires correlation rule"
),
},
)
Rule specification
AnalysisType: rule
Filename: databricks_workspace_admin_privileged_role_assignment.py
RuleID: "Databricks.Audit.WorkspaceAdminPrivilegedRoleAssignment"
DisplayName: "Databricks Workspace Admin Privileged Role Assignment"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Privilege Escalation
- Persistence
Reports:
MITRE ATT&CK:
- TA0004:T1098 # Account Manipulation
- TA0003:T1136 # Create Account
Severity: Medium
Description: >
Detects when workspace-level admin privileges are granted in Databricks through direct role
assignments or administrative group membership. This simplified version detects direct admin
grants and additions to admin groups. For nested group resolution (detecting when groups are
added to admin groups), consider implementing a correlation rule. Successful grants to the
system 'admins' group are elevated to HIGH severity.
Runbook: |
1. Query audit logs for all workspace administrative actions by the target principal in the 24 hours after this privilege grant
2. Check if the target principal created clusters, modified notebooks, or accessed sensitive data in the 6 hours after receiving admin rights
3. Find all workspace admin grants for this workspace in the past 90 days to establish baseline
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/blob/main/base/detections/event-based/workspace_admin_privileged_role_assignment.py
SummaryAttributes:
- actor
- target_principal
- principal_type
- target_group
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
auditLevelisWORKSPACE_LEVELactionNameis not one ofremoveAdmin,removePrincipalFromGroup
This rule also runs imperative logic the parser cannot express as a filter. The conditions above are the structured part it could extract.
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
actionName | in | removeAdmin, removePrincipalFromGroup | excludes:actionName field:"actionName" value:"removeAdmin" field:"actionName" value:"removePrincipalFromGroup" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
auditLevel | eq |
| field:"auditLevel" kind:eq value:"WORKSPACE_LEVEL" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
workspaceId | |
email | userIdentity.email |
Response runbook
1. Query audit logs for all workspace administrative actions by the target principal in the 24 hours after this privilege grant
2. Check if the target principal created clusters, modified notebooks, or accessed sensitive data in the 6 hours after receiving admin rights
3. Find all workspace admin grants for this workspace in the past 90 days to establish baseline
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "setAdmin",
"auditLevel": "WORKSPACE_LEVEL",
"requestParams": {
"targetUserName": "newadmin@example.com"
},
"response": {
"statusCode": 200
},
"serviceName": "accounts",
"sourceIPAddress": "198.51.100.1",
"timestamp": 1234567890000,
"userIdentity": {
"email": "admin@example.com"
},
"workspaceId": "1234567890123456"
}
Databricks Workspace-Level Configuration Changes
#Detects configuration changes at the Databricks workspace level. Workspace-level changes affect a single workspace and include settings like cluster configurations, notebook settings, and workspace-specific security controls.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Detection logic
from panther_databricks_helpers import (
databricks_alert_context,
get_config_key_value,
is_config_change,
)
def rule(event):
# Must be workspace-level audit event
if event.get("auditLevel") != "WORKSPACE_LEVEL":
return False
# Check if it's a workspace configuration change
return is_config_change(event, config_category="workspace")
def title(event):
action = event.get("actionName", "Unknown Action")
actor = event.deep_get("userIdentity", "email", default="Unknown Actor")
workspace = event.get("workspaceId", "Unknown Workspace")
status_code = event.deep_get("response", "statusCode")
status = "Success" if status_code == 200 else "Failed"
# Include the config key in the title when available
config_key, _ = get_config_key_value(event)
if config_key:
return f"Workspace config change ({config_key}) in {workspace} by {actor} - {status}"
return f"Workspace configuration change ({action}) in {workspace} by {actor} - {status}"
def dedup(event):
workspace = event.get("workspaceId", "unknown")
config_key, _ = get_config_key_value(event)
return f"workspace_config_change_{workspace}_{config_key}"
def alert_context(event):
config_key, config_value = get_config_key_value(event)
return databricks_alert_context(
event,
additional_fields={
"change_scope": "WORKSPACE_LEVEL",
"config_key": config_key,
"config_value": config_value,
},
)
Rule specification
AnalysisType: rule
Filename: databricks_config_changes_workspace_level.py
RuleID: "Databricks.Audit.ConfigChangesWorkspaceLevel"
DisplayName: "Databricks Workspace-Level Configuration Changes"
Enabled: true
Status: Experimental
LogTypes:
- Databricks.Audit
Tags:
- Databricks
- Persistence
Reports:
MITRE ATT&CK:
- TA0003:T1098 # Account Manipulation
Severity: Info
Description: >
Detects configuration changes at the Databricks workspace level. Workspace-level changes
affect a single workspace and include settings like cluster configurations, notebook
settings, and workspace-specific security controls.
Runbook: |
1. Query audit logs for all workspace configuration changes in the 24 hours around this event
2. Check if the workspace behavior changed based on cluster activity or user actions in the 6 hours after the configuration change
3. Find all configuration changes for this workspace in the past 30 days to establish baseline
Reference: https://github.com/databricks-solutions/cybersec-workspace-detection-app/blob/main/base/detections/event-based/configuration_changes_workspace_level.py
Stages and Predicates
Fires on Databricks.Audit events when all of the conditions below hold.
Condition
auditLevelisWORKSPACE_LEVELany of:
serviceNameisworkspaceall of:
serviceNameis notworkspaceserviceNameisaccounts
all of:
serviceNameis notworkspaceserviceNameis notaccountsserviceNameisssoConfigBackend
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 |
|---|---|---|---|
auditLevel | eq |
| field:"auditLevel" kind:eq value:"WORKSPACE_LEVEL" |
serviceName | eq |
| field:"serviceName" kind:eq |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
workspaceId | |
email | userIdentity.email |
actionName |
Response runbook
1. Query audit logs for all workspace configuration changes in the 24 hours around this event
2. Check if the workspace behavior changed based on cluster activity or user actions in the 6 hours after the configuration change
3. Find all configuration changes for this workspace in the past 30 days to establish baseline
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"actionName": "workspaceConfEdit",
"auditLevel": "WORKSPACE_LEVEL",
"requestParams": {
"workspaceConfKeys": "defaultClusterVersion",
"workspaceConfValues": "12.2.x-scala2.12"
},
"response": {
"statusCode": 200
},
"serviceName": "workspace",
"sourceIPAddress": "198.51.100.1",
"timestamp": 1234567890000,
"userIdentity": {
"email": "admin@example.com"
},
"workspaceId": "1234567890123456"
}