Skip to content

Basic Scripts

bk-cs edited this page Mar 21, 2022 · 138 revisions

CrowdStrike Falcon


The examples provided below are for example purposes only and are offered 'as is' with no support.


Detections

Hosts

Host Groups

Policies

Real-time Response

Scheduled Reports

Sensor Installers

Threat Intelligence

Users

Vulnerabilities


Detections

Assign detections involving a specific file to a user

#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.0' }
param(
    [Parameter(Mandatory = $true,
        Position = 1)]
    [string] $Username,

    [Parameter(Mandatory = $true,
        Position = 2)]
    [string] $Filename
)
# Get User identifier
$Uuid = Get-FalconUser -Usernames $Username -ErrorAction SilentlyContinue

if (-not $Uuid) {
    throw "Invalid username: '$Username'"
}
# Get Detection identifiers involving $Filename
$Ids = Get-FalconDetection -Filter "behaviors.filename:'$Filename'" -All

if (-not $Ids) {
    throw "No detections found matching '$Filename'"
}
# Notify user that detections are being assigned
Write-Host "Assigning $($Ids.count) detections involving '$Filename' to '$Username'..."

# Modify detections
Edit-FalconDetection -Ids $Ids -Status in_progress -AssignedToUuid $Uuid

Find and hide large numbers of detections

#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.0' }
param(
    [Parameter(Mandatory = $true)]
    [string] $Filename
)
do {
    # Gather up to 1,000 detections (maximum for an edit request) involving $Filename
    $Ids = Get-FalconDetection -Filter "behaviors.filename:'$Filename'" -Limit 1000

    if ($Ids) {
        # Export list of ids being hidden
        $Ids | Out-File -FilePath $pwd\hidden_detections.txt -Append -Force

        # Hide group of detections
        Edit-FalconDetection -Ids $Ids -ShowInUi $false

        # Pause to give the API time to clear them out before the next request
        Start-Sleep -Seconds 5
    }
} while (
    # Exit once no more detections are available
    $Ids
)

Hosts

Add a list of hostnames to a host group

NOTE: This script expects a text file that contains case-sensitive hostnames (one per line) for the -Path parameter.

#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.0' }
param(
    [Parameter(Mandatory = $true,
        Position = 1)]
    [ValidatePattern('\.txt$')]
    [string] $Path,

    [Parameter(Mandatory = $true,
        Position = 2)]
    [ValidatePattern('^\w{32}$')]
    [string] $GroupId
)
$Hostnames = (Get-Content $Path).Normalize()
$Hosts = for ($i = 0; $i -lt $Hostnames.count; $i += 20) {
    # Retrieve the device_id for hostnames in groups of 20
    $Filter = ($Hostnames[$i..($i + 19)] | ForEach-Object {
        if ($_ -ne '') {
            "hostname:['$_']"
        }
    }) -join ','
    Get-FalconHost -Filter $Filter
}
# Add hosts to group
Invoke-FalconHostGroupAction -Name add-hosts -Id $GroupId -HostIds $Hosts

Hide hosts based on last_seen time

#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.0' }
param(
    [Parameter(Mandatory = $true)]
    [int] $Days
)
$Param = @{
    Filter = "last_seen:<'Last $Days days'"
    All = $true
}
$Hosts = Get-FalconHost @Param
if ($Hosts) {
    $Param = @{
        Name = 'hide_host'
        Ids = $Hosts
    }
    Invoke-FalconHostAction @Param
} else {
    "No hosts found using filter: $Filter"
}

Find duplicate hosts and hide them

WARNING: Find-FalconDuplicate only determines whether or not a device is a "duplicate" by hostname in this example. There may be a legitimate reason that two devices have the same hostname in your environment. It is your responsibility to determine whether or not the hosts reported by Find-FalconDuplicate are correct. This script does not provide you with an opportunity to review those hosts before they are hidden, but it does output a list after the hiding is complete. If devices are hidden incorrectly they will continue to communicate with Falcon and can be restored from the trash using their aid value and Invoke-FalconHostAction.

#Requires -Version 5.1 -Modules @{ModuleName="PSFalcon";ModuleVersion='2.1'}
param(
    [switch] $Confirm
)
try {
    # Use Find-FalconDuplicate to find duplicate hosts
    $Duplicates = Find-FalconDuplicate

    if ($Duplicates.device_id -and $Confirm) {
        # Output duplicate list to CSV
        $Duplicates | Export-Csv -Path .\duplicates.csv -NoTypeInformation

        # Use Invoke-FalconHostAction to hide hosts
        Invoke-FalconHostAction -Name hide_host -Ids $Duplicates.device_id
    } elseif ($Duplicates) {
        # Output list of duplicates
        Write-Output "Found $($Duplicates.count) potential duplicate hosts"
        Write-Output $Duplicates
    } else {
       Write-Output "No duplicates found."
    }
} catch {
    Write-Error $_
}

Network contain a device by Hostname

#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.0' }
param(
    [Parameter(Mandatory = $true)]
    [string] $Hostname
)
# Get identifier for target system and choose the most recently seen (in case of duplicates)
$HostId = Get-FalconHost -Filter "hostname:'$Hostname'" -Sort last_seen.desc -Limit 1

if ($HostId) {
    # Contain host
    Invoke-FalconHostAction -Name contain -Ids $HostId
} else {
    throw "No identifier found for '$Hostname'"
}

