> For the complete documentation index, see [llms.txt](https://blog.pepperclipp.com/pepperclipp-public/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://blog.pepperclipp.com/pepperclipp-public/others/introducing-yetihunter-an-open-source-tool-to-detect-and-hunt-for-suspicious-activity-in-snowflake.md).

# Introducing YetiHunter: An open-source tool to detect and hunt for suspicious activity in Snowflake

## Summary

On May 30, 2024 Snowflake confirmed many clients were affected by an attacker leveraging compromised NHI credentials to perform data theft. In [their notice](https://community.snowflake.com/s/question/0D5VI00000Emyl00AB/detecting-and-preventing-unauthorized-user-access), Snowflake included [some indicators](https://community.snowflake.com/s/article/Communication-ID-0108977-Additional-Information) and suggested hunts. Our good friends at Mandiant also provided insight into the attacker (UNC5537) and more hunting opportunities in their [recent blog post](https://cloud.google.com/blog/topics/threat-intelligence/unc5537-snowflake-data-theft-extortion) as well.

To summarize the high level facts of the case:

* An attacker gained access to over 150 credentials (non-mfa) to client organizations of Snowflake
* Mandiant believes the attacker gained access to these credentials through a variety of infostealers
* The attacker leveraged these credentials to perform data theft for the purpose of extortion
* The earliest evidence of the attackers unauthorized access to a victim organization was on April 14, 2024

To see if your Snowflake instance has been compromised, Permiso has released YetiHunter – an open source tool combining several Indicators of Compromise published by the community. Reach out if you need any assistance in scanning your Snowflake environment.&#x20;

## Snowflake Hunting with YetiHunter

In order to ease the burden of hunting through Snowflake data for indicators, the Permiso team has created YetiHunter. YetiHunter combines the Indicators that [Snowflake](https://community.snowflake.com/s/article/Communication-ID-0108977-Additional-Information), [Mandiant](https://cloud.google.com/blog/topics/threat-intelligence/unc5537-snowflake-data-theft-extortion), [DataDog](https://securitylabs.datadoghq.com/articles/a-guide-to-threat-hunting-and-monitoring-in-snowflake/) and Permiso have into one easy to run script for triage.

YetiHunter performs the following queries:

| **Query Name**                    | **Description**                                                                                                                                                                                  | **Query**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |   |                 |   |            |   |             |   |           |   |       |   |             |   |                                                                                                                                                                                                                   |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | --------------- | - | ---------- | - | ----------- | - | --------- | - | ----- | - | ----------- | - | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| select\_all\_without\_where       | <p><code>select \*</code> queries that do not contain a <code>WHERE</code> clause.</p><p><br>Attackers often use simple select statements without extra filtering via WHERE clauses.<br><br></p> | <p>SELECT query\_id, user\_name, query\_text<br>FROM snowflake.account\_usage.query\_history<br>WHERE query\_text ILIKE 'SELECT \*'<br>AND query\_text NOT ILIKE '%WHERE%';</p>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |   |                 |   |            |   |             |   |           |   |       |   |             |   |                                                                                                                                                                                                                   |
| copy\_into\_select\_all           | <p><code>COPY INTO</code> and <code>select \*</code> in a single query.</p><p> </p><p>Attackers often attempt to combine these two commands to export all records to a temp location.</p>        | <p>SELECT query\_id, user\_name, query\_text<br>FROM snowflake.account\_usage.query\_history<br>WHERE query\_text ILIKE 'COPY INTO%'<br>AND query\_text ILIKE 'SELECT \*%';</p>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |   |                 |   |            |   |             |   |           |   |       |   |             |   |                                                                                                                                                                                                                   |
| show\_tables                      | <p>Performing a <code>show tables</code> query</p><p> </p><p><code>show tables</code> is a common recon command that attackers may use to understand the environment they are in.</p>            | <p>SELECT query\_id, user\_name, query\_text<br>FROM snowflake.account\_usage.query\_history<br>WHERE query\_text ILIKE 'SHOW TABLES';</p>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |   |                 |   |            |   |             |   |           |   |       |   |             |   |                                                                                                                                                                                                                   |
| create\_temp\_storage             | <p>Creating temp storage</p><p> </p><p>Attackers will often create a temporary storage location (usually with a generic name like test) as a staging location.</p>                               | <p>SELECT query\_id, user\_name, query\_text<br>FROM snowflake.account\_usage.query\_history<br>WHERE query\_text ILIKE 'CREATE TEMP%’</p>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |   |                 |   |            |   |             |   |           |   |       |   |             |   |                                                                                                                                                                                                                   |
| 10\_largest\_queries              | <p>Top 10 largest queries by rows\_produced</p><p> </p><p>Review queries that have produced a large amount of rows.</p>                                                                          | <p>SELECT query\_id, user\_name, query\_text, rows\_produced<br>FROM snowflake.account\_usage.query\_history<br>WHERE rows\_produced > 2000<br>ORDER BY rows\_produced DESC<br>LIMIT 10;</p>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |   |                 |   |            |   |             |   |           |   |       |   |             |   |                                                                                                                                                                                                                   |
| dbeaver\_used                     | DBEAVER is a common utility that is used by administrators and attackers alike to interact with many database types to include SNOWFLAKE                                                         | <p>SELECT<br>created\_on,<br>user\_name,<br>authentication\_method,<br>PARSE\_JSON(client\_environment) :APPLICATION :: STRING AS client\_application,<br>PARSE\_JSON(client\_environment) :OS :: STRING AS client\_os,<br>PARSE\_JSON(client\_environment) :OS\_VERSION :: STRING AS client\_os\_version,<br>session\_id<br>FROM<br>snowflake.account\_usage.sessions,<br>WHERE<br>PARSE\_JSON(CLIENT\_ENVIRONMENT):APPLICATION = 'DBeaver\_DBeaverUltimate'<br>ORDER BY CREATED\_ON;</p>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |   |                 |   |            |   |             |   |           |   |       |   |             |   |                                                                                                                                                                                                                   |
| accountadmin\_changes             | Review recent ADMIN role grants                                                                                                                                                                  | <p>select user\_name                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |   | ' granted the ' |   | role\_name |   | ' role on ' |   | end\_time |   | ' \[' |   | query\_text |   | ']' as Grants<br>from snowflake.account\_usage.query\_history where execution\_status = 'SUCCESS'<br>and query\_type = 'GRANT' and<br>query\_text ilike '%grant%accountadmin%to%'<br>order by end\_time desc;</p> |
| impactful\_modifications          | This query will list many impactful modifications that occurred in snowflake, review for any suspicious modifications                                                                            | <p>SELECT<br>start\_time,<br>user\_name,<br>role\_name,<br>query\_type,<br>query\_text<br>FROM snowflake.account\_usage.query\_history<br>WHERE execution\_status = 'SUCCESS'<br>AND query\_type NOT in ('SELECT')<br>AND query\_type NOT in ('SHOW')<br>AND query\_type NOT in ('DESCRIBE')<br>AND (query\_text ILIKE '%create role%'<br>OR query\_text ILIKE '%manage grants%'<br>OR query\_text ILIKE '%create integration%'<br>OR query\_text ILIKE '%alter integration%'<br>OR query\_text ILIKE '%create share%'<br>OR query\_text ILIKE '%create account%'<br>OR query\_text ILIKE '%monitor usage%'<br>OR query\_text ILIKE '%ownership%'<br>OR query\_text ILIKE '%drop table%'<br>OR query\_text ILIKE '%drop database%'<br>OR query\_text ILIKE '%create stage%'<br>OR query\_text ILIKE '%drop stage%'<br>OR query\_text ILIKE '%alter stage%'<br>OR query\_text ILIKE '%create user%'<br>OR query\_text ILIKE '%alter user%'<br>OR query\_text ILIKE '%drop user%'<br>OR query\_text ILIKE '%create\_network\_policy%'<br>OR query\_text ILIKE '%alter\_network\_policy%'<br>OR query\_text ILIKE '%drop\_network\_policy%'<br>OR query\_text ILIKE '%copy%'<br>)<br>ORDER BY end\_time desc;</p> |   |                 |   |            |   |             |   |           |   |       |   |             |   |                                                                                                                                                                                                                   |
| least\_common\_applications\_used | Applications in SNOWFLAKE are much like user-agents. This query will show the least common applications used in your environment. Review the list for any abnormal applications                  | <p>SELECT<br>COUNT(\*) AS client\_app\_count,<br>PARSE\_JSON(client\_environment) :APPLICATION :: STRING AS client\_application,<br>PARSE\_JSON(client\_environment) :OS :: STRING AS client\_os,<br>PARSE\_JSON(client\_environment) :OS\_VERSION :: STRING AS client\_os\_version<br>FROM<br>snowflake.account\_usage.sessions sessions<br>WHERE<br>1 = 1<br>AND sessions.created\_on >= '2024-04-01'<br>GROUP BY<br>ALL<br>ORDER BY<br>1 DESC;</p>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |   |                 |   |            |   |             |   |           |   |       |   |             |   |                                                                                                                                                                                                                   |
| malicious\_ips                    | This will check all of the known observed malicious IPs that SNOWFLAKE and Mandiant have released associated with the UNC5537 campaign.                                                          |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |   |                 |   |            |   |             |   |           |   |       |   |             |   |                                                                                                                                                                                                                   |
| frostbite\_used                   | Frostbite is the Mandiant name for one of the applications known used by UNC5537. This query looks for it’s usage.                                                                               | <p>SELECT<br>created\_on,<br>user\_name,<br>authentication\_method,<br>PARSE\_JSON(client\_environment) :APPLICATION :: STRING AS client\_application,<br>PARSE\_JSON(client\_environment) :OS :: STRING AS client\_os,<br>PARSE\_JSON(client\_environment) :OS\_VERSION :: STRING AS client\_os\_version,<br>session\_id<br>FROM<br>snowflake.account\_usage.sessions,<br>WHERE<br>PARSE\_JSON(CLIENT\_ENVIRONMENT):APPLICATION = 'rapeflake'<br>ORDER BY CREATED\_ON;</p>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |   |                 |   |            |   |             |   |           |   |       |   |             |   |                                                                                                                                                                                                                   |
| copy\_http                        | This query will identify any usage of `COPY INTO` with `HTTP` in the `query_text` as well                                                                                                        | <p>SELECT \*,<br>FROM<br>snowflake.account\_usage.query\_history<br>WHERE<br>CONTAINS(QUERY\_TEXT, 'COPY INTO') AND CONTAINS(QUERY\_TEXT, 'http')</p>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |   |                 |   |            |   |             |   |           |   |       |   |             |   |                                                                                                                                                                                                                   |
| get\_file                         | <p>After copying into temp storage, attackers will often use the GET command to retrieve that file. This looks for <code>GET</code> and <code>File</code>: in a single query<br><br><br></p>     | <p>SELECT \*,<br>FROM<br>snowflake.account\_usage.query\_history<br>WHERE<br>query\_text ilike '%GET' AND query\_text ilike '%file:%’</p>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |   |                 |   |            |   |             |   |           |   |       |   |             |   |                                                                                                                                                                                                                   |

### YetiHunter Details

<https://github.com/Permiso-io-tools/YetiHunter>&#x20;

### Installation

To install, the only thing needed, is to install the required libraries.

> ```
> python3 -m venv ./venv
> source venv/bin/activate
> python3 -m pip install -r requirements.txt
>
> ```

If you prefer to run in Docker check for installation instructions in our [README file](https://github.com/Permiso-io-tools/YetiHunter).

Then, just run the tool by running **yetihunter.py**:

> ```
> python3 yetihunter.py -h
> usage: yetihunter [-h] [-u USER] [-p PASSWORD] [-w WAREHOUSE] [-a ACCOUNT] [-d DATABASE] [-s SCHEMA] [-am {SSO,USERPASS}] [-cf CONFIG_FILE_PATH] [-gcf] [-o OUTPUT_DIRECTORY]
>
> Permiso Security has created a tool to query snowflake environments for evidence of compromise, based on indicators from Permiso and the community.
>
> options:
>   -h, --help            show this help message and exit
>
>   -u USER, --user USER  The Snowflake user to authenticate as. Even if the authentication is SSO, the email of the identity authenticating should be added.
>
>   -p PASSWORD, --password PASSWORD
>                         This field is not required if the authentication is SSO, but required if the authentication is USERPASS. If needed and the field is empty, an imput will popup to ask for the password.
>
>   -w WAREHOUSE, --warehouse WAREHOUSE
>                         The Snowflake warehouse to connect to.
>
>   -a ACCOUNT, --account ACCOUNT
>                         The Snowflake Account to connect to. It should be in the format of <organization>-<account>
>
>   -d DATABASE, --database DATABASE
>                         The database name to connect to.
>
>   -s SCHEMA, --schema SCHEMA
>                         The name of the schema inside the database. This can be empty.
>
>   -am {SSO,USERPASS}, --authentication-method {SSO,USERPASS}
>
>   -cf CONFIG_FILE_PATH, --config-file-path CONFIG_FILE_PATH
>                         If a config file is used, this option will have the path of the config file. No other flag is needed besides this.
>
>   -gcf, --generate-config-file
>                         If put, the tool will create an empty config file that can be filled by the user. The fields with null value can be left as null as they are not required.
>
>   -o OUTPUT_DIRECTORY, --output-directory OUTPUT_DIRECTORY
>                         The directory inside ./output/, where the query output files will be saved in.
>
> ```

&#x20;

### YetiHunter Usage

#### Authenticating

YetiHunter supports two (2) forms of authentication. `-am {SSO,USERPASS}, --authentication-method {SSO,USERPASS}`

* Username + password
* SSO

Authentication and database information can be passed via command line parameters or stored in a config. To make this process seamless, YetiHunter includes a config generator:

> ```
> -cf CONFIG_FILE_PATH, --config-file-path CONFIG_FILE_PATH
>                         If a config file is used, this option will have the path of the config file. No other flag is needed besides this.
>
> -gcf, --generate-config-file
>                         If put, the tool will create an empty config file that can be filled by the user. The fields with null value can be left as null as they are not required.!
>
> ```

&#x20;

#### YetiHunter Queries

The rest of the execution is done by the tool itself, based on the queries provided. YetiHunter has a list of queries on `queries/queries.json`, which the user can extend or update.

<figure><img src="/files/yaxBwQqIedfSi1sbg0j7" alt=""><figcaption></figcaption></figure>

At the end of the execution, a list of CSV files are created with the output of each query.

### YetiHunter Custom Queries

As mentioned, YetiHunter stores its queries on `queries/queries.json`. Each query has a `name`, a `description` and the `query` itself. Queries can be added, removed or modified according to the users’ needs and they will automatically get executed by the tool.

### YetiHunter Custom Malicious IPs

A list of known malicious IPs is provided to the users. That list resides in `queries/known_bad_ips` . Users can add new IPs to the list or swap the current ones for their list and the tool will continue to run. The list will be ingested on a rule that checks for logins coming from these IPs.

When there is a hit, a file called `login_from_malicious_ips.csv` will be created with the list of identities and their login info.
