Detection rules › Panther
Snowflake Password Spray
Detects password spraying attacks against Snowflake by tracking the number of distinct user accounts targeted by failed login attempts from the same source IP address within a short timeframe. Unlike brute force against a single account, password spraying distributes attempts across many accounts to evade lockout policies. This rule complements Snowflake.Stream.BruteForceByIp (same IP, any accounts) and Snowflake.PotentialBruteForceSuccess (brute force followed by confirmed login).
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Credential Access | T1110.003 Brute Force: Password Spraying |
Rule body yaml
AnalysisType: rule
RuleID: "Snowflake.Stream.PasswordSpray"
DisplayName: "Snowflake Password Spray"
Filename: snowflake_stream_password_spray.py
Enabled: true
Status: Experimental
LogTypes:
- Snowflake.LoginHistory
Severity: Medium
Threshold: 5
DedupPeriodMinutes: 60
Description: >
Detects password spraying attacks against Snowflake by tracking the number of distinct
user accounts targeted by failed login attempts from the same source IP address within
a short timeframe. Unlike brute force against a single account, password spraying
distributes attempts across many accounts to evade lockout policies. This rule
complements Snowflake.Stream.BruteForceByIp (same IP, any accounts) and
Snowflake.PotentialBruteForceSuccess (brute force followed by confirmed login).
Runbook: |
1. Review the distinct usernames targeted from the source IP and determine if any are privileged accounts (ACCOUNTADMIN, SYSADMIN, SECURITYADMIN) which would elevate the risk significantly
2. Query Snowflake LoginHistory for all events with IS_SUCCESS = 'YES' from the same CLIENT_IP within the past 60 minutes to determine if any spray attempt succeeded — the alert context shows only the last triggering username, not all targeted accounts
3. Check if the source IP belongs to known anonymization infrastructure (VPNs, Tor exit nodes, cloud provider ranges) and cross-reference against other Snowflake tenants if applicable
Reference: https://docs.snowflake.com/en/sql-reference/account-usage/login_history
Tags:
- Snowflake
- Credential Access
- Brute Force
Reports:
MITRE ATT&CK:
- TA0006:T1110.003 # Credential Access: Password Spraying
Tests:
- Name: Match - Failed Login
ExpectedResult: true
Log:
{
"p_event_time": "2024-10-08 14:38:46.061000000",
"p_log_type": "Snowflake.LoginHistory",
"CLIENT_IP": "1.2.3.4",
"EVENT_ID": "393754014361778",
"EVENT_TIMESTAMP": "2024-10-08 14:38:46.061000000",
"EVENT_TYPE": "LOGIN",
"FIRST_AUTHENTICATION_FACTOR": "PASSWORD",
"IS_SUCCESS": "NO",
"RELATED_EVENT_ID": "0",
"REPORTED_CLIENT_TYPE": "OTHER",
"REPORTED_CLIENT_VERSION": "1.11.1",
"USER_NAME": "ckent@dailyplanet.org"
}
- Name: Match - Failed Login Different User Same IP
ExpectedResult: true
Log:
{
"p_event_time": "2024-10-08 14:39:00.000000000",
"p_log_type": "Snowflake.LoginHistory",
"CLIENT_IP": "1.2.3.4",
"EVENT_ID": "393754014361779",
"EVENT_TIMESTAMP": "2024-10-08 14:39:00.000000000",
"EVENT_TYPE": "LOGIN",
"FIRST_AUTHENTICATION_FACTOR": "PASSWORD",
"IS_SUCCESS": "NO",
"RELATED_EVENT_ID": "0",
"REPORTED_CLIENT_TYPE": "OTHER",
"REPORTED_CLIENT_VERSION": "1.11.1",
"USER_NAME": "luthor@lexcorp.com"
}
- Name: No Match - Successful Login
ExpectedResult: false
Log:
{
"p_event_time": "2024-10-08 14:38:46.061000000",
"p_log_type": "Snowflake.LoginHistory",
"CLIENT_IP": "1.1.1.1",
"EVENT_ID": "393754014361780",
"EVENT_TIMESTAMP": "2024-10-08 14:38:46.061000000",
"EVENT_TYPE": "LOGIN",
"FIRST_AUTHENTICATION_FACTOR": "PASSWORD",
"IS_SUCCESS": "YES",
"RELATED_EVENT_ID": "0",
"REPORTED_CLIENT_TYPE": "OTHER",
"REPORTED_CLIENT_VERSION": "1.11.1",
"USER_NAME": "ckent@dailyplanet.org"
}
- Name: No Match - Overflow Failure Event
ExpectedResult: false
Log:
{
"p_event_time": "2024-11-15 00:12:24.288000000",
"p_log_type": "Snowflake.LoginHistory",
"CLIENT_IP": "0.0.0.0",
"ERROR_CODE": 390156,
"ERROR_MESSAGE": "OVERFLOW_FAILURE_EVENTS_ELIDED",
"EVENT_ID": "16592193114297018",
"EVENT_TIMESTAMP": "2024-11-15 00:12:24.288000000",
"EVENT_TYPE": "LOGIN",
"IS_SUCCESS": "NO",
"RELATED_EVENT_ID": "0",
"REPORTED_CLIENT_TYPE": "OTHER",
"REPORTED_CLIENT_VERSION": "0",
"USER_NAME": "luthor@lexcorp.com"
}
- Name: Match - JWT Key Mismatch Downgraded to Info
ExpectedResult: true
Log:
{
"p_event_time": "2024-10-08 14:38:46.061000000",
"p_log_type": "Snowflake.LoginHistory",
"CLIENT_IP": "1.2.3.4",
"ERROR_CODE": 394304,
"ERROR_MESSAGE": "JWT_TOKEN_INVALID_PUBLIC_KEY_FINGERPRINT_MISMATCH",
"EVENT_ID": "393754014361782",
"EVENT_TIMESTAMP": "2024-10-08 14:38:46.061000000",
"EVENT_TYPE": "LOGIN",
"FIRST_AUTHENTICATION_FACTOR": "PASSWORD",
"IS_SUCCESS": "NO",
"RELATED_EVENT_ID": "0",
"REPORTED_CLIENT_TYPE": "OTHER",
"REPORTED_CLIENT_VERSION": "1.11.1",
"USER_NAME": "brucewayne@waynecorp.com"
}
- Name: No Match - Non-Login Event
ExpectedResult: false
Log:
{
"p_event_time": "2024-10-08 14:38:46.061000000",
"p_log_type": "Snowflake.LoginHistory",
"CLIENT_IP": "1.2.3.4",
"EVENT_ID": "393754014361781",
"EVENT_TIMESTAMP": "2024-10-08 14:38:46.061000000",
"EVENT_TYPE": "LOGOUT",
"IS_SUCCESS": "YES",
"RELATED_EVENT_ID": "0",
"REPORTED_CLIENT_TYPE": "OTHER",
"REPORTED_CLIENT_VERSION": "1.11.1",
"USER_NAME": "ckent@dailyplanet.org"
}
Detection logic
Condition
EVENT_TYPE eq "LOGIN"
IS_SUCCESS eq "NO"
ERROR_MESSAGE ne "OVERFLOW_FAILURE_EVENTS_ELIDED"
Indicators
Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.
| Field | Kind | Values |
|---|---|---|
ERROR_MESSAGE | ne |
|
EVENT_TYPE | eq |
|
IS_SUCCESS | eq |
|
Output fields
Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.
| Field | Source |
|---|---|
client_ip | CLIENT_IP |
user_name | USER_NAME |
client_type | REPORTED_CLIENT_TYPE |
error_code | ERROR_CODE |
error_message | ERROR_MESSAGE |