Network contain a list of Hostnames from a CSV file

NOTE: This example requires a CSV with a column labeled Hostname. It will create a new CSV with that includes the hostname, device_id and containment request status.

#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.0' }
param(
    [Parameter(Mandatory = $true)]
    [ValidatePattern('\.csv$')]
    [string] $Path
)
$OutputFile = "Containment_$(Get-Date -Format FileDateTime).csv"
[array] $Hostnames = (Import-Csv $Path).Hostname
$Hosts = for ($i = 0; $i -lt $Hostnames.count; $i += 20) {
    # Retrieve the device_id for hostnames in groups of 20
    $Filter = ($Hostnames[$i..($i + 19)] | ForEach-Object {
        if ($_ -ne '') {
            "hostname:['$_']"
        }
    }) -join ','
    $Output = Get-FalconHost -Filter $Filter -Detailed | Select-Object hostname, device_id
    $Output | ForEach-Object {
        # Add property for each host to update after containment request
        $_.PSObject.Properties.Add((New-Object PSNoteProperty('Contain_Requested', $false)))
    }
    $Output
}
# Contain hosts and add status
Invoke-FalconHostAction -Name contain -Ids $Hosts.device_id | ForEach-Object {
    $Id = $_.id
    ($Hosts | Where-Object { $_.device_id -eq $Id }).Contain_Requested = $true
}
$Hosts | Export-Csv -Path $OutputFile -NoTypeInformation
if (Test-Path $OutputFile) {
    Get-ChildItem $OutputFile
}

Output selected Host info and replace ids with names

Designed to replace all 'ids' with the related 'name', under each Host result. The fields you'd like to include from each host result can be added to the $Fields variable.

#Requires -Version 5.1 -Modules @{ModuleName="PSFalcon";ModuleVersion='2.1.2'}
[CmdletBinding()]
param()
begin {
    # Fields to include with the export to CSV (host group and policy data is automatically added)
    $Fields = @('device_id', 'hostname', 'last_seen', 'first_seen', 'local_ip', 'external_ip', 'agent_version')
}
process {
    # Retrieve all host information and filter to selected fields
    $Fields += 'device_policies', 'groups'
    $HostInfo = Get-FalconHost -Detailed -All | Select-Object $Fields
    if ($HostInfo) {
        # Create hashtable to store object detail for hosts
        $Related = @{
            DeviceControlPolicy = @{}
            FirewallPolicy      = @{}
            IoaGroup            = @{}
            PreventionPolicy    = @{}
            ResponsePolicy      = @{}
            SensorUpdatePolicy  = @{}
            HostGroup           = @{}
        }
        foreach ($ItemType in $Related.Keys) {
            # Match policy type to the label used with hosts
            $HostLabel = switch ($ItemType) {
                'DeviceControlPolicy' { 'device_control' }
                'HostGroup'           { 'groups' }
                'IoaGroup'            { 'rule_groups' }
                'ResponsePolicy'      { 'remote_response' }
                'SensorUpdatePolicy'  { 'sensor_update' }
                default               { ($_ -replace 'Policy', $null).ToLower() }
            }
            $Ids = if ($ItemType -eq 'IoaGroup') {
                # Collect IOA rule group identifiers
                ($HostInfo.device_policies.prevention.rule_groups | Group-Object).Name
            } elseif ($ItemType -match 'Policy$') {
                # Collect policy identifiers
                ($HostInfo.device_policies.$HostLabel.policy_id | Group-Object).Name
            } else {
                # Collect host group identifiers
                ($HostInfo.groups | Group-Object).Name
            }
            # Collect names and identifiers for each item in hashtable
            $Content = & "Get-Falcon$($ItemType)" -Ids $Ids | Select-Object id, name
            if ($Content) {
                $Content | ForEach-Object {
                    $Related.$ItemType["$($_.id)"] = "$($_.name)"
                }
            } else {
                Write-Error "Unable to collect '$ItemType' information. Check permissions."
            }
            $HostInfo | ForEach-Object {
                # Define new property names to add to output
                $Name = if ($ItemType -eq 'HostGroup') {
                    "host_$($HostLabel)"
                } elseif ($ItemType -eq 'IoaGroup') {
                    "ioa_$($HostLabel)"
                } else {
                    "$($HostLabel)_policy"
                }
                $Value = if ($ItemType -eq 'HostGroup') {
                    # Replace host group identifiers with names and remove 'groups'
                    ($_.groups | ForEach-Object { $Related.$ItemType.$_ }) -join ','
                    [void] $_.PSObject.Properties.Remove('groups')
                } elseif ($ItemType -eq 'IoaGroup') {
                    # Replace IOA rule group identifiers with names
                    ($_.device_policies.prevention.rule_groups | ForEach-Object {
                        $Related.$ItemType.$_ }) -join ','
                } else {
                    # Replace policy identifiers with names and add as '<type>_policy'
                    $Related.$ItemType.($_.device_policies.$HostLabel.policy_id)
                }
                $_.PSObject.Properties.Add((New-Object PSNoteProperty($Name, $Value)))
            }
        }
        $HostInfo | Where-Object { $_.device_policies } | ForEach-Object {
            # Remove redundant 'device_policies' property
            [void] $_.PSObject.Properties.Remove('device_policies')
        }
        $HostInfo
    } else {
        Write-Error "Unable to collect Host information. Check permissions."
    }
}

