Public/scripts.ps1
function Export-Report { <# .SYNOPSIS Additional information is available with the -Help parameter .LINK https://github.com/crowdstrike/psfalcon #> [CmdletBinding()] [OutputType()] param() DynamicParam { $Endpoints = @('script:ExportReport') return (Get-Dictionary -Endpoints $Endpoints -OutVariable Dynamic) } begin { if (-not $PSBoundParameters.Help) { $TimeRegex = '(^(first|last)_seen$|^.*_timestamp$|^.*_(applied|assigned)$)' $Exclusions = @{ Detection = @('hostinfo', 'behaviors_processed') Host = @('policies') Incident = @('hosts') } $TypeNames = @{ Detection = @('domain.MsaDetectSummariesResponse') DeviceControl = @('responses.DeviceControlPoliciesV1') Firewall = @('responses.FirewallPoliciesV1') Host = @('domain.DeviceDetailsResponseSwagger', 'responses.HostGroupMembersV1', 'responses.PolicyMembersRespV1') HostGroup = @('responses.HostGroupsV1') Identifier = @('binservclient.MsaPutFileResponse', 'domain.DeviceResponse', 'domain.SPAPIQueryVulnerabilitiesResponse', 'api.MsaIncidentQueryResponse', 'msa.QueryResponse') Incident = @('api.MsaExternalIncidentResponse') IOC = @('api.MsaReplyIOCIDs', 'api.MsaReplyIOC') Prevention = @('responses.PreventionPoliciesV1') PutFile = @('binservclient.MsaPFResponse') SensorUpdate = @('responses.SensorUpdatePoliciesV2') User = @('domain.UserMetaDataResponse') Vulnerability = @('domain.SPAPIVulnerabilitiesEntitiesResponseV2') } function Add-Field ($Object, $Name, $Value) { $Value = if ($Value -and $Name -match $TimeRegex) { [datetime] $Value } elseif (($Value -is [object[]]) -and ($Value[0] -is [string])) { $Value -join ', ' } else { $Value } $Object.PSObject.Properties.Add((New-Object PSNoteProperty($Name, $Value))) } function Get-SimpleObject ($Object) { $Object | ForEach-Object { $Item = [PSCustomObject] @{} $_.PSObject.Properties | ForEach-Object { Add-Field -Object $Item -Name $_.Name -Value $_.Value } $Item } } } } process { if ($PSBoundParameters.Help) { Get-DynamicHelp -Command $MyInvocation.MyCommand.Name } else { $Output = switch (($Meta.PSObject.TypeNames).Where({ $_ -notmatch '^System.*$' })) { { $TypeNames.Detection -contains $_ } { $PSBoundParameters.Object | ForEach-Object { $Item = [PSCustomObject] @{} $Param = @{ Object = $Item } $_.PSObject.Properties | ForEach-Object { if ($_.Name -eq 'device') { Add-Field @Param -Name 'device_id' -Value $_.Value.device_id } elseif ($_.Name -eq 'behaviors') { $TTP = $_.Value | ForEach-Object { "$($_.tactic_id):$($_.technique_id)" } Add-Field @Param -Name 'tactic_and_technique' -Value ($TTP -join ', ') } elseif ($_.Name -eq 'quarantined_files') { Add-Field @Param -Name 'quarantined_files' -Value $_.Value.id } elseif ($Exclusions.Detection -notcontains $_.Name) { Add-Field @Param -Name $_.Name -Value $_.Value } } $Item } } { $TypeNames.DeviceControl -contains $_ } { $PSBoundParameters.Object | ForEach-Object { $Item = [PSCustomObject] @{} $Param = @{ Object = $Item } $_.PSObject.Properties | ForEach-Object { if ($_.Name -eq 'groups') { Add-Field @Param -Name $_.Name -Value ($_.Value.id -join ', ') } elseif ($_.Name -eq 'settings') { Add-Field @Param -Name 'enforcement_mode' -Value $_.Value.enforcement_mode Add-Field @Param -Name 'end_user_notification' -Value $_.Value.end_user_notification } else { Add-Field @Param -Name $_.Name -Value $_.Value } } $Item } } { $TypeNames.Firewall -contains $_ } { $PSBoundParameters.Object | ForEach-Object { $Item = [PSCustomObject] @{} $Param = @{ Object = $Item } $_.PSObject.Properties | ForEach-Object { if ($_.Name -eq 'groups') { Add-Field @Param -Name $_.Name -Value ($_.Value.id -join ', ') } else { Add-Field @Param -Name $_.Name -Value $_.Value } } $Item } } { $TypeNames.Host -contains $_ } { $PSBoundParameters.Object | ForEach-Object { $Item = [PSCustomObject] @{} $Param = @{ Object = $Item } $_.PSObject.Properties | ForEach-Object { if ($_.Name -eq 'device_policies') { $_.Value.psobject.properties | ForEach-Object { Add-Field @Param -Name "$($_.Name)_id" -Value $_.Value.policy_id Add-Field @Param -Name "$($_.Name)_assigned" -Value $_.Value.assigned_date $Applied = if ($_.Value.applied -eq $true) { $_.Value.applied_date } else { $null } Add-Field @Param -Name "$($_.Name)_applied" -Value $Applied if ($_.Value.uninstall_protection) { Add-Field @Param -Name 'uninstall_protection' -Value ( $_.Value.uninstall_protection) } } } elseif ($_.Name -eq 'meta') { Add-Field @Param -Name "$($_.Name)_version" -Value $_.Value.version } elseif ($Exclusions.Host -notcontains $_.Name) { Add-Field @Param -Name $_.Name -Value $_.Value } } $Item } } { $TypeNames.HostGroup -contains $_ } { Get-SimpleObject -Object $PSBoundParameters.Object } { $TypeNames.Identifier -contains $_ } { $PSBoundParameters.Object | ForEach-Object { [PSCustomObject] @{ id = $_ } } } { $TypeNames.Incident -contains $_ } { $PSBoundParameters.Object | ForEach-Object { $Item = [PSCustomObject] @{} $_.PSObject.Properties | ForEach-Object { if ($Exclusions.Incident -notcontains $_.Name) { Add-Field -Object $Item -Name $_.Name -Value $_.Value } } $Item } } { $TypeNames.IOC -contains $_ } { if ($_ -eq 'api.MsaReplyIOCIDs') { $PSBoundParameters.Object | ForEach-Object { [PSCustomObject] @{ type = ($_).Split(':')[0] value = ($_).Split(':')[1] } } } else { Get-SimpleObject -Object $PSBoundParameters.Object } } { $TypeNames.Prevention -contains $_ } { $PSBoundParameters.Object | ForEach-Object { $Item = [PSCustomObject] @{} $Param = @{ Object = $Item } $_.PSObject.Properties | ForEach-Object { if ($_.Name -eq 'groups') { Add-Field @Param -Name $_.Name -Value ($_.Value.id -join ', ') } elseif ($_.Name -eq 'prevention_settings') { $_.Value.settings | ForEach-Object { if ($_.type -eq 'toggle') { Add-Field @Param -Name $_.id -Value $_.Value.enabled } else { Add-Field @Param -Name $_.id -Value ( "$($_.Value.detection):$($_.Value.prevention)") } } } else { Add-Field @Param -Name $_.Name -Value $_.Value } } $Item } } { $TypeNames.PutFile -contains $_ } { Get-SimpleObject -Object $PSBoundParameters.Object } { $TypeNames.SensorUpdate -contains $_ } { $PSBoundParameters.Object | ForEach-Object { $Item = [PSCustomObject] @{} $Param = @{ Object = $Item } $_.PSObject.Properties | ForEach-Object { if ($_.Name -eq 'groups') { Add-Field @Param -Name $_.Name -Value ($_.Value.id -join ', ') } elseif ($_.Name -eq 'settings') { $_.Value.psobject.properties | ForEach-Object { Add-Field @Param -Name $_.Name -Value $_.Value } } else { Add-Field @Param -Name $_.Name -Value $_.Value } } $Item } } { $TypeNames.User -contains $_ } { Get-SimpleObject -Object $PSBoundParameters.Object } { $TypeNames.Vulnerability -contains $_ } { $PSBoundParameters.Object | ForEach-Object { $Item = [PSCustomObject] @{} $Param = @{ Object = $Item } $_.PSObject.Properties | ForEach-Object { if ($_.Name -eq 'cve') { $_.Value.psobject.properties | ForEach-Object { Add-Field @Param -Name "cve_$($_.Name)" -Value $_.Value } } elseif ($_.Name -eq 'app') { $_.Value.psobject.properties | ForEach-Object { Add-Field @Param -Name $_.Name -Value $_.Value } } elseif ($_.Name -eq 'host_info') { $_.Value.psobject.properties | ForEach-Object { if ($_.Name -eq 'groups') { Add-Field @Param -Name $_.Name -Value ($_.Value.name -join ', ') } else { Add-Field @Param -Name $_.Name -Value $_.Value } } } elseif ($_.Name -eq 'remediation') { Add-Field @Param -Name "remediation_ids" -Value ($_.Value.ids -join ', ') } else { Add-Field @Param -Name $_.Name -Value $_.Value } } $Item } } } if ($Output) { $Output | Export-Csv -Path $PSBoundParameters.Path -NoTypeInformation -Append -Force } else { Write-Error "CSV conversion is not available for this request type" } } } } function Find-Duplicate { <# .SYNOPSIS Additional information is available with the -Help parameter .LINK https://github.com/crowdstrike/psfalcon #> [CmdletBinding()] [OutputType()] param() DynamicParam { $Endpoints = @('script:FindDuplicate') return (Get-Dictionary -Endpoints $Endpoints -OutVariable Dynamic) } begin { if (-not $PSBoundParameters.Help) { $Criteria = @('device_id', 'hostname', 'first_seen', 'last_seen') $InputFields = ($PSBoundParameters.Hosts | Get-Member -MemberType NoteProperty).Name function Group-Selection ($Selection, $Criteria) { ((($Selection | Group-Object $Criteria).Where({ $_.Count -gt 1 })).Group | Group-Object $Criteria).foreach{ $_.Group | Sort-Object last_seen | Select-Object -First (($_.Count) - 1) } } } } process { if ($PSBoundParameters.Help) { Get-DynamicHelp -Command $MyInvocation.MyCommand.Name } else { try { ($Criteria).foreach{ if ($InputFields -notcontains $_) { throw "Input object does not contain '$_' field" } } $Param = @{ Selection = $PSBoundParameters.Hosts | Select-Object $Criteria Criteria = 'hostname' } $Duplicates = Group-Selection @Param if ($Duplicates) { $Duplicates } else { Write-Warning "No duplicates found" } } catch { $_ } } } } function Get-Queue { <# .SYNOPSIS Additional information is available with the -Help parameter .LINK https://github.com/crowdstrike/psfalcon #> [CmdletBinding(DefaultParameterSetName = 'script:GetQueue')] [OutputType()] param() DynamicParam { $Endpoints = @('script:GetQueue') return (Get-Dictionary -Endpoints $Endpoints -OutVariable Dynamic) } begin { $Days = if (-not $PSBoundParameters.Days) { 7 } else { $PSBoundParameters.Days } $FileDateTime = Get-Date -Format FileDateTime $OutputFile = "$pwd\FalconQueue_$FileDateTime.csv" $RequiresResponder = @('cp', 'encrypt', 'get', 'kill', 'map', 'memdump', 'mkdir', 'mv', 'reg delete', 'reg load', 'reg set', 'reg unload', 'restart', 'rm', 'runscript', 'shutdown', 'umount', 'unmap', 'xmemdump', 'zip') $RequiresAdmin = @('put', 'run') function Add-Field ($Object, $Name, $Value) { $Object.PSObject.Properties.Add((New-Object PSNoteProperty($Name, $Value))) } } process { if ($PSBoundParameters.Help) { Get-DynamicHelp -Command $MyInvocation.MyCommand.Name } else { try { $Param = @{ Filter = ("(deleted_at:null+commands_queued:1),(created_at:>'Last $Days days'+" + "commands_queued:1)") All = $true Verbose = $true } Get-FalconSession @Param | ForEach-Object { $Param = @{ Ids = $_ Queue = $true Verbose = $true } Get-FalconSession @Param | ForEach-Object { foreach ($Session in $_) { $Session.Commands | ForEach-Object { $Object = [PSCustomObject] @{ aid = $Session.aid user_id = $Session.user_id user_uuid = $Session.user_uuid session_id = $Session.id session_created_at = $Session.created_at session_deleted_at = $Session.deleted_at session_updated_at = $Session.updated_at session_status = $Session.status command_complete = $false command_stdout = $null command_stderr = $null } $_.PSObject.Properties | ForEach-Object { $Name = if ($_.Name -match '(created_at|deleted_at|status|updated_at)') { "command_$($_.Name)" } else { $_.Name } $Object.PSObject.Properties.Add((New-Object PSNoteProperty($Name, $_.Value))) } if ($Object.command_status -eq 'FINISHED') { $Permission = if ($RequiresAdmin -contains $Object.base_command) { 'Admin' } elseif ($RequiresResponder -contains $Object.base_command) { 'Responder' } else { $null } $Param = @{ CloudRequestId = $Object.cloud_request_id Verbose = $true ErrorAction = 'SilentlyContinue' } $CmdResult = & "Confirm-Falcon$($Permission)Command" @Param if ($CmdResult) { ($CmdResult | Select-Object stdout, stderr, complete).PSObject.Properties | ForEach-Object { $Object."command_$($_.Name)" = $_.Value } } } $Object | Export-Csv $OutputFile -Append -NoTypeInformation -Force } } } } } catch { $_ } finally { if (Test-Path $OutputFile) { Get-ChildItem $OutputFile | Out-Host } } } } } function Invoke-Deploy { <# .SYNOPSIS Additional information is available with the -Help parameter .LINK https://github.com/crowdstrike/psfalcon #> [CmdletBinding()] [OutputType()] param() DynamicParam { $Endpoints = @('script:InvokeDeploy') return (Get-Dictionary -Endpoints $Endpoints -OutVariable Dynamic) } begin { $Max = 500 $FileDateTime = Get-Date -Format FileDateTime $OutputFile = "$pwd\FalconDeploy_$FileDateTime.csv" $FilePath = $Falcon.GetAbsolutePath($Dynamic.Path.Value) $Filename = "$([System.IO.Path]::GetFileName($FilePath))" $ProcessName = "$([System.IO.Path]::GetFileNameWithoutExtension($FilePath))" function Write-Result ($Object, $Step, $BatchId) { $Output = foreach ($Item in $Object) { [PSCustomObject] @{ aid = $Item.aid batch_id = $BatchId session_id = $null cloud_request_id = $null complete = $false stdout = $null stderr = $null errors = $null offline_queued = $false deployment_step = $Step } } foreach ($Result in ($Object | Select-Object aid, session_id, task_id, complete, stdout, stderr, errors, offline_queued)) { $Result.PSObject.Properties | ForEach-Object { $Value = if (($_.Name -eq 'errors') -and $_.Value) { "$($_.Value.code): $($_.Value.message)" } else { $_.Value } $Name = if ($_.Name -eq 'task_id') { 'cloud_request_id' } else { $_.Name } $Output | Where-Object { $_.aid -eq $Result.aid } | ForEach-Object { $_.$Name = $Value } } } $Output | Export-Csv $OutputFile -Append -NoTypeInformation } if (-not $PSBoundParameters.Help -and $FilePath) { try { Write-Host "Checking cloud for existing file..." $CloudFile = foreach ($Item in ( Get-FalconPutFile -Filter "name:['$Filename']" -Detailed | Select-Object id, name, created_timestamp, modified_timestamp, sha256)) { [ordered] @{ id = $Item.id name = $Item.Name created_timestamp = [datetime] $Item.created_timestamp modified_timestamp = [datetime] $Item.modified_timestamp sha256 = $Item.sha256 } } if ($CloudFile) { $LocalFile = foreach ($Item in (Get-ChildItem $FilePath | Select-Object CreationTime, Name, LastWriteTime)) { [ordered] @{ name = $Item.Name created_timestamp = [datetime] $Item.CreationTime modified_timestamp = [datetime] $Item.LastWriteTime sha256 = ((Get-FileHash -Algorithm SHA256 -Path $FilePath).Hash).ToLower() } } if ($LocalFile.sha256 -eq $CloudFile.sha256) { Write-Host "Matched hash values between local and cloud files..." } else { foreach ($Item in @('CloudFile', 'LocalFile')) { Write-Host "[$($Item -replace 'File', $null)]" (Get-Variable $Item).Value | Select-Object name, created_timestamp, modified_timestamp, sha256 | Format-List | Out-Host } $FileChoice = $host.UI.PromptForChoice( "$Filename exists in your 'Put Files'. Use the existing version?", $null, [System.Management.Automation.Host.ChoiceDescription[]] @("&Yes", "&No"), 0) if ($FileChoice -eq 0) { Write-Host "Proceeding with $($CloudFile.id)..." } else { $RemovePut = Remove-FalconPutFile -FileId $CloudFile.id if ($RemovePut.resources_affected -eq 1) { Write-Host "Removed cloud file $($CloudFile.id)" } } } } } catch { $_ } } } process { if ($PSBoundParameters.Help) { Get-DynamicHelp -Command $MyInvocation.MyCommand.Name } elseif (-not $FilePath) { Write-Error "Cannot find path '$($Dynamic.Path.Value)' because it does not exist." } else { try { if (($RemovePut.resources_affected -eq 1) -or (-not $CloudFile)) { Write-Host "Uploading $Filename..." $Param = @{ Path = $FilePath Name = $Filename Description = "$ProcessName" Comment = "PSFalcon: Invoke-FalconDeploy" } $AddPut = Send-FalconPutFile @Param if ($AddPut.resources_affected -ne 1) { break } } for ($i = 0; $i -lt $PSBoundParameters.HostIds.count; $i += $Max) { $Param = @{ HostIds = $PSBoundParameters.HostIds[$i..($i + ($Max - 1))] } switch -Regex ($PSBoundParameters.Keys) { '(QueueOffline|Timeout)' { if ($PSBoundParameters.$_) { $Param[$_] = $PSBoundParameters.$_ } } } $Session = Start-FalconSession @Param if ($Session) { $Param = @{ Object = $Session.hosts Step = 'session_start' BatchId = $Session.batch_id } Write-Result @Param $SessionHosts = ($Session.hosts | Where-Object { ($_.complete -eq $true) -or ($_.offline_queued -eq $true) }).aid } if ($SessionHosts) { Write-Host "Pushing $Filename to $($SessionHosts.count) host(s)..." $Param = @{ BatchId = $Session.batch_id Command = 'put' Arguments = "$Filename" OptionalHostIds = $SessionHosts } if ($PSBoundParameters.Timeout) { $Param['Timeout'] = $PSBoundParameters.Timeout } $CmdPut = Invoke-FalconAdminCommand @Param if ($CmdPut) { $Param = @{ Object = $CmdPut Step = 'put_file' BatchId = $Session.batch_id } Write-Result @Param $PutHosts = ($CmdPut | Where-Object { ($_.stdout -eq 'Operation completed successfully.') -or ($_.offline_queued -eq $true) }).aid } } if ($PutHosts) { Write-Host "Starting $Filename on $($PutHosts.count) host(s)..." $Arguments = "\$Filename" if ($PSBoundParameters.Arguments) { $Arguments += " -CommandLine=`"$($PSBoundParameters.Arguments)`"" } $Param = @{ BatchId = $Session.batch_id Command = 'run' Arguments = $Arguments OptionalHostIds = $PutHosts } if ($PSBoundParameters.Timeout) { $Param['Timeout'] = $PSBoundParameters.Timeout } $CmdRun = Invoke-FalconAdminCommand @Param if ($CmdRun) { $Param = @{ Object = $CmdRun Step = 'run_file' BatchId = $Session.batch_id } Write-Result @Param } } } } catch { $_ } finally { if (Test-Path $OutputFile) { Get-ChildItem $OutputFile | Out-Host } } } } } function Invoke-RTR { <# .SYNOPSIS Additional information is available with the -Help parameter .LINK https://github.com/crowdstrike/psfalcon #> [CmdletBinding()] [OutputType()] param() DynamicParam { $Endpoints = @('script:InvokeRTR') return (Get-Dictionary -Endpoints $Endpoints -OutVariable Dynamic) } begin { if (-not $PSBoundParameters.Help) { # Max number of hosts per session $MaxHosts = 500 # Sleep time, and max time to sleep when interacting with a single host $Sleep = 2 $MaxSleep = 30 # Commands segregated by permission level $Responder = @("cp","encrypt","get","kill","map","memdump","mkdir","mv","reg delete","reg load", "reg set","reg unload","restart","rm","shutdown","umount","unmap","update history", "update install","update list","update install","xmemdump","zip") $Admin = @("put","run","runscript") # Determine permission level from input command $Permission = switch ($PSBoundParameters.Command) { { $Admin -contains $_ } { 'Admin' } { $Responder -contains $_ } { 'Responder' } default { $null } } # Force 'Timeout' into 'Arguments' when using 'runscript' if ($PSBoundParameters.Command -match 'runscript' -and $PSBoundParameters.Timeout -and ($PSBoundParameters.Arguments -notmatch "-Timeout=\d{2,3}")) { $PSBoundParameters.Arguments += " -Timeout=$($PSBoundParameters.Timeout)" if ($HostCount -eq 1) { $MaxSleep = $PSBoundParameters.Timeout } } $InvokeCmd = if ($PSBoundParameters.Command -eq 'get' -and $PSBoundParameters.HostIds.count -gt 1) { # Set command for 'get' with multiple hosts "Invoke-FalconBatchGet" } else { # Set command "Invoke-Falcon$($Permission)Command" } if ($PSBoundParameters.HostIds.count -eq 1) { # Set confirmation command to match $ConfirmCmd = "Confirm-Falcon$($Permission)Command" } function Write-Result ($Object) { $Object.PSObject.Properties | ForEach-Object { $Value = if (($_.Value -is [object[]]) -and ($_.Value[0] -is [string])) { # Convert array results into strings $_.Value -join ', ' } elseif ($_.Value.code -and $_.Value.message) { # Convert error code and message into string "$($_.Value.code): $($_.Value.message)" } else { $_.Value } $Name = if ($_.Name -eq 'task_id') { # Rename 'task_id' 'cloud_request_id' } elseif ($_.Name -eq 'queued_command_offline') { # Rename 'queued_command_offline' 'offline_queued' } else { $_.Name } $Item = if ($Object.aid) { # Match using 'aid' for batches $Output | Where-Object { $_.aid -eq $Object.aid } } else { # Assume single host $Output[0] } if ($Item.PSObject.Properties.Name -contains $Name) { # Add result to output $Item.$Name = $Value } } } } } process { if ($PSBoundParameters.Help) { Get-DynamicHelp -Command $MyInvocation.MyCommand.Name } else { for ($i = 0; $i -lt $PSBoundParameters.HostIds.count; $i += $MaxHosts) { try { [array] $Output = ($PSBoundParameters.HostIds[$i..($i + ($MaxHosts - 1))]).foreach{ # Create base output object for each host [PSCustomObject] @{ aid = $_ session_id = $null cloud_request_id = $null complete = $false offline_queued = $false stdout = $null stderr = $null errors = $null } } # Determine total number of hosts and set request parameters $HostParam = if ($Output.aid.count -eq 1) { 'HostId' } else { 'HostIds' } $Param = @{ $HostParam = $Output.aid } switch ($PSBoundParameters.Keys) { 'QueueOffline' { $Param['QueueOffline'] = $PSBoundParameters.$_ } 'Timeout' { if ($HostParam -eq 'HostIds') { $Param['Timeout'] = $PSBoundParameters.$_ } } } # Start session and capture results $Init = Start-FalconSession @Param if ($Init) { $Content = if ($Init.hosts) { $Init.hosts } else { $Init } $Content | ForEach-Object { Write-Result -Object $_ } if ($Init.batch_id) { $Output | Where-Object { $_.session_id } | ForEach-Object { # Add batch_id $_.PSObject.Properties.Add( (New-Object PSNoteProperty('batch_id', $Init.batch_id))) } } # Set command parameters based on init result $SessionType = if ($HostParam -eq 'HostIds') { 'BatchId' $IdValue = $Init.batch_id } else { 'SessionId' $IdValue = $Init.session_id } $Param = @{ $SessionType = $IdValue } switch ($PSBoundParameters.Keys) { # Add user input to command parameters 'Command' { if ($InvokeCmd -ne 'Invoke-FalconBatchGet') { $Param[$_] = $PSBoundParameters.$_ } } 'Arguments' { if ($InvokeCmd -eq 'Invoke-FalconBatchGet') { $Param['FilePath'] = $PSBoundParameters.$_ } else { $Param[$_] = $PSBoundParameters.$_ } } 'Timeout' { if ($SessionType -eq 'BatchId') { $Param[$_] = $PSBoundParameters.$_ } } } # Perform command request $Request = & $InvokeCmd @Param } if ($Request -and $InvokeCmd -eq 'Invoke-FalconBatchGet') { $Output | Where-Object { $_.session_id } | ForEach-Object { # Add 'batch_get_cmd_req_id' for batch 'get' requests $_.PSObject.Properties.Add((New-Object PSNoteProperty( 'batch_get_cmd_req_id', $Request.batch_get_cmd_req_id))) } # Capture results $Request | ForEach-Object { Write-Result -Object $_ } $Output | ForEach-Object { if ($_.stdout -eq 'C:\') { # Remove 'stdout' from initial 'pwd' command to reduce confusion when using 'get' $_.stdout = $null } } # Output result $Output } elseif ($Request -and $HostParam -eq 'HostIds') { # Capture results and output $Request | ForEach-Object { Write-Result -Object $_ } $Output } elseif ($Request) { # Capture results Write-Result -Object $Request if ($Output.cloud_request_id -and $Output.complete -eq $false -and $Output.offline_queued -eq $false) { do { # Loop command confirmation using intervals of $Sleep Start-Sleep -Seconds $Sleep $Confirm = & $ConfirmCmd -CloudRequestId $Output.cloud_request_id Write-Result -Object $Confirm $i += $Sleep } until ( # Break if command is complete or $MaxSleep is reached ($Output[0].complete -eq $true) -or ($i -ge $MaxSleep) ) } # Output results $Output } } catch { $_ } } } } } function Open-Stream { <# .SYNOPSIS Additional information is available with the -Help parameter .LINK https://github.com/crowdstrike/psfalcon #> [CmdletBinding(DefaultParameterSetName = 'script:OpenStream')] [OutputType()] param() DynamicParam { $Endpoints = @('script:OpenStream') return (Get-Dictionary -Endpoints $Endpoints -OutVariable Dynamic) } process { if ($PSBoundParameters.Help) { Get-DynamicHelp -Command $MyInvocation.MyCommand.Name } elseif (($PSVersionTable.PSVersion.Major -lt 6) -or ($IsWindows -eq $true)) { try { $Stream = Get-FalconStream -AppId 'psfalcon' -Format json if ($Stream) { $ArgumentList = "try { `$Param = @{ Uri = '$($Stream.datafeedURL)' Method = 'get' Headers = @{ accept = 'application/json' authorization = 'Token $($Stream.sessionToken.token)' } OutFile = '$($pwd)\Stream_$(Get-Date -Format FileDateTime).json' } Invoke-WebRequest @Param } catch { Write-Output `$_ | Out-File `$FilePath }" Start-Process -FilePath powershell.exe -ArgumentList $ArgumentList } } catch { $_ } } else { throw "This command is only compatible with PowerShell on Windows" } } } function Search-MalQueryHash { <# .SYNOPSIS Additional information is available with the -Help parameter .LINK https://github.com/crowdstrike/psfalcon #> [CmdletBinding()] [OutputType()] param() DynamicParam { $Endpoints = @('script:MalQueryHash') return (Get-Dictionary -Endpoints $Endpoints -OutVariable Dynamic) } begin { $Sleep = 5 $MaxSleep = 30 $Search = 'Invoke-FalconMalQuery' $Confirm = 'Get-FalconMalQuery' } process { if ($PSBoundParameters.Help) { Get-DynamicHelp -Command $MyInvocation.MyCommand.Name } else { try { $Param = @{ YaraRule = "import `"hash`"`nrule SearchHash`n{`ncondition:`nhash.sha256(0, filesize) == " + "`"$($PSBoundParameters.Sha256)`"`n}" FilterMeta = 'sha256', 'type', 'label', 'family' } $Request = & $Search @Param if ($Request.reqid) { $Param = @{ Ids = $Request.reqid OutVariable = 'Result' } if ((& $Confirm @Param).status -EQ 'inprogress') { do { Start-Sleep -Seconds $Sleep $i += $Sleep } until ( ((& $Confirm @Param).status -NE 'inprogress') -or ($i -ge $MaxSleep) ) } $Result } } catch { $_ } } } } function Show-Map { <# .SYNOPSIS Additional information is available with the -Help parameter .LINK https://github.com/crowdstrike/psfalcon #> [CmdletBinding()] [OutputType()] param() DynamicParam { $Endpoints = @('script:ShowMap') return (Get-Dictionary -Endpoints $Endpoints -OutVariable Dynamic) } begin { $FalconUI = "$($Falcon.Hostname -replace 'api', 'falcon')" } process { if ($PSBoundParameters.Help) { Get-DynamicHelp -Command $MyInvocation.MyCommand.Name } else { $Param = Get-Param -Endpoint $Endpoints[0] -Dynamic $Dynamic $Param.Query = $Param.Query | ForEach-Object { $Split = $_ -split ':' $Type = switch ($Split[0]) { 'sha256' { 'hash' } 'md5' { 'hash' } 'ipv4' { 'ip' } 'ipv6' { 'ip' } 'domain' { 'domain' } } "$($Type):'$($Split[1])'" } Start-Process "$($FalconUI)$($Falcon.Endpoints($Endpoints[0]).path)$($Param.Query -join ',')" } } } function Show-Module { <# .SYNOPSIS Additional information is available with the -Help parameter .LINK https://github.com/crowdstrike/psfalcon #> [CmdletBinding(DefaultParameterSetName = 'script:ShowModule')] [OutputType()] param() DynamicParam { $Endpoints = @('script:ShowModule') return (Get-Dictionary -Endpoints $Endpoints -OutVariable Dynamic) } process { if ($PSBoundParameters.Help) { Get-DynamicHelp -Command $MyInvocation.MyCommand.Name } else { $Parent = Split-Path -Path $Falcon.GetAbsolutePath($PSScriptRoot) -Parent if (Test-Path "$Parent\PSFalcon.psd1") { $Module = Import-PowerShellDataFile $Parent\PSFalcon.psd1 [PSCustomObject] @{ ModuleVersion = "v$($Module.ModuleVersion) {$($Module.GUID)}" ModulePath = $Parent UserHome = $HOME UserPSModulePath = ($env:PSModulePath -split ';') -join ', ' UserSystem = ("PowerShell $($PSVersionTable.PSEdition): v$($PSVersionTable.PSVersion)" + " [$($PSVersionTable.OS)]") } | Format-List } else { throw "PSFalcon.psd1 missing from default location" } } } } function Test-Token { <# .SYNOPSIS Additional information is available with the -Help parameter .LINK https://github.com/crowdstrike/psfalcon #> [CmdletBinding(DefaultParameterSetName = 'script:TestToken')] [OutputType()] param() DynamicParam { $Endpoints = @('script:TestToken') return (Get-Dictionary -Endpoints $Endpoints -OutVariable Dynamic) } process { if ($PSBoundParameters.Help) { Get-DynamicHelp -Command $MyInvocation.MyCommand.Name } else { [PSCustomObject] @{ Token = if ($Falcon.Token -and ($Falcon.Expires -gt (Get-Date).AddSeconds(30))) { $true } else { $false } Hostname = $Falcon.Hostname ClientId = $Falcon.ClientId MemberCid = $Falcon.MemberCid } } } } |