In May and June 2024, Snowflake disclosed that a threat actor designated UNC5537 (with substantial overlap with Scattered Spider) had compromised customer accounts at more than 160 organisations — including Ticketmaster, Santander Bank, Advance Auto Parts, and LendingTree — and exfiltrated significant volumes of customer data. The intrusion method was not a vulnerability in Snowflake’s platform. It was credential stuffing against accounts with no MFA enforced.
Snowflake environments now feature prominently in data breach investigations and threat actor tradecraft into 2026. Understanding how these attacks work and where Snowflake’s default configuration leaves gaps is essential for any organisation using the platform for data warehousing.
How the Campaign Worked
UNC5537 did not attack Snowflake itself. They attacked the accounts of Snowflake customers using credentials obtained from infostealer malware logs. The sequence:
- Credentials for Snowflake accounts are captured by infostealer malware (RedLine, Raccoon, Vidar) on employee or contractor machines, typically outside the corporate environment where endpoint security is absent.
- Those credentials are sold or appear in compiled credential databases accessible to threat actors.
- UNC5537 tested credentials against Snowflake’s public login endpoint. Without MFA, valid credentials provide immediate access.
- Once authenticated, they used Snowflake’s native tooling — SQL queries,
GET_DDL(),COPY INTOto external stages — to enumerate and exfiltrate data. - Exfiltrated data was staged and offered for sale or used for extortion.
Two configuration properties made this possible at scale: Snowflake’s legacy username/password authentication had no MFA requirement, and many accounts had no network policy restricting login to known IP ranges.
The Key Misconfigurations
1. No MFA Enforcement
Snowflake’s default configuration does not require MFA. Users can authenticate with username and password alone. This remains true in 2026 for legacy accounts unless an administrator has explicitly enforced MFA.
Check your current state:
-- Identify users who do not have MFA enrolled
SELECT name, login_name, has_mfa, has_password, disabled
FROM snowflake.account_usage.users
WHERE has_mfa = FALSE
AND disabled = FALSE
AND has_password = TRUE
ORDER BY name;
Enforce MFA via the SNOWFLAKE_SSO Okta/SAML integration or Snowflake’s native MFA:
-- Enforce MFA for a specific user
ALTER USER <username> SET MINS_TO_MFA_UNSET = 0;
-- Require MFA for all users in a role (account admin only)
ALTER ACCOUNT SET REQUIRE_MFA = TRUE;
The most robust approach is to federate Snowflake authentication with your enterprise IdP (Okta, Entra ID, Ping) via SAML/OIDC and enforce MFA at the IdP level. This also centralises access control and enables conditional access policies.
2. No Network Policy
Without a network policy, Snowflake accounts accept login attempts from any IP address. Credential stuffing attacks originate from a wide range of IP addresses; without IP restrictions there is no network-level barrier.
Create and apply a network policy:
-- Create a network policy allowing only your corporate egress IPs
CREATE NETWORK POLICY corporate_access_policy
ALLOWED_IP_LIST = ('203.0.113.0/24', '198.51.100.50')
COMMENT = 'Restrict Snowflake access to known corporate egress ranges';
-- Apply at account level
ALTER ACCOUNT SET NETWORK_POLICY = corporate_access_policy;
-- Or apply per user for admin accounts
ALTER USER <admin_username> SET NETWORK_POLICY = corporate_access_policy;
For organisations where users connect from dynamic or distributed IPs (remote workers without a VPN or split-tunnel configuration), pairing network policy with Snowflake Private Link or a Snowflake for VPN configuration is the appropriate approach.
3. Overly Permissive Roles
Snowflake’s role hierarchy defaults to granting SYSADMIN and SECURITYADMIN privileges broadly. Many deployments also accumulate grants over time, creating accounts with SELECT on all databases that should have narrower access.
Audit effective privileges:
-- Enumerate all grants to a role
SHOW GRANTS TO ROLE <role_name>;
-- Identify users with ACCOUNTADMIN
SELECT grantee_name, role
FROM snowflake.account_usage.grants_to_roles
WHERE role = 'ACCOUNTADMIN'
AND granted_to = 'ROLE'
AND deleted_on IS NULL;
-- Find users directly granted SYSADMIN
SELECT grantee_name
FROM snowflake.account_usage.grants_to_users
WHERE role = 'SYSADMIN'
AND deleted_on IS NULL;
Apply least privilege: service accounts used by ETL pipelines should have INSERT or COPY INTO on specific tables only, not broad SYSADMIN or SELECT on all schemas.
4. Unmonitored External Stages
During the UNC5537 campaign, attackers exfiltrated data by creating temporary external stages pointing to attacker-controlled S3 buckets or Azure Blob Storage containers, then using COPY INTO to move data there. This is a feature of Snowflake’s data loading/unloading capability, not a vulnerability, but it is frequently unmonitored.
Detect external stage creation and copy operations:
-- Query the last 30 days for COPY INTO operations to external stages
SELECT query_text, user_name, start_time, end_time
FROM snowflake.account_usage.query_history
WHERE query_type = 'COPY'
AND query_text ILIKE '%@%'
AND start_time >= DATEADD(day, -30, CURRENT_TIMESTAMP())
ORDER BY start_time DESC;
-- Find all external stages
SHOW STAGES IN ACCOUNT;
SELECT stage_name, stage_url, stage_type
FROM snowflake.account_usage.stages
WHERE stage_type = 'External Named'
AND deleted IS NULL;
Restrict external stage creation to specific roles (typically only your data engineering roles) and alert on new external stage creation or large COPY INTO operations outside business hours.
5. No Session Policies
Default Snowflake sessions do not time out aggressively. An attacker who captures a session token — or an employee who leaves a browser session open on an unmanaged device — maintains access indefinitely.
-- Create a strict session policy
CREATE SESSION POLICY strict_timeout
SESSION_IDLE_TIMEOUT_MINS = 15
SESSION_UI_IDLE_TIMEOUT_MINS = 15
COMMENT = 'Enforce 15-minute idle timeout for all sessions';
-- Apply at account level
ALTER ACCOUNT SET SESSION POLICY strict_timeout;
Detection and Monitoring
Snowflake Access History and Query History
SNOWFLAKE.ACCOUNT_USAGE.ACCESS_HISTORY is the primary detection table. It records every object accessed, by whom, and what they did. Enable it and ship it to your SIEM.
Key queries for threat hunting:
-- Identify large data reads by a single user outside normal hours
SELECT user_name,
SUM(bytes_read_from_result) AS bytes_read,
COUNT(*) AS query_count,
DATE_TRUNC('hour', query_start_time) AS hour
FROM snowflake.account_usage.access_history
WHERE query_start_time >= DATEADD(day, -7, CURRENT_TIMESTAMP())
AND HOUR(query_start_time) NOT BETWEEN 7 AND 19
GROUP BY 1, 4
HAVING bytes_read > 1073741824 -- 1 GB
ORDER BY bytes_read DESC;
-- New IP addresses logging in for existing users
SELECT user_name,
client_net_address,
MIN(event_timestamp) AS first_seen
FROM snowflake.account_usage.login_history
WHERE event_timestamp >= DATEADD(day, -30, CURRENT_TIMESTAMP())
AND is_success = 'YES'
GROUP BY 1, 2
HAVING first_seen >= DATEADD(day, -7, CURRENT_TIMESTAMP())
ORDER BY first_seen DESC;
-- Failed login spike (credential stuffing indicator)
SELECT user_name,
client_net_address,
COUNT(*) AS failed_attempts,
MIN(event_timestamp) AS first_attempt,
MAX(event_timestamp) AS last_attempt
FROM snowflake.account_usage.login_history
WHERE is_success = 'NO'
AND event_timestamp >= DATEADD(hour, -24, CURRENT_TIMESTAMP())
GROUP BY 1, 2
HAVING failed_attempts >= 10
ORDER BY failed_attempts DESC;
Snowflake Telemetry in SIEM
Snowflake’s ACCOUNT_USAGE schema retains data for 365 days. For real-time alerting, configure Snowflake to export logs to your SIEM using:
- Snowflake Connector for Splunk (official Splunkbase app)
- Azure Event Hub integration for Microsoft Sentinel
- Snowflake Log Export to S3 → CloudWatch or a third-party SIEM ingestion pipeline
Key tables to ingest: LOGIN_HISTORY, QUERY_HISTORY, ACCESS_HISTORY, STAGES, GRANTS_TO_ROLES, GRANTS_TO_USERS.
Hardening Checklist
| Control | Priority | Implementation |
|---|---|---|
| Enforce MFA or SSO with MFA | Critical | ALTER ACCOUNT SET REQUIRE_MFA = TRUE or SAML/OIDC federation |
| Apply network policy | Critical | Restrict to known corporate egress IPs |
| Remove unused accounts | High | Audit users table; disable immediately on offboarding |
| Least-privilege role design | High | No broad SYSADMIN for service accounts |
| Monitor external stage creation | High | Alert on new external stages |
| Apply session idle timeout | Medium | 15 minutes for most users |
| Enable and ship audit logs | Medium | LOGIN_HISTORY, QUERY_HISTORY, ACCESS_HISTORY to SIEM |
| Use Snowflake Private Link | Medium | Remove public endpoint exposure where possible |
Restrict COPY INTO to external | Medium | Role-based control; alert on large unload operations |
| Rotate credentials post-breach | Immediate | If any account was exposed to infostealers, rotate immediately |
Snowflake Private Link
For environments with strict data residency or network control requirements, Snowflake Private Link removes the public-facing login endpoint entirely. Traffic routes through your VPC/VNet to Snowflake’s network without traversing the public internet. This eliminates the IP-based credential stuffing attack surface by making the account unreachable from arbitrary internet sources.
Private Link does not replace MFA — you still need MFA or SSO for authentication. But it removes the public attack surface that UNC5537 exploited.