Get host information from multiple Falcon instances

NOTE: This example requires that you input values for <client_id>, <client_secret>, and each <member_cid>. To avoid hard-coding credentials you could pass them as parameters instead.

#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.0' }
# ClientId, ClientSecret and MemberCids
$ClientId = '<client_id>'
$ClientSecret = '<client_secret>'
$CIDs = @('<member_cid>', '<member_cid>')

# Enumerate $CIDs
$CIDs | ForEach-Object {
    $Param = @{
        ClientId = $ClientId
        ClientSecret = $ClientSecret
        MemberCid = ($_).ToLower()
    }
    # Authenticate with CID
    Request-FalconToken @Param

    try {
        # Gather and export Host data
        Get-FalconHost -Detailed -All | Export-FalconReport ".\Hosts_for_MemberCid_$($_).csv"
    } catch {
        # Break 'foreach' loop if host retrieval/export fails
        throw $_
    } finally {
        # Remove authentication token and credentials for next CID
        Revoke-FalconToken
    }
}

Host Groups

Create a host group and add a list of devices by hostname

#Requires -Version 5.1 -Modules @{ModuleName="PSFalcon";ModuleVersion='2.1'}
param(
    [Parameter(Mandatory = $true, Position = 1)]
    [ValidatePattern('\.txt$')]
    [ValidateScript({
        if (Test-Path -Path $_ -PathType Leaf) {
            $true
        } else {
            throw "Cannot find path '$_' because it does not exist or is a directory."
        }
    })]
    [string] $Path,

    [Parameter(Mandatory = $true, Position = 2)]
    [string] $Name,

    [Parameter(Position = 3)]
    [string] $Description
)
process {
    try {
        # Import hostnames and create host group
        $Hostnames = (Get-Content -Path $PSBoundParameters.Path).Normalize()
        $Param = @{
            GroupType = 'static'
            Name      = $PSBoundParameters.Name
        }
        if ($PSBoundParameters.Description) {
            $Param['Description'] = $PSBoundParameters.Description
        }
        $Group = New-FalconHostGroup @Param
        [array] $HostIds = for ($i = 0; $i -lt $Hostnames.count; $i += 20) {
            # Retrieve the device_id for hostnames in groups of 20
            $Filter = ($Hostnames[$i..($i + 19)] | ForEach-Object {
                if ($_ -ne '') {
                    "hostname:['$_']"
                }
            }) -join ','
            ,(Get-FalconHost -Filter $Filter)
        }
        # Add hosts to group
        Invoke-FalconHostGroupAction -Name add-hosts -Id $Group.id -HostIds $HostIds
    } catch {
        throw $_
    }
}

Verify that a list of Host Groups exist within child CIDs

#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.0' }
[CmdletBinding()]
param(
    [Parameter(Mandatory = $true)]
    [ValidatePattern('^\w{32}$')]
    [string] $ClientId,

    [Parameter(Mandatory = $true)]
    [ValidatePattern('^\w{40}$')]
    [string] $ClientSecret,

    [Parameter()]
    [ValidateSet('eu-1', 'us-gov-1', 'us-1', 'us-2')]
    [string] $Cloud,

    [Parameter(Mandatory = $true)]
    [ValidatePattern('^\w{32}$')]
    [array] $MemberCIDs,

    [Parameter(Mandatory = $true)]
    [array] $GroupNames
)
begin {
    # Log file name and output location
    $OutputFile = "VerifyGroup_$(Get-Date -Format FileDateTime).csv"
    $OutputPath = "$(Join-Path -Path ([Environment]::CurrentDirectory) -ChildPath $OutputFile)"
}
process {
    foreach ($CID in $MemberCIDs) {
        $Param = @{
            ClientId = $ClientId
            ClientSecret = $ClientSecret
            MemberCid = ($CID).ToLower()
        }
        if ($Cloud) {
            $Param.Add('Cloud', $Cloud)
        }
        # Authenticate with Member CID
        Request-FalconToken @Param
        try {
            # Get group information
            $Groups = for ($i = 0; $i -lt $GroupNames.count; $i += 20) {
                [array] $NameFilter = ($GroupNames[$i..($i + 19)]).foreach{
                    "name:'$(($_).ToLower())'"
                }
                Get-FalconHostGroup -Filter ($NameFilter -join ',') -Detailed | Select-Object id, name
            }
            # Create output object
            $Output = [PSCustomObject] @{
                CID = $CID
            }
            foreach ($GroupName in $GroupNames) {
                # Add each group name and id
                $IdValue = if ($Groups.name -contains $GroupName) {
                    ($Groups | Where-Object { $_.name -eq $GroupName }).id
                } else {
                    $null
                }
                $Output.PSObject.Properties.Add((New-Object PSNoteProperty($GroupName, $IdValue)))
            }
            # Output to CSV
            $Output | Export-Csv -Path $OutputPath -NoTypeInformation -Append
            Write-Host "Output Host Groups for CID: $CID"
        } catch {
            # Output error and end script
            $_
            break
        } finally {
            # Remove authentication token and credentials for next CID
            Revoke-FalconToken

            # Sleep for 5 seconds to avoid rate limiting on token request
            Start-Sleep -Seconds 5
        }
    }
}
end {
    if (Test-Path -Path $OutputPath) {
        Get-ChildItem -Path $OutputPath
    }
}

Policies

Modify all Sensor Visibility Exclusions to include an additional Host Group

