Skip to content

Commit 97fc335

Browse files
authored
Merge pull request #1 from databricks-solutions/latest_merge
Latest merge
2 parents 2eb9033 + fdb45be commit 97fc335

34 files changed

+1595
-362
lines changed

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
global

README.md

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ A collection of security detection notebooks for Databricks workspaces that anal
44

55
## Overview
66

7-
This detection app provides 25+ pre-built security detection notebooks designed for security operations teams to monitor Databricks workspace activities. The detections cover various security scenarios including:
7+
This detection app provides 30+ pre-built security detection notebooks designed for security operations teams to monitor Databricks workspace activities. The detections cover various security scenarios including:
88

99
- **Authentication & Access Control**: Token creation/deletion, MFA changes, SSO configuration changes
1010
- **User Management**: Account creation/deletion, role modifications, group changes
@@ -14,11 +14,12 @@ This detection app provides 25+ pre-built security detection notebooks designed
1414

1515
## Features
1616

17-
- **Coverage**: 25+ detection scenarios covering major security use cases
17+
- **Coverage**: 30+ detection scenarios covering major security use cases
1818
- **Production Ready**: Designed for batch execution via Databricks workflows
1919
- **Configurable**: Customizable time ranges and detection parameters
2020
- **Audit Table Focus**: Leverages Databricks `system.access.audit` table for comprehensive visibility
2121
- **Unity Catalog Compatible**: Designed for Unity Catalog enabled accounts
22+
- **MITRE ATT&CK Mapped**: Many detections include MITRE ATT&CK framework mappings for threat intelligence
2223

2324
## Detection Categories
2425

@@ -50,6 +51,30 @@ This detection app provides 25+ pre-built security detection notebooks designed
5051
- Attempted Logon from Denied IP
5152
- Token Scanning Activity Detection
5253

54+
### Data Exfiltration & Movement
55+
- Potential Data Movement via SQL Queries
56+
- Potential Data Movement via Workspace Downloads
57+
- Potential Data Movement via Explicit Credentials
58+
59+
### Configuration & Policy Monitoring
60+
- High Priority Configuration Changes
61+
- Workspace-Level Configuration Changes
62+
- Account-Level Configuration Changes
63+
64+
### Secrets & Credential Management
65+
- Secret Scanning Activity Detection
66+
- Admin User Account Changes
67+
68+
## Enhanced Detection Capabilities
69+
70+
The latest version includes advanced detection scenarios that go beyond basic audit monitoring:
71+
72+
- **Data Exfiltration Detection**: Identifies potential data movement attempts using SQL queries, workspace downloads, and explicit credentials
73+
- **Configuration Tampering**: Monitors for unauthorized changes to security-critical workspace and account configurations
74+
- **Secret Enumeration**: Detects reconnaissance activities targeting secret scopes and credential harvesting
75+
- **Admin Privilege Escalation**: Tracks administrative privilege changes and group membership modifications
76+
- **Comprehensive Coverage**: Integrates both `system.access.audit` and `system.query.history` tables for complete visibility
77+
5378
## Installation
5479

5580
### Prerequisites

base/detections/access_token_created.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,30 @@
66
# MAGIC %md
77
# MAGIC ```yaml
88
# MAGIC dscc:
9-
# MAGIC author: root
10-
# MAGIC created: '2025-05-09T12:56:50'
11-
# MAGIC modified: '2025-05-09T12:56:50'
12-
# MAGIC uuid: 4e8de7fb-5fbe-424c-99be-22e6efbb5445
9+
# MAGIC author: derek.king
10+
# MAGIC created: '2025-06-17T11:59:15'
11+
# MAGIC modified: '2025-06-17T11:59:15'
12+
# MAGIC uuid: ea49708b-50cf-4926-b734-c8acef522744
1313
# MAGIC content_type: detection
1414
# MAGIC detection:
1515
# MAGIC name: Access Token Created
1616
# MAGIC description: Detects access tokens created from unknown IPs or user agents with
1717
# MAGIC a non-zero lifetime.
18+
# MAGIC fidelity: high
19+
# MAGIC category: POLICY
1820
# MAGIC objective: 'Identify potentially suspicious access token creation events by filtering
1921
# MAGIC for: the `generateDbToken` action within the `accounts` service, tokens created
2022
# MAGIC outside of known IP address and user agent baselines, and tokens with a measurable
2123
# MAGIC lifespan (i.e., not ephemeral). This helps surface anomalous authentication
2224
# MAGIC behavior that may signify credential abuse, script-based automation, or unauthorized
23-
# MAGIC access attempts.
24-
# MAGIC
25-
# MAGIC '
26-
# MAGIC taxonomy: []
25+
# MAGIC access attempts.'
26+
# MAGIC false_positives: unknown
27+
# MAGIC severity: high
28+
# MAGIC taxonomy:
29+
# MAGIC - none
30+
# MAGIC platform:
31+
# MAGIC - databricks
32+
# MAGIC version: 1.0.0
2733
# MAGIC dscc-tests:
2834
# MAGIC tests:
2935
# MAGIC - function: access_token_created
@@ -99,7 +105,12 @@ def access_token_created(earliest:str = None, latest: str = None):
99105

