Overview
In this post we will try to emphasize the importance of not only looking for MITRE ATT&CK TTPs in an attempt to flesh out adversaries, but also consider the role corporate employees play as the human element in the cyber-breach equation.
Yes - the employees which were given permissions to, and who we trust with virtually every piece of information within our organization. Those employees can easily be used against the organization, knowingly or inadvertently. Below, we outline popular insider use cases with their matching Cortex XDR XQL queries, which you can implement in your day-to-day hunting routine.
Use case #1 - Overly-confident admin users
Here’s a fairly popular use case: an admin needs to access a certain file or service legitimately ,however, their non-admin user has no access to it.
What would the admin do?
- Go through the bureaucratic path and ask for permissions for their non-admin user.
- Remotely login to a server located in a different VLAN with their admin user and access the same file or service.
- Launch an elevated explorer.exe/cmd.exe and perform the task with their admin user on the personal laptop.
We can already envision most of the admins reading this post trying to hide a sly smile on their face while reading option 3.
The real problem with option 3 is that you are now logged in with your admin account on a machine that is more exposed to threats than your Domain Controller for example. Your admin account kerberos ticket now resides within memory and is vulnerable to multiple techniques that can be exploited by threat actors for elevation/lateral movement.
This is why the Cortex Managed Threat Hunting team has created the following query to assist you in locating workstations that are logged in with both admin/non-admin accounts in your organization.
config case_sensitive = false |//Setting the query to be case-insensitive.
dataset = xdr_data // Using the xdr dataset |filter event_type = PROCESS and event_sub_type = PROCESS_START and actor_effective_username not contains"system" and actor_effective_username not contains "network service" and actor_effective_username not contains "local service" and actor_effective_username not contains "DWM" and actor_effective_username not contains "_tmp" | fields agent_hostname as Hostname, actor_effective_username as Username | join (dataset = xdr_data |filter event_type = PROCESS and causality_actor_process_image_name ="explorer.exe" and actor_effective_username contains "adm") as AdminHosts Hostname = AdminHosts.agent_hostname | dedup Hostname ,Username | sort asc Hostname, asc Username |
A few notes regarding the query:
- It is case insensitive.
- Uses the “join” function to join between hosts where admin users have logged into and other users as well.
- Drops built in user accounts.
- De-duplicates the results based on Hostname + username logic.
Tips for query enhancement:
- Filter out all hosts where admin users are allowed to login. (agent_hostname != “*<Naming convention here>*")
- Make sure to target your admin account naming convention appropriately. (actor_effective_username contains "<Naming convention here>")
This query will assist you in targeting those admin users who are possibly abusing their admin accounts on their corporate machines rather than using them to perform administrative tasks only.
Our recommendations in this case are as follows:
- Implement corporate policy where administrative users are to be logged only in dedicated secured VLANs.
- Educate admin users on WHY this is a real problem to the organization if indeed such a machine is breached.
- Schedule the query to run every week to map new results from it.
Use case #2 - Uploading somewhere?
Cloud storage is becoming more affordable these days, and most organizations prefer using corporate cloud storage over traditional on-prem ones.
With that said, have you ever thought about a user that is logging into their own cloud account and syncing corporate files to it?
Part of the Cortex Managed Threat Hunting team tasks is to watch for anomalies in terms of uploads that are going into the cloud, essentially leaving the organization with no control of them.
(Unlike other queries mentioned above, this query would have to include a few manipulations to match your organization)
config case_sensitive = false |
dataset = xdr_data // Using the xdr dataset | filter event_type = NETWORK and (actor_process_image_name contains "googledrive" or actor_process_image_name contains "onedrive" or actor_process_image_name contains "dropbox" or actor_process_image_name contains "baidu") and (action_external_hostname != "") | fields action_upload, action_remote_ip as remote_ip, action_external_hostname as remote_hostname, actor_process_image_name as process_name, actor_causality_id, actor_process_image_sha256 as sha_256, agent_hostname as Hostname, actor_effective_username as Username // Selecting the relevant fields | join (dataset = xdr_data // Using the xdr dataset | filter event_type = NETWORK and (actor_process_image_name contains "googledrive" or actor_process_image_name contains "onedrive" or actor_process_image_name contains "dropbox" or actor_process_image_name contains "baidu") and (action_external_hostname != "") | fields action_upload, action_remote_ip as remote_ip, action_external_hostname as remote_hostname, actor_process_image_name as process_name, actor_causality_id, actor_process_image_sha256 as sha_256, agent_hostname as Hostname, actor_effective_username as Username // Selecting the relevant fields | join (dataset = xdr_data | filter event_type = NETWORK and (actor_process_image_name contains "googledrive" or actor_process_image_name contains "onedrive" or actor_process_image_name contains "dropbox" or actor_process_image_name contains "baidu") and (action_external_hostname != "") | fields action_upload, action_remote_ip as remote_ip, action_external_hostname as remote_hostname, actor_process_image_name as process_name, actor_causality_id, actor_process_image_sha256 as sha_256, agent_hostname as Hostname, actor_effective_username as Username // Selecting the relevant fields | comp sum(action_upload) as total_upload by process_name, remote_hostname, Hostname , Username // Summing the total upload by process + ip + host |filter total_upload > 104857600) as Uploading_Agents Hostname = Uploading_Agents.Hostname | comp count(Username) as counter by process_name, remote_hostname, Hostname // Summing the total upload by process + ip + host | sort asc counter) as Upload_agent Hostname = Upload_agent.Hostname | comp sum(action_upload) as total_upload by process_name, remote_hostname, Hostname , Username // Summing the total upload by process + ip + host | sort desc total_upload // Sorting by total upload |
A few notes regarding the query:
- It is case insensitive.
- Looks for executables of popular cloud services: “Google Drive”, “Microsoft OneDrive”, “Dropbox” and “Baidu Drive”
- Outputs a list of all “Hostname\User” which uploaded data to the cloud.
Tips for query enhancement:
- Filter out authorized Cloud Client to reduce the amount of false positives.
- When targeting “Microsoft Onedrive” add the following filter to target upload towards non-corporate account (action_external_hostname contains “users.storage.live.com”)
Our recommendations in this case are as follows:
- Block any executables of non-authorized cloud clients to prevent future uploads.
- Educate users on the risks while uploading corporate data to personal cloud.
- Schedule the query to run every week to map new results from it.
Use case #3 - passwords.xlsx
Let’s face it. When it comes to password management, collectively we have a problem. Not only do we have too many passwords to remember, but those passwords are getting more complex, and more restrictive. It’s pretty clear to all of us in the industry that these policies are needed, however, not so much for our users with all the cumbersome requirements around password policies.
Hence in an attempt to keep track of all of them, a user will go on and create an innocent file called “passwords.txt” or “passwords.docx” to store all of their passwords in a single place for future reference. The more sophisticated users will even make password protected files.
The fundamental problem here actually relies on us, the security professionals who failed in educating the user on WHY is it so dangerous to store those credentials in plain text. But I guess properly educating users to Security Awareness is a topic for another post so I’ll just say it once:
“Saying ‘Don’t’ to a user is great, Saying ‘Don’t’ to a user and explaining why is far better!”
So the user has created this file, yet why is it so risky? It’s because this is considered to be low hanging fruit from a threat actor's point of view on their initial access to a machine. A plaintext password can save a whole lot of time for an attacker who attempts to elevate his session or lateral move within the organization.
This is the reason the Cortex Managed Threat Hunting team has created the following query that will assist you with hunting for cleartext passwords containing files within your organization.
config case_sensitive = false | //Setting the query to be case-insensitive.
dataset = xdr_data // Using the xdr dataset | filter action_file_name contains "password" and (action_file_name contains ".doc" or action_file_name contains ".xls" or action_file_name contains ".txt" or action_file_name contains ".csv") and action_file_path not contains "chrome" and action_file_path not contains "firefox" and action_file_name not contains "~$" and action_file_name not contains ".lnk" and action_file_name not contains ":Zone.Identifier" and actor_effective_username not contains "system" and actor_process_image_name not contains "chrome.exe" and actor_process_image_name not contains "cmd.exe" and actor_process_image_name not contains "java.exe" and actor_process_image_name not contains "searchprotocolhost.exe" and action_file_path contains "users" |fields action_file_name as FileName, action_file_path as File_Path, actor_effective_username as User, agent_hostname as Hostname, actor_process_image_name as Acting_Process | dedup FileName , Hostname | sort asc User, asc Hostname |
A few notes regarding the query:
- It is case insensitive.
- Focuses on the following file extensions: .xls, .doc*. .txt, .csv.
- Focuses on the local profile folders.
- Excludes many False Positive results coming from processes and files naming conventions.
- De-duplicates the results based on Path + hostname logic.
Tips for query enhancement:
- Adding more file extensions regularly used in your environment. (action_file_name contains ".<file extension here>")
- Adding the “password” word in your user's native language.
e.g. (action_file_name contains "Passwort" or action_file_name contains “Passwörter”)
- Adding home folders which are located on Network Attached Storage devices. (action_file_path contains "desktop" or action_file_path contains "<your home folder naming convention >")
Screenshot of expected results:
![screenshot of expected results](https://www.paloaltonetworks.com/blog/wp-content/uploads/2021/06/word-image-86.png)
While this is far from being a sophisticated hunting technique, you would be surprised by the sheer amount of users (including privileged ones!) and files we were able to locate by utilizing this method.
Our recommendations in this case are as follows:
- Implement a corporate wide password vault solution.
- Eliminate any use of files containing clear-text passwords.
- Educate users on the risks associated with using such files.
- Schedule the query to run every week to map new results from it.
Summary
In conclusion, the Threat Hunters’ job is far from being routine and repetitive.
A good threat hunter will always strive to look for anomalies and events that aren’t necessarily caught by security products due to their non-malicious nature.
There’s nothing wrong with logging into a machine with a Domain Admin account - unless it’s a regular workstation furthermore it is recommended that you use a dedicated software to manage privileged accounts of any type.
There’s nothing wrong with uploading a file to the cloud - unless it’s a private account and not private company information.
There’s also nothing wrong with storing passwords on a plain text file (OK, we couldn’t find an “Unless” here.) It’s totally wrong to store passwords in a plain text file no matter what the situation is.
And all of those things are probably unintentionally performed by users in your organization.
We hope the provided XQL queries will assist you in locating and remediating them.
Happy hunting!
For more information on Managed Threat Hunting:
Download our Cortex XDR Managed Threat Hunting Solution Brief
Download our Cortex XDR Whitepaper