#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.0' }
param(
    [Parameter(Mandatory = $true,
        Position = 1)]
    [ValidatePattern('^\w{32}$')]
    [string] $GroupId
)
$SVEs = Get-FalconSVExclusion -Detailed -All
foreach ($SVE in $SVEs) {
    Edit-FalconSVExclusion -Id $SVE.id -GroupIds @($SVE.groups.id, $GroupId)
}

Assign a list of Host Group names to a specific Policy Id within a list of Child CIDs

#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.0' }
[CmdletBinding()]
param(
    [Parameter(Mandatory = $true)]
    [ValidatePattern('^\w{32}$')]
    [string] $ClientId,

    [Parameter(Mandatory = $true)]
    [ValidatePattern('^\w{40}$')]
    [string] $ClientSecret,

    [Parameter()]
    [ValidateSet('eu-1', 'us-gov-1', 'us-1', 'us-2')]
    [string] $Cloud,

    [Parameter(Mandatory = $true)]
    [ValidatePattern('^\w{32}$')]
    [array] $MemberCIDs,

    [Parameter(Mandatory = $true)]
    [array] $GroupNames,

    [Parameter(Mandatory = $true)]
    [ValidateSet('DeviceControl', 'Firewall', 'Prevention', 'Response', 'SensorUpdate')]
    [string] $PolicyType,

    [Parameter(Mandatory = $true)]
    [ValidatePattern('^\w{32}$')]
    [string] $PolicyId
)
begin {
    # Log file name and output location
    $LogName = "AssignGroup_$(Get-Date -Format FileDateTime).log"
    $LogLocation = "$(Join-Path -Path ([Environment]::CurrentDirectory) -ChildPath $LogName)"

    function Write-LogEntry ([string] $Source, [string] $Message) {
        # Write output and add to log file
        "[$(Get-Date -Format 'yyyy-MM-dd hh:mm:ss') $Source] $Message" | Tee-Object -FilePath $LogLocation -Append
    }
}
process {
    foreach ($CID in $MemberCIDs) {
        $Param = @{
            ClientId = $ClientId
            ClientSecret = $ClientSecret
            MemberCid = ($CID).ToLower()
        }
        if ($Cloud) {
            $Param.Add('Cloud', $Cloud)
        }
        # Authenticate with Member CID
        Request-FalconToken @Param
        try {
            ($GroupNames).foreach{
                # Get Host Group Id
                $GroupId = Get-FalconHostGroup -Filter "name:'$(($_).ToLower())'"
                if ($GroupId) {
                    # Assign Host Group to policy
                    $InvokeCommand = "Invoke-Falcon$($PolicyType)PolicyAction"
                    $Param = @{
                        Name = 'add-host-group'
                        Id = $PolicyId
                        GroupId = $GroupId
                    }
                    $Assigned = & $InvokeCommand @Param
                    if ($Assigned) {
                        $Param = @{
                            Source = $InvokeCommand
                            Message = "Assigned group $($GroupId) to $($PolicyType) policy" +
                                " $($Assigned.id) in CID $($CID)"
                        }
                        Write-LogEntry @Param
                    } else {
                        $Param = @{
                            Source = $InvokeCommand
                            Message = "Failed to assign group $($GroupId) to $($PolicyId) in CID $($CID)"
                        }
                        Write-LogEntry @Param
                    }
                } else {
                    $Param = @{
                        Source = 'Get-FalconHostGroup'
                        Message = "No results for group name '$_' in CID $($CID)"
                    }
                    Write-LogEntry @Param
                }
            }
        } catch {
            # Output error and end script
            $_
            break
        } finally {
            # Remove authentication token and credentials for next CID
            Revoke-FalconToken

            # Sleep for 5 seconds to avoid rate limiting on token request
            Start-Sleep -Seconds 5
        }
    }
}
end {
    if (Test-Path -Path $LogLocation) {
        # Output log file and path
        Get-ChildItem -Path $LogLocation
    }
}

Output a list of assigned Host Groups for designated Policy ids within child CIDs

#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.0' }
[CmdletBinding()]
param(
    [Parameter(Mandatory = $true)]
    [ValidatePattern('^\w{32}$')]
    [string] $ClientId,

    [Parameter(Mandatory = $true)]
    [ValidatePattern('^\w{40}$')]
    [string] $ClientSecret,

    [Parameter()]
    [ValidateSet('eu-1', 'us-gov-1', 'us-1', 'us-2')]
    [string] $Cloud,

    [Parameter(Mandatory = $true)]
    [ValidatePattern('^\w{32}$')]
    [array] $MemberCIDs,

    [Parameter(Mandatory = $true)]
    [ValidateSet('DeviceControl', 'Firewall', 'Prevention', 'Response', 'SensorUpdate')]
    [string] $PolicyType,

    [Parameter(Mandatory = $true)]
    [ValidatePattern('^\w{32}$')]
    [array] $PolicyIds
)
begin {
    $Filename = "$($PolicyType)PolicyAssignments_$(Get-Date -Format FileDate).csv"
    $OutputPath = "$(Join-Path -Path ([Environment]::CurrentDirectory) -ChildPath $Filename)"
}
process {
    foreach ($CID in $MemberCIDs) {
        $Param = @{
            ClientId = $ClientId
            ClientSecret = $ClientSecret
            MemberCid = ($CID).ToLower()
        }
        if ($Cloud) {
            $Param.Add('Cloud', $Cloud)
        }
        # Authenticate with Member CID
        Request-FalconToken @Param
        try {
            # Get policy information
            $InvokeCommand = "Get-Falcon$($PolicyType)Policy"
            & $InvokeCommand -Ids $PolicyIds | Select-Object id, groups | ForEach-Object {
                [PSCustomObject] @{
                    # Output with CID, policy id and assigned groups
                    CID = $CID
                    PolicyId = "$($_.id)"
                    Groups = $_.groups.id -join ', '
                } | Export-Csv -Path $OutputPath -NoTypeInformation -Append
            }
            Write-Host "Output assigned Host Groups for policies in CID: $CID"
        } catch {
            # Output error and end script
            $_
            break
        } finally {
            # Remove authentication token and credentials for next CID
            Revoke-FalconToken

            # Sleep for 5 seconds to avoid rate limiting on token request
            Start-Sleep -Seconds 5
        }
    }
}
end {
    if (Test-Path -Path $OutputPath) {
        Get-ChildItem -Path $OutputPath
    }
}

