CrowdStrike / psfalcon

PowerShell for CrowdStrike's OAuth2 APIs
The Unlicense
369 stars 70 forks source link

[ BUG ] `Invoke-FalconDeploy` error due to execution policy restriction #432

Open hkelley opened 2 weeks ago

hkelley commented 2 weeks ago

Describe the bug When using Invoke-FalconDeploy Archive+Run argument set on a Windows sensor, a Powershell script is launched in a new Powershell.exe process but that process does not get the -ExecutionPolicy Bypass flag, so local script policies can halt execution.

runscript -Raw=```Start-Process powershell.exe '-c &{Set-Location \Windows\Temp\FalconDeploy_20241104T2247078990;\Windows\Temp\FalconDeploy_20241104T2247078990\run-.ps1}' -RedirectStandardOutput \Windows\Temp\FalconDeploy_20241104T2247078990\stdout.log -RedirectStandardError \Windows\Temp\FalconDeploy_20241104T2247078990\stderr.log -PassThru | ForEach-Object {"The process was successfully started"}```

stderr.log in the FalconDeploy folder has this:

\Windows\Temp\FalconDeploy_20241104T2247078990\run.ps1 : File 
C:\Windows\Temp\FalconDeploy_20241104T2247078990\run.ps1 cannot be loaded because running 
scripts is disabled on this system. For more information, see about_Execution_Policies at 
https:/go.microsoft.com/fwlink/?LinkID=135170.
At line:1 char:63
+ ... T2247078990;\Windows\Temp\FalconDeploy_20241104T2247078990\run...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : SecurityError: (:) [], PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess

To Reproduce Run a command like this: Invoke-FalconDeploy -Archive C:\case001\collect.zip -Run run-collector.ps1 on a Windows host where script execution policy (Get-ExecutionPolicy) is Restricted

Expected behavior Invoke-FalconDeploy should add -ExecutionPolicy Bypass to the constructed powershell.exe command line so that execution is reliable on all endpoints.

Environment (please complete the following information):

hkelley commented 2 weeks ago

At https://github.com/CrowdStrike/psfalcon/blob/c87f096a6a842904b1080c61071d8c00352c73d5/public/psf-real-time-response.ps1#L448

we can address this by changing from

                        [string]$Executable = if ($RunFile -match '\.(bat|cmd)$') {
                          'cmd.exe',"'$String'" -join ' '
                        } else {
                          'powershell.exe',"'-c &{$String}'" -join ' '
                        }

to

                        [string]$Executable = if ($RunFile -match '\.(bat|cmd)$') {
                          'cmd.exe',"'$String'" -join ' '
                        } else {
                          'powershell.exe',"'-ExecutionPolicy Bypass -c &{$String}'" -join ' '
                        }
bk-cs commented 2 weeks ago

Thanks for the report and the suggestion!

I'm weary of making that automatic because of how often the execution policy is bypassed in a malicious scenario. Would it work for you if it was optional and added to the Invoke-FalconDeploy parameters?

hkelley commented 2 weeks ago

Yes, I agree that it would be a heavy-handed default. That said, it'll be a real PITA if you're sending a package to thousands of devices and have inconsistent PS execution policies. There doesn't appear to be a good way to report the failure reason via the RTR framework (the EXE launches "successfully").

I guess this is what documentation is for.

bk-cs commented 2 weeks ago

Yes, I agree that it would be a heavy-handed default. That said, it'll be a real PITA if you're sending a package to thousands of devices and have inconsistent PS execution policies. There doesn't appear to be a good way to report the failure reason via the RTR framework (the EXE launches "successfully").

Yes, I had to make a choice when running an executable to mimic how run worked, or wait for execution like runscript. Since there was no knowing whether or not something a user runs would have output, I chose to make it work like run (i.e. start the process and don't wait for output) and tried to capture anything that might come out in those log files.

I think I can find a way so you can do something like this to add the -ExecutionPolicy Bypass switch into the script like you suggested, without automatically doing it every time:

Invoke-FalconDeploy -File .\my.exe -Argument "/install" -BypassExecutionPolicy $true
hkelley commented 2 weeks ago

That would work for me.

I'm veering off topic a bit, but your comment about run reminded me of another question - the Invoke-FalconDeploy docs say there is a 600s max time for the command to run, but if the command is run-style, there isn't really a limit, is there? From what I can see, the FalconDeploy_* folder is never cleaned up so the running files and logs could remain even after the RTR session concludes.

bk-cs commented 2 weeks ago

That would work for me.

I'm veering off topic a bit, but your comment about run reminded me of another question - the Invoke-FalconDeploy docs say there is a 600s max time for the command to run, but if the command is run-style, there isn't really a limit, is there? From what I can see, the FalconDeploy_* folder is never cleaned up so the running files and logs could remain even after the RTR session concludes.

Yes, that's generally correct, but that 600 seconds also includes the amount of time it takes for each device to start the process and notify the cloud that it's started.

Usually this only presents a problem during the put step when a transfer exceeds the maximum timeout. Invoke-FalconDeploy is designed to continue on with any devices that respond, but if the file/zip being transferred is too large, it's possible that it'll cause the put step to fail.

bk-cs commented 2 weeks ago

I've added a BypassExecPolicy switch to Invoke-FalconDeploy for the next PSFalcon release. You can apply the change to your local module by replacing public\psf-real-time-response.ps1 using these steps:

Import-Module -Name PSFalcon
$ModulePath = (Show-FalconModule).ModulePath
(Invoke-WebRequest -Uri https://raw.githubusercontent.com/CrowdStrike/psfalcon/1f00cf143585b775f9dd078d5ef7653ad3980f5d/public/psf-real-time-response.ps1 -UseBasicParsing).Content > (Join-Path (Join-Path $ModulePath public) psf-real-time-response.ps1)

Please ensure that you close and re-open PowerShell before testing. I'll leave this issue open for other users to apply this change until the next PSFalcon release.