❌

Normal view

There are new articles available, click to refresh the page.
Before yesterdayNVISO Labs

Scaling your threat hunting operations with CrowdStrike and PSFalcon

13 December 2023 at 08:00

Introduction

Most modern day EDRs have some sort of feature which allows blue teamers to remotely connect to hosts with an EDR agent/sensor installed, to aid in their investigation of incidents. In CrowdStrike, this is called Real Time Response, and it provides a wide range of capabilities, from executing built-in commands like ipconfig and netstat to running your own PowerShell scripts.

In this blog post, I’ll showcase how CrowdStrike’s PSFalcon PowerShell module can be used to execute RTR commands on multiple hosts at once for the purpose of threat hunting. I’ll also be providing the code for the threat hunting script, and by the end of this blog you will be able to use the script to pull registry run keys, scheduled tasks, WMI subscriptions, startup folder files, and services from multiple machines, to uncover hidden persistence mechanisms. Attackers may establish persistence in your environment without being detected, as such hunting for some of the techniques they use could uncover a potential breach. You can find the Persistence-Hunter script here: https://github.com/NVISOsecurity/blogposts/blob/master/Persistence%20Hunter/Persistence-Hunter.ps1

Getting started

Before interacting with CrowdStrike’s Oauth2 API via PSFalcon, you will need PowerShell installed and a valid API client (which consists of a ClientID and a secret), that you can create via this link https://falcon.crowdstrike.com/api-clients-and-keys/clients. You can get an API client yourself if you have the Falcon administrator role, otherwise an administrator has to provide you with one. Make sure that your client has the Real time response (admin): Write permission enabled. After your API client is created, you have to install the PSFalcon module. You can find instructions on that through this link https://github.com/CrowdStrike/psfalcon/wiki/Installation,-Upgrade-and-Removal#use-the-powershell-gallery. Once that is done, run Show-FalconModule in a PowerShell prompt to verify that everything is correctly installed.

Since API keys could provide someone with a great deal of access, here are some best practices to keep in mind when handling them:

  1. Principle of least privilege; Don’t over assign privileges and rights to your key that are not necessary for the work you will be doing.
  2. Keep your key (ClientID and secret) stored somewhere safe, preferably a password manager.
  3. Don’t hardcode your key in a script, for obvious reasons.
  4. Don’t share your key with anyone, treat it as you would treat a password.

Workflow and script details

Persistence-Hunter.ps1 utilizes the PSFalcon module in order to query multiple hosts at once. It initiates Real Time Response sessions to each host in a specific group simultaneously and checks for persistence mechanisms that indicate if a host might be compromised or not.

Invoke-FalconRtr is used to initiates RTR sessions. Depending on the parameter provided, it will start a Real-time Response session with:

  • -GroupId: Members of the specified host group identifier
  • -HostId: A single host identifier
  • -HostIds: An array containing one or more host identifiers

Example command:

Invoke-FalconRtr -Command 'runscript' -Argument $Arguments -Timeout 60 -GroupId $group_ID

In this case, Invoke-FalconRtr was supplied with the with the following parameters:

  • runscript: To run a PowerShell script
  • Arguments: the Base64 commands about to be executed
  • Timeout: timeout limit
  • Group_ID: The ID of the host group

Sadly, PSFalcon does not understand host group names, so you need to convert the host group name to the corresponding host group identifier (GroupId).

How do I go from group name to GroupId?

You can do it manually via PSFalcon. Let’s say you want to find the GroupId of the following group:

Domain - Workstations

First, you need to turn all of the characters into lowercase:

$GroupName = 'Domain - Workstations'.ToLower()

You can get the value of GroupName in a PowerShell prompt to see if it worked by typing $GroupName:

domain - workstations

Then you need to use the following PSFalcon cmdlet:

$Id = Get-FalconHostGroup -Filter "name:'$GroupName'"

Once again you can get the value of id in a PowerShell prompt by typing $Id :

k43b…………………..d9120

The value of id is your GroupId, which is a 32-character long string. The GroupId parameter can then be supplied to the Persistence-Hunter script.

What are the commands that get executed via the script?

1. Registry keys (MITRE T1547.001):
gi -path 'Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run\' -ea silentlycontinue | out-string ;
gi -path 'Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce\' -ea SilentlyContinue | out-string;
gi -path 'Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run\' -ea silentlycontinue | out-string;
2. Scheduled tasks: (MITRE T1053.005):
schtasks /query /fo csv /v | convertfrom-csv | select TaskName, 'Task To Run', Author | fl | out-string;                                
3. Startup folder (MITRE T1547.001):
gci -path 'C:\Users\*\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\*' -ea silentlycontinue | select fullname,Length,CreationTime,LastWriteTime,LastAccessTime,Mode | fl | out-string;
gci -path 'C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\*' -ea silentlycontinue | select fullname,Length,CreationTime,LastWriteTime,LastAccessTime,Mode | fl | out-string;
4. WMI (MITRE T1546.003):
Get-WmiObject -namespace 'root\subscription' -class __EventConsumer | fl | out-string
Get-WmiObject -namespace 'root\subscription' -class __EventFilter | fl | out-string
Get-WmiObject -namespace 'root\subscription' -class __FilterToConsumerBinding | fl | out-string
5. Services (MITRE T1543.003):
gwmi win32_service |select name,pathname,state,status,startmode| fl | out-string;

How can I modify the commands or run other commands?

The commands need to be Base64 encoded since they are custom scripts to be executed. If you want to run your own PowerShell commands, you can use the following snippet to Base64 encode them:

$EncodedScript = [Convert]::ToBase64String(
        [System.Text.Encoding]::Unicode.GetBytes((Get-Content -Path $Path -Raw)))

Running the script

Before running the script, you’ll need to edit line 39 to include your group ID, and change the group name in line 42 as well. With that done, you are ready to run it via the command line. The script will produce 5 different csv files, one for each technique mentioned above. Happy hunting!

Conclusion

In this blog post, we looked into how the PSFalcon module can be leveraged in order to execute multiple commands in a group of hosts in CrowdStrike for threat hunting purposes.

This script has assisted me in the following use cases:

  1. Identify persistence via registry run keys in a host with a crypto miner infection, which was not detected by the CrowdStrike agent since it was a pre-sensor malware infection and the host had not been rebooted in order for a detection to have triggered.
  2. Identify plaintext credentials in backup related scheduled tasks, also something that CrowdStrike did not generate an alert on, since it is not malicious per se, but could be abused by attackers.

All in all, this code and the methodology presented could be modified to execute any PowerShell command in a group of hosts, so feel free to experiment with your own commands, whether it is threat hunting, system hardening or whatever else you want to do.

Dimitris Binichakis

Dimitris is a senior cybersecurity consultant at NVISO working as a Cyber Emergency Response Team (CERT) member. In his free time, he enjoys tinkering with code and skateboarding.

❌
❌