Add a list of combined_id exceptions to a Device Control policy

NOTE: This script will create 'FULL_ACCESS' exceptions for the 'MASS_STORAGE' class within an existing policy. You can modify the hashtable created in $Exceptions to add key/value pairs like vendor_name or product_name.

#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.0' }
[CmdletBinding()]
param(
    [Parameter(Mandatory = $true, Position = 1)]
    [ValidatePattern('^\w{32}$')]
    [string] $PolicyId,

    [Parameter(Mandatory = $true, Position = 2)]
    [array] $CombinedIds
)
begin {
    # Maximum number of exceptions to add per request
    $Max = 50
}
process {
    for ($i = 0; $i -lt ($CombinedIds | Measure-Object).Count; $i += $Max) {
        # Create exceptions in groups of $Max
        $IdGroup = $CombinedIds[$i..($i + ($Max - 1))]
        [array] $Exceptions = $IdGroup | ForEach-Object {
            @{
                action = 'FULL_ACCESS'
                combined_id = $_
            }
        }
        $Settings = @{
            classes = @(
                @{
                    id = 'MASS_STORAGE'
                    exceptions = $Exceptions
                }
            )
        }
        Edit-FalconDeviceControlPolicy -Id $PolicyId -Settings $Settings | ForEach-Object {
            # Validate presence of 'combined_id' in policy
            $PolicyExceptions = ($_.settings.classes | Where-Object { $_.id -eq 'MASS_STORAGE' }).exceptions
            ($IdGroup).foreach{
                if ($PolicyExceptions.combined_id -contains $_) {
                    Write-Output "OK: $_"
                }
            }
        }
    }
}

Create CSVs containing Device Control policy details and exceptions

This script will create a series of CSV files containing information about a given Device Control policy (settings, members, groups, exceptions, etc.).

#Requires -Version 5.1
using module @{
    ModuleName    = 'PSFalcon'
    ModuleVersion = '2.1'
}
[CmdletBinding(DefaultParameterSetName = 'Id')]
param(
    [Parameter(ParameterSetName = 'Id', Mandatory = $true, Position = 1)]
    [Parameter(ParameterSetName = 'Name', Mandatory = $true, Position = 1)]
    [ValidatePattern('^\w{32}$')]
    [string] $ClientId,

    [Parameter(ParameterSetName = 'Id', Mandatory = $true, Position = 2)]
    [Parameter(ParameterSetName = 'Name', Mandatory = $true, Position = 2)]
    [ValidatePattern('^\w{40}$')]
    [string] $ClientSecret,

    [Parameter(ParameterSetName = 'Id', Position = 3)]
    [Parameter(ParameterSetName = 'Name', Position = 3)]
    [ValidateSet('eu-1', 'us-gov-1', 'us-1', 'us-2')]
    [string] $Cloud,

    [Parameter(ParameterSetName = 'Id', Mandatory = $true, Position = 4)]
    [ValidatePattern('^\w{32}$')]
    [string] $Id,

    [Parameter(ParameterSetName = 'Name', Mandatory = $true, Position = 5)]
    [string] $Name,

    [Parameter(ParameterSetName = 'Id', Position = 6)]
    [Parameter(ParameterSetName = 'Name', Position = 6)]
    [ValidateScript({
        if ((Test-Path $_) -eq $false) {
            throw "Cannot find path '$_' because it does not exist."
        } elseif ((Test-Path $_ -PathType Container) -eq $false) {
            throw "'Path' must specify a folder."
        } else {
            $true
        }
    })]
    [string] $Path

)
begin {
    function Write-Output ([object] $Content, [string] $Type) {
        if ($Content) {
            $Param = @{
                Path              = Join-Path -Path $OutputFolder -ChildPath "$(Get-Date -Format FileDate)_$(
                    $Id)_$($Type).csv"
                NoTypeInformation = $true
                Append            = $true
                Force             = $true
            }
            $Content | Export-Csv @Param
        }
    }
    $OutputFolder = if (!$Path) {
        (Get-Location).Path
    } else {
        $Path
    }
    $Param = @{
        ClientId     = $ClientId
        ClientSecret = $ClientSecret
    }
    if ($Cloud) {
        $Param['Cloud'] = $Cloud
    }
    Request-FalconToken @Param
    $VerbosePreference = 'Continue'
}
process {
    $PolicyId = if ((Test-FalconToken).Token -eq $true) {
        if ($Name) {
            try {
                Get-FalconDeviceControlPolicy -Filter "name:'$($Name.ToLower())'" -Detailed
            } catch {
                throw "No Device Control policy found matching '$($Name.ToLower())'."
            }
        } else {
            $Id
        }
    }
    if ($PolicyId) {
        foreach ($Item in (Get-FalconDeviceControlPolicy -Ids $PolicyId)) {
            $Item.settings.PSObject.Members.Where({ $_.MemberType -eq 'NoteProperty' }).foreach{
                if ($_.Name -eq 'classes') {
                    Write-Output ($_.Value | Select-Object id, action) 'classes'
                    foreach ($Exception in ($_.Value).Where({ $_.exceptions }).exceptions) {
                        Write-Output $Exception 'exceptions'
                    }
                } else {
                    $Item.PSObject.Properties.Add((New-Object PSNoteProperty($_.Name, $_.Value)))
                }
            }
            foreach ($Property in @('groups', 'settings')) {
                if ($Item.$Property -and $Property -eq 'groups') {
                    Write-Output ($Item.$Property | Select-Object id, name) $Property
                }
                $Item.PSObject.Properties.Remove($Property)
            }
            Write-Output $Item 'settings'
            Write-Output (Get-FalconDeviceControlPolicyMember -Id $PolicyId -Detailed -All |
                Select-Object device_id, hostname) 'members'
        }
    }
}

Real-time Response

Run a command against a group of devices

#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.0' }
param(
    [Parameter(Mandatory = $true,
        Position = 1)]
    [string] $GroupName,

    [Parameter(Mandatory = $true,
        Position = 2)]
    [string] $Command,

    [Parameter(Position = 3)]
    [string] $Arguments,

    [boolean] $QueueOffline
)
# Find group identifier using 'name' filter
$GroupId = Get-FalconHostGroup -Filter "name:'$($GroupName.ToLower())'"

if ($GroupId) {
    # Get host identifiers for members of $GroupId
    $Members = Get-FalconHostGroupMember -Id $GroupId -All
} else {
    throw "No host group found matching '$GroupName'"
}
if ($Members) {
    # Set filename for CSV export
    $ExportName = "$pwd\rtr_$($Command -replace ' ','_')_$GroupId.csv"

    # Set base parameters for Invoke-FalconRTR
    $Param = @{
        Command = $Command
        HostIds = $Members
    }
    switch ($PSBoundParameters.Keys) {
        # Append parameters from user input that match Invoke-FalconRTR
        { $_ -ne 'GroupName' } {
            $Param[$_] = $PSBoundParameters.$_
        }
    }
    # Issue command and export results to CSV
    Invoke-FalconRTR @Param | Export-Csv -Path $ExportName
    
    if (Test-Path $ExportName) {
        # Display CSV file
        Get-ChildItem $ExportName
    }
} else {
    throw "No members found in host group '$GroupName' [$GroupId]"
}

Upload and execute a local script

NOTE: This will get the content of a script from the local administrator computer, encode it (to minimize potential errors due to quotation marks) and run it as a "Raw" script using Invoke-FalconRTR.

[CmdletBinding()]
param(
    [Parameter(
        Mandatory = $true,
        Position = 1)]
    [ValidateScript({ Test-Path $_ })]
    [string] $Path,

    [Parameter(
        Mandatory = $true,
        Position = 2)]
    [ValidatePattern('^\w{32}$')]
    [array] $HostIds,

    [Parameter(Position = 3)]
    [int] $Timeout
)
begin {
    $EncodedScript = [Convert]::ToBase64String(
        [System.Text.Encoding]::Unicode.GetBytes((Get-Content -Path $Path -Raw)))
}
process {
    $Param = @{
        Command = 'runscript'
        Arguments = '-Raw=```powershell.exe -Enc ' + $EncodedScript + '```'
        HostIds = $HostIds
    }
    if ($HostIds.count -gt 1 -and $Timeout) {
        $Param['Timeout'] = $Timeout
    }
    Invoke-FalconRTR @Param
}

Upload and execute a local script as a secondary process

NOTE: Similar to the other example this will run a script as a secondary PowerShell process on the target device, which helps when scripts are expected to exceed the Real-time Response timeout limit. The downside is that you will not be able to return results from the script unless you write them to a local file on the target host that you access later.

[CmdletBinding()]
param(
    [Parameter(
        Mandatory = $true,
        Position = 1)]
    [ValidateScript({ Test-Path $_ })]
    [string] $Path,

    [Parameter(
        Mandatory = $true,
        Position = 2)]
    [ValidatePattern('^\w{32}$')]
    [array] $HostIds,

    [Parameter(Position = 3)]
    [int] $Timeout
)
begin {
    $EncodedScript = [Convert]::ToBase64String(
        [System.Text.Encoding]::Unicode.GetBytes((Get-Content -Path $Path -Raw)))
}
process {
    $Param = @{
        Command = 'runscript'
        Arguments = '-Raw=```Start-Process -FilePath powershell.exe -ArgumentList "-Enc ' + $EncodedScript + '"```'
        HostIds = $HostIds
    }
    if ($HostIds.count -gt 1 -and $Timeout) {
        $Param['Timeout'] = $Timeout
    }
    Invoke-FalconRTR @Param
}

Scheduled Reports

