Skip to content

Instantly share code, notes, and snippets.

@kevinelwell
Created June 7, 2023 19:50
Show Gist options
  • Save kevinelwell/d24a79cfa74af028aa1ccdbe7a284b2c to your computer and use it in GitHub Desktop.
Save kevinelwell/d24a79cfa74af028aa1ccdbe7a284b2c to your computer and use it in GitHub Desktop.
#Requires -Version 5.1
#Requires -Modules @{ModuleName='PSFalcon';ModuleVersion='2.2.1'}
#Requires -RunAsAdministrator
<#
.SYNOPSIS
This script will copy and execute the KAPE forensics tool on a remote
Microsoft Windows host using CrowdStrike API's and RTR
.DESCRIPTION
Script that leverages the PSFalcon PowerShell module
https://github.com/CrowdStrike/psfalcon
Version 2.2.1 has been tested with this script
.REQUIREMENTS
Requires API client & key and API scopes:Access level - Hosts:Read, Real Time
Response (admin):Read/Write, Real Time
Response:Read/Write
Support and Resources --> API Clients and Keys --> Add New API Client
Requires KAPE-RTR.zip to be uploaded to the CrowdStrike Console
Host Setup and Management --> Response and Containment --> Response Scripts and Files --> "PUT Files"
.INPUTS
Users are prompted to select the appropriate CrowdStrike Cloud
Users must supply their clientID and secret API keys
Users must supply either the host's AID or host name
.OUTPUTS
Verbose logging to C:\Temp\PSFalcon-KAPE-Forensics.log
.NOTES
Version: 1.6
Script Name: PSFalcon-KAPE-Forensics.ps1
Author: Kevin Elwell - elwell1@protonmail.com
Creation Date: 8/25/2022
Purpose/Change: Initial script development
#>
# Import the psfalcon module - REQUIRES the PSFalcon PowerShell Module be placed in one of the PowerShell Modules directories
Import-Module -Name PSFalcon -Force -PassThru
#region Variables
# Initialize some Variables
$LogFolder = "C:\Temp\PSFalcon"
$LogFile = $LogFolder + "\" + "PSFalcon-KAPE-Forensics.log"
# Create C:\Temp\PSFalcon directory
New-Item -Path $LogFolder -ItemType Directory -Force -ErrorAction SilentlyContinue
#endregion Variables
#region Functions
# Logging function
Function Write-Log
{
param (
[Parameter(Mandatory=$True)]
[array]$LogOutput,
[Parameter(Mandatory=$False)]
[ValidateSet("INFO","WARN","ERROR","FATAL","DEBUG")]
[string]$level = "INFO",
[Parameter(Mandatory=$True)]
[string]$Path
)
#"[" + (Get-Date -f g) + "] " + $level + ": " + $logOutput | Out-File $Path -Append
"[" + (Get-Date -UFormat "%m/%d/%y %T %p") + "] " + $level + ": " + $logOutput | Out-File $Path -Append
# Write-Log -level DEBUG -LogOutput "Testing 456" -Path $LogFile
# Produces the following line: [8/31/2022 12:44 PM] DEBUG: Testing 456
}
# Initialize the log file
$initLog = "`r`n[" + (Get-Date -UFormat "%m/%d/%y %T %p") + "] Starting script execution" | Out-File -FilePath $LogFile -Append
Function Select-HostqueryType{
#Clear-Host
do {
Write-Host "`n========================== SELECT HOST =============================="
Write-Host " PLEASE SELECT AN OPTION BELOW TO EXECUTE THE KAPE FORENSICS TOOL"
Write-Host "`'1' TARGET HOST USING AID"
Write-Host "`'2' TARGET HOST USING HOSTNAME"
Write-Host "`'Q' QUIT"
Write-Host "======================================================================="
# Prompt user to select one of the CrowdStrike Cloud environments
$choice = Read-Host "`nENTER CHOICE"
} until (($choice -eq '1') -or ($choice -eq '2') -or ($choice -eq 'Q') )
switch ($choice) {
'1'{
Write-Host "`nYOU HAVE SELECTED TO USE THE AID OF THE HOST"
$hostquerymethod = "aid"
$hostquery = Read-Host "`nENTER THE AID"
}
'2'{
Write-Host "`nYOU HAVE SELECTED TO USE THE HOSTNAME OF THE HOST"
$hostquerymethod = "hostname"
$hostquery = Read-Host "`nENTER THE HOSTNAME"
}
'Q'{
Write-Host "`nEXITING THE MENU. PLEASE NOTE YOU MUST SELECT EITHER AID OR HOSTNAME TO PROCEED."
$hostquery = "quit"
}
}
If($hostquery -ne "quit") {
# Log the choice from above
Write-Log -level INFO -LogOutput "User choose to find the host via $hostquerymethod." -Path $LogFile
Return $hostquerymethod, $hostquery
}
If($hostquery -eq "quit") {
# Log that the user choose to quit
Write-Log -level INFO -LogOutput "User choose to quit the menu. Execution halting." -Path $LogFile
Break
}
}
Function CS-Cloud {
Clear-Host
do {
Write-Host "`n============= SELECT THE APPROPRIATE CROWDSTRIKE CLOUD ================"
Write-Host "`'1' FOR US-1 CLOUD"
Write-Host "`'2' FOR US-2 CLOUD"
Write-Host "`'3' FOR EU CLOUD"
Write-Host "`'4' FOR GOV CLOUD"
Write-Host "`'Q' TO QUIT"
Write-Host "======================================================================="
# Prompt user to select one of the CrowdStrike Cloud environments
$choice = Read-Host "`nENTER CHOICE"
} until (($choice -eq '1') -or ($choice -eq '2') -or ($choice -eq '3') -or ($choice -eq '4') -or ($choice -eq 'Q') )
switch ($choice) {
'1'{
Write-Host "`nYou have selected the US-1 Cloud"
$cloud = "us-1"
}
'2'{
Write-Host "`nYou have selected the US-2 Cloud"
$cloud = "us-2"
}
'3'{
Write-Host "`nYou have selected the EU Cloud"
$cloud = "eu-1"
}
'4'{
Write-Host "`nYou have selected the GOV Cloud"
$cloud = "us-gov-1"
}
'Q'{
Write-Host "`nExiting menu. Please note you MUST select one of the CrowdStrike Cloud environments."
$cloud = "quit"
}
}
If($cloud -ne "quit") {
# Log the choice from above
Write-Log -level INFO -LogOutput "User choose the CrowdStrike $cloud Cloud." -Path $LogFile
Return $cloud
}
If($cloud -eq "quit") {
# Log that the user choose to quit
Write-Log -level INFO -LogOutput "User choose to quit the menu. Execution halting." -Path $LogFile
Break
}
}
#endregion Functions
# Prompt the user for the CrowdStrike Cloud environment
$cloudenv = CS-Cloud
# Prompt for the API clientid and secret
$clientid = Read-Host -Prompt 'INPUT YOUR CLIENT ID API KEY'
$secret = Read-Host -Prompt 'INPUT YOUR API SECRET'
# Force TLS 1.2
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12
# Request an oAUTH2 token
try {
Request-FalconToken -ClientId $clientid -ClientSecret $secret -Cloud $cloudenv;
If ((Test-FalconToken).Token -eq $true) {
Write-Host "`n`rWE RECEIVED A TOKEN. PROCEEDING.`n`r"
# Log that a token was received
Write-Log -level INFO -LogOutput "Token received successfully." -Path $LogFile
}
} catch {
Write-Host "`n`rERROR! WE DID NOT RECEIVE A TOKEN!`n`r"
# Log that a token was NOT received
Write-Log -level ERROR -LogOutput "Token was NOT received successfully." -Path $LogFile
Break
}
# Gather information from the Select-HostQueryType function
$Hostqmethod, $Hostq = Select-HostqueryType
# Determine the operating system, hostname and/or AID
If($Hostqmethod -ieq "aid") { $oscheck = Get-FalconHost -Ids $hostq | Select-Object os_version, hostname
If($oscheck.os_version -inotmatch "windows") {
Write-Host "`n`rERROR: THIS SCRIPT ONLY SUPPORTS MICROSOFT WINDOWS OPERATING SYSTEMS. QUITTING SCRIPT..`n`r"
Write-Log -level ERROR -LogOutput "THIS SCRIPT ONLY SUPPORTS MICROSOFT WINDOWS OPERATING SYSTEMS. QUITTING SCRIPT" -Path $LogFile
Revoke-FalconToken
}else{
Write-Host "`n`rMICROSOFT WINDOWS OPERATING SYSTEM DETECTED. PROCEEDING..`n`r"
Write-Log -level INFO -LogOutput "MICROSOFT WINDOWS OPERATING SYSTEM DETECTED. PROCEEDING" -Path $LogFile
# Define a variable to hold the AID
$hostaid = $hostq
}
}
# Determine the operating system, hostname and/or AID
If($hostqmethod -ieq "hostname") { $oscheck1 = Get-FalconHost -Filter "hostname:['$hostq']" -Detailed | Select-Object os_version, device_id
If($oscheck.os_version -inotmatch "windows") {
Write-Host "`n`rERROR: THIS SCRIPT ONLY SUPPORTS MICROSOFT WINDOWS OPERATING SYSTEMS. QUITTING SCRIPT..`n`r"
Write-Log -level ERROR -LogOutput "THIS SCRIPT ONLY SUPPORTS MICROSOFT WINDOWS OPERATING SYSTEMS. QUITTING SCRIPT" -Path $LogFile
Revoke-FalconToken
Break
}else{
Write-Host "`n`rMICROSOFT WINDOWS OPERATING SYSTEM DETECTED. PROCEEDING..`n`r"
Write-Log -level INFO -LogOutput "MICROSOFT WINDOWS OPERATING SYSTEM DETECTED. PROCEEDING" -Path $LogFile
# Define a variable to hold the AID
$hostaid = $oscheck1.device_id
}
}
# Initialize the connection to the remote machine
#--------------------------------------------------------------------
#--------------------------------------------------------------------
# IMPORTANT - Start-FalconSession Requires: 'real-time-response:read'
#--------------------------------------------------------------------
#--------------------------------------------------------------------
$Init = Start-FalconSession -HostId $hostaid
#regionFileCopyandFileExecution
# Copy KAPE-RTR.zip on the remote host
Try {
Write-Host "Putting KAPE-RTR.zip on host"
Write-Log -level INFO -LogOutput "Putting KAPE-RTR.zip on host" -Path $LogFile
$put1 = Invoke-FalconAdminCommand -SessionId $Init.session_id -Command put -Arguments KAPE-RTR.zip
$put1Complete = Confirm-FalconCommand -CloudRequestId $put1.cloud_request_id
Start-Sleep -seconds 10
}
Catch {
Write-Host "Error checking for file copy status: $($error[0])"
Write-Log -level ERROR -LogOutput "Error checking for file copy status: $($error[0])" -Path $LogFile
Revoke-FalconToken
Return
}
$updSess1 = Update-FalconSession -hostid $($hostaid)
# Extract C:\KAPE-RTR.zip to C:\KAPE-RTR on the remote host
Try {
Write-Host "Extracting KAPE-RTR"
Write-Log -level INFO -LogOutput "Extracting KAPE-RTR" -Path $LogFile
$x = Invoke-FalconAdminCommand -SessionId $Init.session_id -Command 'runscript' -Arguments '-Raw=```Expand-Archive -LiteralPath C:\KAPE-RTR.zip -DestinationPath C:\KAPE-RTR -Force``` -Timeout=900'
$ExtractComplete = Confirm-FalconCommand -CloudRequestId $x.cloud_request_id
Start-Sleep -seconds 10
}
Catch {
Write-Host "Error checking for file extraction status: $($error[0])"
Write-Log -level ERROR -LogOutput "Error checking for file extraction status: $($error[0])" -Path $LogFile
Revoke-FalconToken
Return
}
# Checking to see if file extraction has completed
While($ExtractComplete.count -eq 0){
$updSess2 = Update-FalconSession -hostid $($hostaid)
Write-Host "File extraction still running. Will check again in 10 seconds..."
Write-Log -level INFO -LogOutput "File extraction still running. Will check again in 10 seconds..." -Path $LogFile
Start-Sleep -Seconds 10
$ExtractComplete = Confirm-FalconCommand -CloudRequestId $x.cloud_request_id
}
$ExtractStatus = Confirm-FalconCommand -CloudRequestId $x.cloud_request_id
if($ExtractStatus.complete -eq "True") {
Write-Host "File extraction completed successfully"
Write-Log -level INFO -LogOutput "File extraction completed successfully" -Path $LogFile
}
$updSess3 = Update-FalconSession -hostid $($hostaid)
# Execute kape.exe --ul (this command will reference the C:\Temp\KAPE-RTR\_kape.cli file for instructions)
Try {
Write-Host "Executing KAPE"
Write-Log -level INFO -LogOutput "Executing KAPE" -Path $LogFile
$execKAPE = Invoke-FalconAdminCommand -SessionId $Init.session_id -Command 'runscript' -Arguments '-Raw=```C:\KAPE-RTR\kape.exe --ul``` -Timeout=900'
$exec1Complete = Confirm-FalconCommand -CloudRequestId $execKAPE.cloud_request_id
Start-Sleep -seconds 10
}
Catch {
Write-Host "Error checking for KAPE execution status: $($error[0])"
Write-Log -level ERROR -LogOutput "Error checking for KAPE execution status: $($error[0])" -Path $LogFile
Revoke-FalconToken
Return
}
# Checking to see if KAPE execution has completed
Write-Host "Checking if the KAPE process is running on the remote host"
Write-Log -level INFO -LogOutput "Checking if the KAPE process is running on the remote host" -Path $LogFile
$KAPErunning = Invoke-FalconAdminCommand -SessionId $Init.session_id -Command 'runscript' -Arguments '-Raw=```Get-Process -name "kape"```'
Start-Sleep -seconds 10
$KAPEComplete = Confirm-FalconCommand -CloudRequestId $KAPErunning.cloud_request_id
Start-Sleep -seconds 10
While($KAPEComplete.stdout -imatch "System.Diagnostics.Process"){
$updSess4 = Update-FalconSession -hostid $($hostaid)
Write-Host "The KAPE process is still running on the remote host. Checking again in shortly..."
Write-Log -level INFO -LogOutput "The KAPE process is still running on the remote host. Checking again shortly..." -Path $LogFile
Start-Sleep -seconds 10
$KAPErunning = Invoke-FalconAdminCommand -SessionId $Init.session_id -Command 'runscript' -Arguments '-Raw=```Get-Process -name "kape"```'
Start-Sleep -seconds 10
$KAPEComplete = Confirm-FalconCommand -CloudRequestId $KAPErunning.cloud_request_id
Start-Sleep -seconds 10
}
$cleanupKAPEStatus = Confirm-FalconCommand -CloudRequestId $KAPErunning.cloud_request_id
if($cleanupKAPEStatus.stderr -imatch "Cannot find a process with the name") {
Write-Host "KAPE completed successfully. Proceeding to next step..."
Write-Log -level INFO -LogOutput "KAPE completed successfully. Proceeding to next step..." -Path $LogFile
}
# Sleep for 5 seconds
Start-Sleep -Seconds 5
$updSess5 = Update-FalconSession -hostid $($hostaid)
# Zip up the results of the KAPE execution
Try {
Write-Host "Zipping results of the KAPE execution"
Write-Log -level INFO -LogOutput "Zipping results of the KAPE execution" -Path $LogFile
$DeviceName = (Get-FalconHost -Id $hostaid).Hostname
$execz = Invoke-FalconAdminCommand -SessionId $Init.session_id -Command zip -Arguments "C:\$($DeviceName) C:\Windows\Temp\$($DeviceName).zip"
$exec2Complete = Confirm-FalconCommand -CloudRequestId $execz.cloud_request_id
Start-Sleep -seconds 10
}
Catch {
Write-Host "Error checking for zip execution status: $($error[0])"
Write-Log -level ERROR -LogOutput "Error checking for zip execution status: $($error[0])" -Path $LogFile
Revoke-FalconToken
Return
}
#endregionFileCopyandFileExecution
# Sleep for 5 seconds
Start-Sleep -Seconds 5
$updSess6 = Update-FalconSession -hostid $($hostaid)
#regionCleanUp
# Delete files from the remote host
Try {
Write-Host "Removing C:\KAPE-RTR.zip from the remote host"
Write-Log -level INFO -LogOutput "Removing C:\KAPE-RTR.zip from the remote host" -Path $LogFile
$cleanup1 = Invoke-FalconAdminCommand -SessionId $Init.session_id -Command rm -Arguments '-Force C:\KAPE-RTR.zip'
$cleanup1Complete = Confirm-FalconCommand -CloudRequestId $cleanup1.cloud_request_id
Start-Sleep -seconds 10
}
Catch {
Write-Host "Error removing C:\KAPE-RTR.zip from the remote host status: $($error[0])"
Write-Log -level ERROR -LogOutput "Error removing C:\KAPE-RTR.zip from the remote host status: $($error[0])" -Path $LogFile
Revoke-FalconToken
Return
}
While($cleanup1Complete -eq 0){
Write-Host "Checking if C:\KAPE-RTR.zip was deleted from the remote host again in 10 seconds..."
Write-Log -level INFO -LogOutput "Checking if C:\KAPE-RTR.zip was deleted from the remote host again in 10 seconds..." -Path $LogFile
Start-Sleep -Seconds 10
$cleanup1Complete = Confirm-FalconCommand -CloudRequestId $cleanup1.cloud_request_id
}
$cleanup1Status = Confirm-FalconCommand -CloudRequestId $cleanup1.cloud_request_id
if($cleanup1Status.complete -eq "True") {
Write-Host "Deletion of C:\KAPE-RTR.zip on the remote host completed successfully"
Write-Log -level INFO -LogOutput "Deletion of C:\KAPE-RTR.zip on the remote host completed successfully" -Path $LogFile
}
# Sleep for 5 seconds
Start-Sleep -Seconds 5
$updSess7 = Update-FalconSession -hostid $($hostaid)
# Delete files from the remote host
Try {
Write-Host "Removing C:\KAPE-RTR from the remote host"
Write-Log -level INFO -LogOutput "Removing C:\KAPE-RTR from the remote host" -Path $LogFile
$cleanup3 = Invoke-FalconAdminCommand -SessionId $Init.session_id -Command rm -Arguments '-Force C:\KAPE-RTR'
$cleanup3Complete = Confirm-FalconCommand -CloudRequestId $cleanup3.cloud_request_id
Start-Sleep -seconds 10
}
Catch {
Write-Host "Error removing C:\KAPE-RTR from the remote host status: $($error[0])"
Write-Log -level ERROR -LogOutput "Error removing C:\KAPE-RTR from the remote host status: $($error[0])" -Path $LogFile
Revoke-FalconToken
Return
}
While($cleanup3Complete -eq 0){
$sessionUpdate10 = Update-FalconSession -hostid $($hostaid)
Write-Host "Checking if C:\KAPE-RTR was deleted from the remote host again in 10 seconds..."
Write-Log -level INFO -LogOutput "Checking if C:\KAPE-RTR was deleted from the remote host again in 10 seconds.." -Path $LogFile
Start-Sleep -Seconds 10
$cleanup3Complete = Confirm-FalconCommand -CloudRequestId $cleanup3.cloud_request_id
}
$cleanup3Status = Confirm-FalconCommand -CloudRequestId $cleanup3.cloud_request_id
if($cleanup3Status.complete -eq "True") {
Write-Host "Deletion of C:\KAPE-RTR on the remote host completed successfully"
Write-Log -level INFO -LogOutput "Deletion of C:\KAPE-RTR on the remote host completed successfully" -Path $LogFile
}
# Sleep for 5 seconds
Start-Sleep -Seconds 5
$updSess8 = Update-FalconSession -hostid $($hostaid)
# Delete files from the remote host
Try {
Write-Host "Removing directory C:\$($DeviceName) and its contents from the remote host"
Write-Log -level INFO -LogOutput "Removing directory C:\$($DeviceName) and its contents from the remote host" -Path $LogFile
$cleanup4 = Invoke-FalconAdminCommand -SessionId $Init.session_id -Command rm -Arguments "-Force C:\$($DeviceName)"
$cleanup4Complete = Confirm-FalconCommand -CloudRequestId $cleanup4.cloud_request_id
Start-Sleep -seconds 10
}
Catch {
Write-Host "Error removing C:\$($DeviceName) from the remote host status: $($error[0])"
Write-Log -level ERROR -LogOutput "Error removing C:\$($DeviceName) from the remote host status: $($error[0])" -Path $LogFile
Revoke-FalconToken
Return
}
While($cleanup4Complete -eq 0){
$sessionUpdate11 = Update-FalconSession -hostid $($hostaid)
Write-Host "Checking if directory C:$($DeviceName) and its contents were deleted from the remote host again in 10 seconds..."
Write-Log -level INFO -LogOutput "Checking if directory C:$($DeviceName) and its contents were deleted from the remote host again in 10 seconds.." -Path $LogFile
Start-Sleep -Seconds 10
$cleanup4Complete = Confirm-FalconCommand -CloudRequestId $cleanup4.cloud_request_id
}
$cleanup4Status = Confirm-FalconCommand -CloudRequestId $cleanup4.cloud_request_id
if($cleanup4Status.complete -eq "True") {
Write-Host "Deletion of directory C:\$($DeviceName) and its contents from the remote host completed successfully"
Write-Log -level INFO -LogOutput "Deletion of directory C:\$($DeviceName) and its contents from the remote host completed successfully" -Path $LogFile
}
$updSess9 = Update-FalconSession -hostid $($hostaid)
#endregionCleanUp
# Sleep for 5 seconds
Start-Sleep -Seconds 5
# Upload the zipped results of the KAPE execution to the CrowdStrike Cloud
Try {
Write-Host "Uploading the zipped results of the KAPE execution to the CrowdStrike Cloud"
$execget = Invoke-FalconAdminCommand -SessionId $Init.session_id -Command get -Arguments "C:\Windows\Temp\$($DeviceName).zip"
$exec3Complete = Confirm-FalconCommand -CloudRequestId $execget.cloud_request_id
Start-Sleep -seconds 10
}
Catch {
Write-Host "Error checking for uploading zipped file status: $($error[0])"
Write-Log -level ERROR -LogOutput "Error removing C:\$($DeviceName) from the remote host status: $($error[0])" -Path $LogFile
Revoke-FalconToken
Return
}
# Notify user to check for the file in the CrowdStrike Console
Write-Host "The file $($DeviceName).zip is currently uploading to the CrowdStrike Cloud."
Write-Host "You can monitor the upload progress in the Real Time Response Audit page of the CrowdStrike console."
Write-Log -level INFO -LogOutput "The file $($DeviceName).zip is currently uploading to the CrowdStrike Cloud..." -Path $LogFile
Write-Log -level INFO -LogOutput "You can monitor the upload progress in the Real Time Response Audit page of the CrowdStrike console." -Path $LogFile
# Sleep for 5 seconds
Start-Sleep -Seconds 5
# Launch Browser to the RTR Audit Log
Write-Host "Launching default browser to Real Time Response Audit page now"
Write-Log -level INFO -LogOutput "Launching default browser to Real Time Response Audit Page now" -Path $LogFile
Start-Process https://falcon.$cloudenv.crowdstrike.com/activity/real-time-response/audit-logs
# Sleep for 5 seconds
Start-Sleep -Seconds 5
# Revoke Falcon Token
Write-Host "Script is complete. Revoking authorization token now."
Write-Log -level INFO -LogOutput "Script is complete. Revoking authorization token now." -Path $LogFile
# Finalize the log file
$FinalizeLog = "[" + (Get-Date -UFormat "%m/%d/%y %T %p") + "] Ending script execution`r`n" | Out-File -FilePath $LogFile -Append
Revoke-FalconToken
# End of script
@kevinelwell
Copy link
Author

kevinelwell commented Apr 12, 2024 via email

@tomee7
Copy link

tomee7 commented May 14, 2024

thanks for this script.

on line 347 where it comments

Execute kape.exe --ul (this command will reference the C:\Temp\KAPE-RTR_kape.cli file for instructions)

is this created via the script or does the file need to be created and populated manually for instructions?

@kevinelwell
Copy link
Author

C:\Temp\KAPE-RTR_kape.cli is extracted from the KAPE-RTR.zip file. The KAPE-RTR.zip file must be uploaded to your CS console

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment