Detection rules › Panther

Snowflake Password Spray

Status
Experimental
Severity
medium
Group by
CLIENT_IP
Log types
Snowflake.LoginHistory
Tags
Snowflake, Credential Access, Brute Force
Reference
https://docs.snowflake.com/en/sql-reference/account-usage/login_history
Source
github.com/panther-labs/panther-analysis

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

TacticTechniques
Credential AccessT1110.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.

FieldKindValues
ERROR_MESSAGEne
  • OVERFLOW_FAILURE_EVENTS_ELIDED
EVENT_TYPEeq
  • LOGIN
IS_SUCCESSeq
  • NO

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.

FieldSource
client_ipCLIENT_IP
user_nameUSER_NAME
client_typeREPORTED_CLIENT_TYPE
error_codeERROR_CODE
error_messageERROR_MESSAGE