Download your most recent scheduled report results

This is a simple one-line script that will download the most recent scheduled report results into your current directory.

#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.1.6' }

(Get-FalconScheduledReport -Detailed -All).last_execution | ForEach-Object {
    Receive-FalconScheduledReport -Id $_.id -Path "$($_.result_metadata.report_file_name)"
}

Sensor Installers

Download the installer package assigned to a Sensor Update policy

#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.0' }
param(
    [Parameter(Mandatory = $true)]
    [ValidatePattern('^\w{32}$')]
    [string] $PolicyId
)
try {
    # Retrieve Sensor Update policy detail
    $Policy = Get-FalconSensorUpdatePolicy -Ids $PolicyId
    if ($Policy.platform_name -and $Policy.settings) {
        # Use build and sensor_version to create regex pattern
        [regex] $Pattern = "^($([regex]::Escape(
            ($Policy.settings.sensor_version -replace '\.\d+$',$null)))\.\d{1,}|\d\.\d{1,}\.$(
            $Policy.settings.build.Split('|')[0]))$"
        $Match = try {
            # Select matching installer from list for 'platform_name' using regex pattern
            Get-FalconInstaller -Filter "platform:'$($Policy.platform_name.ToLower())'" -Detailed |
                Where-Object { $_.version -match $Pattern -and $_.description -match 'Falcon Sensor' }
        } catch {
            throw 'Unable to find installer through version match'
        }
        if ($Match.sha256 -and $Match.name) {
            $Installer = Join-Path -Path (Get-Location).Path -ChildPath $Match.name
            if ((Test-Path $Installer) -and ((Get-FileHash -Algorithm SHA256 -Path $Installer) -eq $Match.sha256)) {
                # Abort if matching file already exists
                throw "File exists with matching hash [$($Match.sha256)]"
            } elseif (Test-Path $Installer) {
                # Remove other versions
                Remove-Item -Path $Installer -Force
            }
            # Download the installer package
            Receive-FalconInstaller -Id $Match.sha256 -Path $Installer
        } else {
            throw "Properties 'sha256' or 'name' missing from installer result"
        }
    }
} catch {
    throw $_
}

Download the installer package assigned to your default Sensor Update policy

#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.0' }
[CmdletBinding()]
param(
    [Parameter(Mandatory = $true, Position = 1)]
    [ValidateScript({
        if (Test-Path -Path $_ -PathType Container) {
            $true
        } else {
            throw "Cannot find path '$_' because it does not exist or is not a directory."
        }
    })]
    [string] $Path
)
begin {
    # Ensure absolute path for output directory
    $OutputDirectory = if (![IO.Path]::IsPathRooted($PSBoundParameters.Path)) {
        $FullPath = Join-Path -Path (Get-Location).Path -ChildPath $PSBoundParameters.Path
        $FullPath = Join-Path -Path $FullPath -ChildPath '.'
        [IO.Path]::GetFullPath($FullPath)
    } else {
        $PSBoundParameters.Path
    }
}
process {
    try {
        # Retrieve Sensor Update policy detail
        $Policy = Get-FalconSensorUpdatePolicy -Filter "platform_name:'Windows'+name:'platform_default'" -Detailed
        if ($Policy.platform_name -and $Policy.settings) {
            # Use build and sensor_version to create regex pattern
            [regex] $Pattern = "^($([regex]::Escape(
                ($Policy.settings.sensor_version -replace '\.\d+$',$null)))\.\d{1,}|\d\.\d{1,}\.$(
                $Policy.settings.build.Split('|')[0]))$"
            $Match = try {
                # Select matching installer from list for 'platform_name' using regex pattern
                Get-FalconInstaller -Filter "platform:'$($Policy.platform_name.ToLower())'" -Detailed |
                    Where-Object { $_.version -match $Pattern -and $_.description -match 'Falcon Sensor' }
            } catch {
                throw 'Unable to find installer through version match'
            }
            if ($Match.sha256 -and $Match.name) {
                $Installer = Join-Path -Path $OutputDirectory -ChildPath $Match.name
                if ((Test-Path $Installer) -and ((Get-FileHash -Algorithm SHA256 -Path $Installer) -eq $Match.sha256)) {
                    # Abort if matching file already exists
                    throw "File exists with matching hash [$($Match.sha256)]"
                } elseif (Test-Path $Installer) {
                    # Remove other versions
                    Remove-Item -Path $Installer -Force
                }
                # Download the installer package
                $Receive = Receive-FalconInstaller -Id $Match.sha256 -Path $Installer
                if (Test-Path $Receive.FullName) {
                    $Match | ForEach-Object {
                        # Output installer information with 'file_path' of local file
                        $_.PSObject.Properties.Add((New-Object PSNoteProperty('file_path', $Receive.FullName)))
                        $_
                    }
                } else {
                    throw "Installer download failed [$($Match.sha256)]"
                }
            } else {
                throw "Properties 'sha256' or 'name' missing from installer result"
            }
        } else {
            throw "Unable to retrieve default policy for Windows"
        }
    } catch {
        throw $_
    }
}

Threat Intelligence

Export domain and IP indicators updated within the last week to CSV