100106
# COMMAND ----------
101107

102-
display(access_token_created(earliest="2025-01-01", latest="2025-02-25"))
108+
if __name__ == "__main__" or dbutils.widgets.get("earliest"):
109+
earliest, latest = get_time_range_from_widgets()
110+
display(access_token_created(
111+
earliest=dbutils.widgets.get("earliest"),
112+
latest=dbutils.widgets.get("latest")
113+
))
103114

104115
# COMMAND ----------
105116

base/detections/access_token_deleted.py

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,26 @@
66
# MAGIC %md
77
# MAGIC ```yaml
88
# MAGIC dscc:
9-
# MAGIC author: Derek King - Databricks
10-
# MAGIC created: '2025-05-09T12:56:50'
11-
# MAGIC modified: '2025-05-09T12:56:50'
12-
# MAGIC uuid: 4e8de7fb-5fbe-424c-99be-22e6efbb5445
9+
# MAGIC author: derek.king
10+
# MAGIC created: '2025-06-17T12:21:38'
11+
# MAGIC modified: '2025-06-17T12:21:38'
12+
# MAGIC uuid: 479f8708-b446-4a2a-af32-0c14ee52a5d4
1313
# MAGIC content_type: detection
1414
# MAGIC detection:
1515
# MAGIC name: Access Token Deleted
16-
# MAGIC description: 'Detects access tokens being revoked via the accounts service.
17-
# MAGIC
18-
# MAGIC '
19-
# MAGIC objective: 'Monitor for explicit revocation of access tokens to track potentially
20-
# MAGIC suspicious cleanup activity,
21-
# MAGIC
22-
# MAGIC such as unauthorized credential invalidation or the concealment of prior access.
23-
# MAGIC
24-
# MAGIC '
25-
# MAGIC taxonomy: []
16+
# MAGIC description: Detects access tokens being revoked via the accounts service.
17+
# MAGIC fidelity: high
18+
# MAGIC category: POLICY
19+
# MAGIC objective: Monitor for explicit revocation of access tokens to track potentially
20+
# MAGIC suspicious cleanup activity, such as unauthorized credential invalidation or
21+
# MAGIC the concealment of prior access.
22+
# MAGIC false_positives: unknown
23+
# MAGIC severity: low
24+
# MAGIC taxonomy:
25+
# MAGIC - none
26+
# MAGIC platform:
27+
# MAGIC - databricks
28+
# MAGIC version: 1.0.0
2629
# MAGIC dscc-tests:
2730
# MAGIC tests:
2831
# MAGIC - function: access_token_deleted
@@ -77,4 +80,9 @@ def access_token_deleted(earliest:str = None, latest: str = None):
7780

7881
# COMMAND ----------
7982

80-
display(access_token_deleted(earliest="2020-01-01", latest="2025-02-25"))
83+
if __name__ == "__main__" or dbutils.widgets.get("earliest"):
84+
earliest, latest = get_time_range_from_widgets()
85+
display(access_token_deleted(
86+
earliest=dbutils.widgets.get("earliest"),
87+
latest=dbutils.widgets.get("latest")
88+
))

