-
Notifications
You must be signed in to change notification settings - Fork 176
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding Some Snowflake Behavioral/Anomaly Scheduled Queries (#1408)
Co-authored-by: Ariel Ropek <[email protected]>
- Loading branch information
1 parent
ac1ad37
commit 81a1f7e
Showing
11 changed files
with
310 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
queries/snowflake_queries/snowflake_attempted_login_by_disabled_user.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
def rule(_): | ||
return True | ||
|
||
|
||
def title(event): | ||
source = event.get("p_source_label", "<UNKNOWN SOURCE>") | ||
username = event.get("USER_NAME", "<UNKNOWN USER>") | ||
return f"{source}: Attempted signin by disabled user {username}" | ||
|
||
|
||
def alert_context(event): | ||
return event.get("user") |
42 changes: 42 additions & 0 deletions
42
queries/snowflake_queries/snowflake_attempted_login_by_disabled_user.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
AnalysisType: scheduled_rule | ||
Filename: snowflake_attempted_login_by_disabled_user.py | ||
RuleID: "Snowflake.Stream.AttemptedLoginByDisabledUser" | ||
Enabled: true | ||
ScheduledQueries: | ||
- Snowflake Attempted Login With Disabled User | ||
Severity: Low | ||
Reports: | ||
MITRE ATT&CK: | ||
- TA0001:T1078.004 | ||
Description: > | ||
Detects when a login is attempted by a disabled user account. | ||
Tags: | ||
- Snowflake | ||
- Behavior Analysis | ||
- Initial Access:Valid Accounts:Cloud Accounts | ||
Tests: | ||
- Name: Login by Disabled User | ||
ExpectedResult: true | ||
Log: | ||
{ | ||
"p_source_label": "SF-Prod", | ||
"user": { | ||
"CREATED_ON": "2024-10-09 19:43:05.083000000", | ||
"DEFAULT_ROLE": "PANTHER_AUDIT_VIEW_ROLE", | ||
"DISABLED": true, | ||
"DISPLAY_NAME": | ||
"FORMER_ADMIN", | ||
"EXT_AUTHN_DUO": false, | ||
"HAS_MFA": false, | ||
"HAS_PASSWORD": true, | ||
"HAS_RSA_PUBLIC_KEY": false, | ||
"LAST_SUCCESS_LOGIN": "2024-10-09 20:59:00.043000000", | ||
"LOGIN_NAME": "FORMER_ADMIN", | ||
"MUST_CHANGE_PASSWORD": false, | ||
"NAME": "FORMER_ADMIN", | ||
"OWNER": "ACCOUNTADMIN", | ||
"SNOWFLAKE_LOCK": false, | ||
"USER_ID": "51" | ||
}, | ||
"USER_NAME": "FORMER_ADMIN" | ||
} |
23 changes: 23 additions & 0 deletions
23
queries/snowflake_queries/snowflake_attempted_login_by_disabled_user_query.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
AnalysisType: scheduled_query | ||
QueryName: Snowflake Attempted Login With Disabled User | ||
Enabled: false | ||
Description: > | ||
Returns instances where a disabled user's login credentials were used in a login | ||
attempt. | ||
Tags: | ||
- Snowflake | ||
Query: | | ||
with disabled_users as ( | ||
select DATA as USER from panther_logs.public.snowflake_users_variant | ||
where USER:DISABLED = true | ||
), | ||
logins as ( | ||
select * from | ||
panther_logs.public.snowflake_loginhistory | ||
where p_occurs_since('24h', , p_parse_time) | ||
) | ||
select * from logins join disabled_users | ||
on logins.USER_NAME = disabled_users.USER:NAME | ||
Schedule: | ||
RateMinutes: 1440 | ||
TimeoutMinutes: 2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
def rule(_): | ||
return True | ||
|
||
|
||
def title(event): | ||
return f"{event.get('p_source_label', '<UNKNOWN SOURCE>')}: Suspicious Application Session" | ||
|
||
|
||
def dedup(event): | ||
return "-".join( | ||
( | ||
event.get("client_application", "<UNKNOWN APP>"), | ||
event.get("client_os", "<UNKNOWN OS>"), | ||
event.get("client_os_version", "<UNKNOWN VERSION>"), | ||
) | ||
) |
30 changes: 30 additions & 0 deletions
30
queries/snowflake_queries/snowflake_suspicious_session.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
AnalysisType: scheduled_rule | ||
Filename: snowflake_suspicious_session.py | ||
RuleID: "Snowflake.Stream.SuspiciousSession.UnusualApp" | ||
DisplayName: Suspicious Snowflake Sessions - Unusual Application | ||
Enabled: true | ||
ScheduledQueries: | ||
- "Suspicious Snowflake Sessions - Unusual Application" | ||
Severity: Low | ||
Reports: | ||
MITRE ATT&CK: | ||
- TA0001:T1078.004 | ||
Description: Detects unusual (non-common) applications and client characteristics | ||
that have been used to connect to a Snowflake account | ||
DedupPeriodMinutes: 1440 | ||
Tags: | ||
- Snowflake | ||
- Behavior Analysis | ||
- Initial Access:Valid Accounts:Cloud Accounts | ||
Tests: | ||
- Name: New Session | ||
ExpectedResult: true | ||
Log: | ||
{ | ||
"p_source_id": "26c3f2be-005e-443a-90cb-f623522f37a2", | ||
"p_source_label": "SF Prod", | ||
"client_application": "Snowflake Web App", | ||
"first_seen": "2024-10-09 14:48:33.284", | ||
"last_seen": "2024-10-09 15:01:13.492", | ||
"n_sessions": 83 | ||
} |
33 changes: 33 additions & 0 deletions
33
queries/snowflake_queries/snowflake_suspicious_session_query.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
AnalysisType: scheduled_query | ||
QueryName: Suspicious Snowflake Sessions - Unusual Application | ||
Enabled: false | ||
Description: This query can be used for the detection of unusual, non-common applications | ||
and client characteristics that had been used to connect to the Snowflake account, | ||
using a comparison to the previous usage baseline. | ||
Schedule: | ||
RateMinutes: 1320 | ||
TimeoutMinutes: 2 | ||
Tags: | ||
- Snowflake | ||
- Configuration Required | ||
Query: | | ||
-- Adjustments as follows: | ||
-- adjust n_sessions threshold on line 18 as needed | ||
-- adjust baseline lookback period on line 16 as desired | ||
-- adust recent lookpack period on line 19 as desired | ||
-- adjust scheduled query period to be 2 hrs shorter than the lookback window on line 19 | ||
select | ||
CLIENT_ENVIRONMENT:APPLICATION as client_application, | ||
CLIENT_ENVIRONMENT:OS as client_os, | ||
CLIENT_ENVIRONMENT:OS_VERSION as client_os_version, | ||
min(CREATED_ON) as first_seen, | ||
max(CREATED_ON) as last_seen, | ||
count(*) as n_sessions, | ||
p_source_id, | ||
p_source_label | ||
from panther_logs.public.snowflake_sessions | ||
where p_occurs_since(90d) | ||
group by client_application, client_os, client_os_version, p_source_id, p_source_label | ||
having n_sessions > 50 | ||
and first_seen > timeadd('day', -10, p_current_timestamp()) | ||
order by n_sessions desc |
8 changes: 8 additions & 0 deletions
8
queries/snowflake_queries/snowflake_user_query_volume_spike.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
def rule(_): | ||
return True | ||
|
||
|
||
def title(event): | ||
username = event.get("user_name", "<UNKNOWN USER>") | ||
source = event.get("p_source_label", "<UNKNOWN SOURCE>") | ||
return f"{source}: Abnormally large query volume from user {username}" |
36 changes: 36 additions & 0 deletions
36
queries/snowflake_queries/snowflake_user_query_volume_spike.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
AnalysisType: scheduled_rule | ||
Filename: snowflake_user_query_volume_spike.py | ||
RuleID: "Snowflake.Stream.UserDailyQueryVolumeSpike" | ||
DisplayName: "Snowflake User Daily Query Volume Spike" | ||
Enabled: true | ||
ScheduledQueries: | ||
- "Snowflake User Daily Query Volume Spike" | ||
Severity: Low | ||
Reports: | ||
MITRE ATT&CK: | ||
- TA0010:T1567 | ||
Description: > | ||
Returns instances where a user's cumulative daily query volume is much larger than | ||
normal. Could indicate exfiltration attempts. | ||
Runbook: > | ||
Review the user's query history for the past day. Identify any large queries | ||
and determine if any data was accessed that shouldn't be. | ||
Tags: | ||
- Snowflake | ||
- Behavior Analysis | ||
- Exfiltration:Exfiltration Over Web Service | ||
Tests: | ||
- Name: High Query Volume | ||
ExpectedResult: true | ||
Log: | ||
{ | ||
"p_source_id": "26c3f2be-005e-443a-90cb-f623522f37a2", | ||
"p_source_label": "SF Prod", | ||
"daily_bytes": 1376806.35210866, | ||
"mean": 729289.7142857143, | ||
"std_dev": 206110.99016770572, | ||
"tend": "2024-10-21 22:09:13.47Z", | ||
"tstart": "2024-10-20 22:09:13.47Z", | ||
"USER_NAME": "ZEEBLE_BROMBUS", | ||
"zscore": 3.141592, | ||
} |
77 changes: 77 additions & 0 deletions
77
queries/snowflake_queries/snowflake_user_query_volume_spike_query.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
AnalysisType: scheduled_query | ||
QueryName: "Snowflake User Daily Query Volume Spike" | ||
Enabled: false | ||
Description: > | ||
Returns instances where a user's cumulative daily query volume is much larger than | ||
normal. Could indicate exfiltration attempts. | ||
Query: | | ||
with t as ( | ||
select | ||
dateadd('day', -1-seq4(), p_current_timestamp()) as tstart, | ||
dateadd('day', -seq4(), p_current_timestamp()) as tend | ||
from table(generator(rowcount => 90)) | ||
), | ||
data as ( | ||
select | ||
user_name, | ||
end_time as t, | ||
bytes_written_to_result as n_bytes, | ||
p_source_id, | ||
p_source_label | ||
from panther_logs.public.snowflake_queryhistory | ||
where p_occurs_since('90d', , end_time) | ||
), | ||
dimensions as ( | ||
select distinct user_name, p_source_id from data | ||
), | ||
axes as ( | ||
select * from t cross join dimensions | ||
), | ||
histogram as ( | ||
select | ||
sum(data.n_bytes) as daily_bytes, | ||
data.p_source_label, | ||
axes.tstart, | ||
axes.tend, | ||
axes.user_name, | ||
axes.p_source_id | ||
from data join axes | ||
on | ||
p_occurs_between(axes.tstart, axes.tend, data, t) | ||
and data.user_name = axes.user_name | ||
and data.p_source_id = axes.p_source_id | ||
group by ( | ||
axes.p_source_id, | ||
data.p_source_label, | ||
axes.tstart, | ||
axes.tend, | ||
axes.user_name | ||
) | ||
), | ||
stats as ( | ||
select | ||
avg(daily_bytes) as mean, | ||
stddev(daily_bytes) as std_dev, | ||
user_name, | ||
p_source_id, | ||
p_source_label | ||
from histogram | ||
group by | ||
p_source_id, | ||
p_source_label, | ||
user_name | ||
) | ||
select | ||
abs(histogram.daily_bytes - stats.mean) / COALESCE(NULLIF(stats.std_dev, 0), 1) as zscore, | ||
histogram.*, | ||
stats.mean, | ||
stats.std_dev | ||
from histogram join stats on | ||
histogram.user_name = stats.user_name and | ||
histogram.p_source_id = stats.p_source_id, | ||
where p_occurs_since('12h', histogram, tend) | ||
and zscore > 3 | ||
and histogram.daily_bytes > 1000000 -- Minimum 1MB threshold | ||
Schedule: | ||
CronExpression: "0 0 * * *" | ||
TimeoutMinutes: 3 |
25 changes: 25 additions & 0 deletions
25
queries/snowflake_queries/snowflake_user_query_volume_spike_threat_hunting.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
AnalysisType: saved_query | ||
QueryName: "Snowflake User Daily Query Volume Spike - Threat Hunting" | ||
Description: This query returns the most voluminous queries executed by a specific | ||
user over the past 48 hours. | ||
Tags: | ||
- Snowflake | ||
- Threat Hunting | ||
Query: |- | ||
-- pragma: template | ||
-- Adjust 'username' and 'source_label' values as needed | ||
{% set username = 'PANTHER_AUDIT_VIEW_USER' %} | ||
{% set source_label = 'SF-Ben' %} | ||
select | ||
p_event_time, | ||
BYTES_WRITTEN_TO_RESULT, | ||
QUERY_TEXT, | ||
QUERY_TAG, | ||
EXECUTION_STATUS, | ||
QUERY_ID, | ||
from panther_logs.public.snowflake_queryhistory | ||
where p_occurs_since('48h') | ||
and USER_NAME = '{{username}}' | ||
and p_source_label = '{{source_label}}' | ||
order by BYTES_WRITTEN_TO_RESULT desc |