#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.0' }
$UnixDate = [DateTimeOffset]::Now.AddDays(-7).ToUnixTimeSeconds()
$Param = @{
    Filter = "(type:'ip_address',type:'domain')+last_updated:>$UnixDate"
    Detailed = $true
    All = $true
}
Get-FalconIndicator @Param | Select-Object indicator, type, malicious_confidence, labels | ForEach-Object {
    [PSCustomObject] @{
        value = $_.indicator
        type = $_.type
        confidence = $_.malicious_confidence
        comment = "$(($_.Labels.name | Where-Object { $_ -notmatch 'MaliciousConfidence/*' }) -join ', ')"
    } | Export-Csv -Path .\indicators.csv -NoTypeInformation -Append
}

Users

Create users from CSV

#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.0' }
param(
    [Parameter(Mandatory = $true, Position = 1)]
    [ValidateSet('https://api.crowdstrike.com', 'https://api.us-2.crowdstrike.com',
        'https://api.laggar.gcw.crowdstrike.com', 'https://api.eu-1.crowdstrike.com')]
    [string] $BaseAddress,

    [Parameter(Mandatory = $true, Position = 2)]
    [ValidatePattern('^\w{32}$')]
    [string] $ClientId,

    [Parameter(Mandatory = $true, Position = 3)]
    [ValidatePattern('^\w{40}$')]
    [string] $ClientSecret,

    [Parameter(Position = 4)]
    [ValidatePattern('^\w{32}$')]
    [string] $MemberCid,

    [Parameter(Mandatory = $true, Position = 5)]
    [ValidatePattern('\.csv$')]
    [ValidateScript({ Test-Path $_ })]
    [string] $Path
)
$Param = @{
    Hostname     = $BaseAddress
    ClientId     = $ClientId
    ClientSecret = $ClientSecret
}
if ($MemberCid) {
    $Param['MemberCid'] = $MemberCid
}
Request-FalconToken @Param
if ((Test-FalconToken).Token -eq $true) {
    $CSV = Import-Csv $Path
    @($CSV).foreach{
        @($_.PSObject.Properties.Name).foreach{
            if ($_ -notmatch '^(Email|Firstname|Lastname|Roles)$') {
                # Error if invalid columns exist
                throw "Unexpected column. Ensure 'Email', 'Firstname', 'Lastname' and 'Roles' are present. ['$_']"
            }
        }
        if ($_.Roles) {
            # Convert Roles into an [array]
            $_.Roles = ($_.Roles -Split ',').Trim()
        }
    }
    if ($CSV.Roles -and $CSV.Roles -match '\s') {
        # Replace 'Display Names' (from console output) with role IDs
        $Roles = Get-FalconRole -Detailed
        if ($Roles) {
            @($CSV).foreach{
                $_.Roles = @($_.Roles).foreach{
                    $_ -replace $_, ($Roles | Where-Object display_name -eq $_).id
                }
            }
        }
    }
    $CSV | ForEach-Object {
        # Create user
        $User = New-FalconUser -Username $_.Email -Firstname $_.Firstname -Lastname $_.Lastname
        if ($User.uuid -and $_.Roles) {
            # Assign roles
            Add-FalconRole -UserId $User.uuid -Ids $_.Roles
        } elseif ($User.uuid) {
            # Assign 'falcon_console_guest' if roles are not present
            Add-FalconRole -UserId $User.uuid -Ids falcon_console_guest
        }
    }
}

Vulnerabilities

Create a report with additional Host fields

#Requires -Version 5.1
using module @{ ModuleName = 'PSFalcon'; ModuleVersion = '2.0' }
param(
    [Parameter(Position = 1)]
    [int] $Days = 7,

    [Parameter(Position = 2)]
    [array] $Fields = @('last_seen', 'mac_address', 'serial_number', 'external_ip')
)
if ($Fields -notcontains 'device_id') {
    # Force 'device_id' as a field to be used for matching results
    $Fields += ,'device_id'
}
# Set filename for CSV export
$ExportName = "$pwd\Vulnerabilities_$((Get-Date).AddDays(-$Days).ToString('yyyyMMdd'))_to_$(Get-Date -Format FileDate).csv"

# Gather vulnerabilities within date range (default: last 7 days) and export to CSV
$Param = @{
    Filter = "created_timestamp:>'last $Days days'"
    Detailed = $true
    All = $true
    Verbose = $true
}
Get-FalconVulnerability @Param | Export-FalconReport -Path $ExportName

if (Test-Path $ExportName) {
    # Import newly created vulnerability report
    $CSV = Import-Csv -Path $ExportName

    # Gather host ids
    $HostIds = ($CSV | Group-Object aid).Name

    # Get detailed information about hosts to append to CSV
    $Param = @{
        Ids = $HostIds
        Verbose = $true
    }
    $HostInfo = Get-FalconHost @Param | Select-Object $Fields

    foreach ($Item in $HostInfo) {
        foreach ($Result in ($CSV | Where-Object { $_.aid -eq $Item.device_id })) {
            foreach ($Property in ($Item.PSObject.Properties | Where-Object { $_.Name -ne 'device_id' })) {
                # Add $Fields from Get-FalconHost, excluding 'device_id' (already present as 'aid')
                $Result.PSObject.Properties.Add((New-Object PSNoteProperty($Property.Name, $Property.Value)))
            }
        }
    }
    # Re-export CSV with added fields
    $CSV | Export-Csv -Path $ExportName -NoTypeInformation -Force
} else {
    throw "No vulnerabilities created within the last $Days days"
}

The examples provided above are for example purposes only and are offered 'as is' with no support.


Clone this wiki locally