base/detections/attempted_logon_from_denied_ip.py

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,27 @@
66
# MAGIC %md
77
# MAGIC ```yaml
88
# MAGIC dscc:
9-
# MAGIC author: Derek King - Databricks
10-
# MAGIC created: '2025-05-09T12:56:50'
11-
# MAGIC modified: '2025-05-09T12:56:50'
12-
# MAGIC uuid: 4e8de7fb-5fbe-424c-99be-22e6efbb5445
9+
# MAGIC author: derek.king
10+
# MAGIC created: '2025-06-17T11:57:56'
11+
# MAGIC modified: '2025-06-17T11:57:56'
12+
# MAGIC uuid: 5e7e72b8-5174-447f-970d-3ba5772ca8e9
1313
# MAGIC content_type: detection
1414
# MAGIC detection:
15-
# MAGIC name: Attempted Logon from Denied IP
16-
# MAGIC description: 'Detects blocked login attempts from IP addresses denied by workspace
15+
# MAGIC name: Attempted Logon From Denied Ip
16+
# MAGIC description: Detects blocked login attempts from IP addresses denied by workspace
1717
# MAGIC access control policies.
18-
# MAGIC
19-
# MAGIC '
20-
# MAGIC objective: 'Identify logon attempts from explicitly denied IPs that bypass known
21-
# MAGIC service agents and telemetry paths,
22-
# MAGIC
23-
# MAGIC which may indicate unauthorized scanning activity, policy testing, or brute-force
24-
# MAGIC attempts from untrusted networks.
25-
# MAGIC
26-
# MAGIC '
27-
# MAGIC taxonomy: []
18+
# MAGIC fidelity: high
19+
# MAGIC category: POLICY
20+
# MAGIC objective: Identify logon attempts from explicitly denied IPs that bypass known
21+
# MAGIC service agents and telemetry paths, which may indicate unauthorized scanning
22+
# MAGIC activity, policy testing, or brute-force attempts from untrusted networks.
23+
# MAGIC false_positives: unknown
24+
# MAGIC severity: low
25+
# MAGIC taxonomy:
26+
# MAGIC - none
27+
# MAGIC platform:
28+
# MAGIC - databricks
29+
# MAGIC version: 1.0.0
2830
# MAGIC dscc-tests:
2931
# MAGIC tests:
3032
# MAGIC - function: attempted_logon_from_denied_ip
@@ -55,7 +57,7 @@
5557
# COMMAND ----------
5658

5759
@detect(output=Output.asDataFrame)
58-
def attempted_logon_from_denied_ip(earliest:str = None, latest: str = None, ignore_tokens: bool = False):
60+
def attempted_logon_from_denied_ip(earliest:str = None, latest: str = None, ignore_tokens: bool = True):
5961
from pyspark.sql.functions import (col, current_date, date_sub, to_date, current_timestamp,
6062
expr, unix_timestamp, round, from_unixtime, when, to_timestamp)
6163

@@ -111,5 +113,9 @@ def attempted_logon_from_denied_ip(earliest:str = None, latest: str = None, igno
111113

112114
# COMMAND ----------
113115

114-
df = attempted_logon_from_denied_ip(earliest="2025-03-13", latest="2025-03-14", ignore_tokens=True)
115-
display(df)
116+
if __name__ == "__main__" or dbutils.widgets.get("earliest"):
117+
earliest, latest = get_time_range_from_widgets()
118+
display(attempted_logon_from_denied_ip(
119+
earliest=dbutils.widgets.get("earliest"),
120+
latest=dbutils.widgets.get("latest")
121+
))
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Databricks notebook source
2+
# MAGIC %run ../../lib/common
3+
4+
# COMMAND ----------
5+
6+
# MAGIC %md
7+
# MAGIC ```yaml
8+
# MAGIC dscc:
9+
# MAGIC author: David Veuve - Databricks
10+
# MAGIC created: '2025-06-04T12:00:00'
11+
# MAGIC modified: '2025-06-04T12:00:00'
12+
# MAGIC uuid: 1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d
13+
# MAGIC content_type: detection
14+
# MAGIC detection:
15+
# MAGIC name: Account Level Configuration Changes
16+
# MAGIC description: Detects when users modify account-level settings, which could
17+
# MAGIC indicate privilege escalation attempts or unauthorized administrative changes
18+
# MAGIC to tenant-wide configurations.
19+
# MAGIC objective: 'Identify potentially suspicious account-level configuration changes
20+
# MAGIC that could affect the entire Databricks tenant. These modifications might
21+
# MAGIC indicate attempts to escalate privileges, weaken security controls, or
22+
# MAGIC establish persistence at the tenant level. Maps to MITRE ATT&CK TA0004
23+
# MAGIC - Privilege Escalation - T1484 - Domain or Tenant Policy Modification.'
24+
# MAGIC taxonomy:
25+
# MAGIC - MITRE.TA0004.Privilege_Escalation
26+
# MAGIC - MITRE.T1484.Domain_or_Tenant_Policy_Modification
27+
# MAGIC fidelity: low
28+
# MAGIC category: DETECTION
29+
# MAGIC false_positives: unknown
30+
# MAGIC severity: low
31+
# MAGIC platform:
32+
# MAGIC - databricks
33+
# MAGIC dscc-tests:
34+
# MAGIC tests:
35+
# MAGIC - function: configuration_changes_account_level
36+
# MAGIC input:
37+
# MAGIC earliest: '2025-01-01'
38+
# MAGIC latest: '2025-02-25'
39+
# MAGIC expect:
40+
# MAGIC count: '>0'
41+
# MAGIC schema: []
42+
# MAGIC data: null
43+
# MAGIC mocked_inputs:
44+
# MAGIC - table: system.access.audit
45+
# MAGIC path: None
46+
# MAGIC required_columns:
47+
# MAGIC - EVENT_TIME
48+
# MAGIC - SERVICE_NAME
49+
# MAGIC - ACTION_NAME
50+
# MAGIC - ACTION_DESCRIPTION
51+
# MAGIC - USER_EMAIL
52+
# MAGIC - USER_AGENT
53+
# MAGIC - SOURCE_IP_ADDRESS
54+
# MAGIC - REQUEST_PARAMS
55+
# MAGIC - event_time
56+
# MAGIC - service_name
57+
# MAGIC - action_name
58+
# MAGIC - audit_level
59+
# MAGIC - user_identity.email
60+
# MAGIC - user_agent
61+
# MAGIC - source_ip_address
62+
# MAGIC - request_params
63+
# MAGIC ```
64+
65+
# COMMAND ----------
66+
67+
@detect(output=Output.asDataFrame)
68+
def configuration_changes_account_level(earliest: str = None, latest: str = None):
69+
from pyspark.sql.functions import (col, current_date, date_sub, current_timestamp,
70+
expr, to_timestamp, concat, lit)
71+
72+
earliest = earliest or current_timestamp() - expr("INTERVAL 2 months")
73+
latest = latest or current_timestamp()
74+
75+
df = spark.table("system.access.audit")
76+
77+
# Filter for account-level setting changes
78+
df_filtered = df.filter(
79+
(col("audit_level") == "ACCOUNT_LEVEL") &
80+
(col("action_name") == "setSetting") &
81+
(col("event_time").between(earliest, latest))
82+
).select(
83+
to_timestamp(col("event_time")).alias("EVENT_TIME"),
84+
col("service_name").alias("SERVICE_NAME"),
85+
col("action_name").alias("ACTION_NAME"),
86+
concat(
87+
col("action_name"),
88+
lit(" for "),
89+
col("audit_level")
90+
).alias("ACTION_DESCRIPTION"),
91+
col("user_identity.email").alias("USER_EMAIL"),
92+
col("user_agent").alias("USER_AGENT"),
93+
col("source_ip_address").alias("SOURCE_IP_ADDRESS"),
94+
col("request_params").alias("REQUEST_PARAMS")
95+
).orderBy(col("EVENT_TIME").desc())
96+
97+
return df_filtered
98+
99+
# COMMAND ----------
100+
101+
if __name__ == "__main__" or dbutils.widgets.get("earliest"):
102+
earliest, latest = get_time_range_from_widgets()
103+
display(configuration_changes_account_level(
104+
earliest=dbutils.widgets.get("earliest"),
105+
latest=dbutils.widgets.get("latest")
106+
))
107+
# COMMAND ----------

0 commit comments

Comments
 (0)