Created
November 16, 2021 20:03
-
-
Save m2021acct/ee15fccd297065d8b422ea515cb4385f to your computer and use it in GitHub Desktop.
PSContain - Crowdsrike RTR
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
################################################################################## | |
# ____ _____ ______ __ _ | |
# / __ \ / ___/ / ____/ ____ ____ / /_ ____ _ (_) ____ | |
# / /_/ / \__ \ / / / __ \ / __ \ / __/ / __ `/ / / / __ \ | |
# / ____/ ___/ / / /___ / /_/ / / / / // /_ / /_/ / / / / / / / | |
# /_/ /____/ \____/ \____/ /_/ /_/ \__/ \__,_/ /_/ /_/ /_/ | |
# | |
################################################################################## | |
# | |
# This script will use the CS API to stop network connectivity on workstations that | |
# attempt to connect to the internet without a router. | |
# | |
# Logic: | |
# Load updated global host list from CS | |
# Load existing contained host list from local CSV | |
# - Remove hosts that are quarantined for more than 30 minutes or changed their local IP | |
# - Update contained hosts | |
# Parse/filter local IP addresses from updated global host list | |
# Start session with CS and run "confirmation powershell script" on workstation | |
# Contain hosts that have not been contained earlier | |
# Add newly contained hosts to CSV | |
# | |
# Done: | |
# - Create log folder and documment stuff | |
# | |
# Todo: | |
# - Send email with newly contained hosts | |
# - Warning to user they connected using bad network config - Call IT | |
# - Way to exclude workstations | |
# | |
$Logfile = ".\Logs\PSContain.log" | |
function IPInRange { | |
[cmdletbinding()] | |
[outputtype([System.Boolean])] | |
param( | |
# IP Address to find. | |
[parameter(Mandatory, | |
Position=0)] | |
[validatescript({ | |
([System.Net.IPAddress]$_).AddressFamily -eq 'InterNetwork' | |
})] | |
[string] | |
$IPAddress, | |
# Range in which to search using CIDR notation. (ippaddr/bits) | |
[parameter(Mandatory, | |
Position=1)] | |
[validatescript({ | |
$IP = ($_ -split '/')[0] | |
$Bits = ($_ -split '/')[1] | |
(([System.Net.IPAddress]($IP)).AddressFamily -eq 'InterNetwork') | |
if (-not($Bits)) { | |
throw 'Missing CIDR notiation.' | |
} elseif (-not(0..32 -contains [int]$Bits)) { | |
throw 'Invalid CIDR notation. The valid bit range is 0 to 32.' | |
} | |
})] | |
[alias('CIDR')] | |
[string] | |
$Range | |
) | |
# Split range into the address and the CIDR notation | |
[String]$CIDRAddress = $Range.Split('/')[0] | |
[int]$CIDRBits = $Range.Split('/')[1] | |
# Address from range and the search address are converted to Int32 and the full mask is calculated from the CIDR notation. | |
[int]$BaseAddress = [System.BitConverter]::ToInt32((([System.Net.IPAddress]::Parse($CIDRAddress)).GetAddressBytes()), 0) | |
[int]$Address = [System.BitConverter]::ToInt32(([System.Net.IPAddress]::Parse($IPAddress).GetAddressBytes()), 0) | |
[int]$Mask = [System.Net.IPAddress]::HostToNetworkOrder(-1 -shl ( 32 - $CIDRBits)) | |
# Determine whether the address is in the range. | |
if (($BaseAddress -band $Mask) -eq ($Address -band $Mask)) { | |
$true | |
} else { | |
$false | |
} | |
} | |
function LogWrite { | |
Param ([string]$logstring) | |
Add-content $Logfile -value $logstring | |
} | |
$detectInternal = @' | |
# Check IPs on Machine | |
# Internal IPs | |
$LocalIPs = Get-NetIPAddress | Where {$_.SuffixOrigin -eq 'Dhcp' -or $_.SuffixOrigin -eq 'Manual'} | Select IPAddress,ifIndex | |
# External IPs | |
$ExternalIP = (Invoke-WebRequest 'https://api.ipify.org' -UseBasicParsing).Content | |
if ($ExternalIP.Length -lt 7) { $ExternalIP = (Invoke-WebRequest 'https://ifconfig.me/ip' -UseBasicParsing).Content } | |
if ($ExternalIP.Length -lt 7) { $ExternalIP = (Resolve-DnsName -Name myip.opendns.com -Server 208.67.222.220).IPAddress } | |
if ($ExternalIP.Length -lt 7) { $ExternalIP = (Resolve-DnsName o-o.myaddr.l.google.com -Server ns1.google.com -Type TXT).Strings } | |
foreach ($LocalIP in $LocalIPs) { | |
#write-output $LocalIP | |
if ($LocalIP.IPAddress -eq $ExternalIP) { | |
write-output "Match" | |
Return | |
} | |
if ($ExternalIP -eq "<TEST-IP>") { | |
write-output "TestMatch" | |
Return | |
} | |
} | |
write-output "NotMatch" | |
'@ | |
############################################################################################################## | |
# Install PSFalcon(by CrowdStrike Team) | |
# Install-Module -Name PSFalcon -Scope AllUsers | |
Import-Module -Name PSFalcon | |
$ClientID="ClientID" | |
$ClientSecret="ClientSecret" | |
Request-FalconToken -Cloud 'us-2' -ClientId $ClientID -ClientSecret $ClientSecret | |
if (Test-Path .\Hosts.csv) { | |
Remove-Item '.\Hosts.csv' -Force | |
} | |
Get-FalconHost -Detailed -All | Export-FalconReport -Path .\Hosts.csv | |
<##> | |
$CSVFile = Get-Content .\Hosts.csv -TotalCount 1 | |
$Columns = $CSVFile.split(",") -replace '"' | |
$workstations = Import-Csv .\Hosts.csv -delimiter ',' -header $Columns | Select *, containment_date | Where { $_.product_type_desc -eq "Workstation" } | |
if (!(Test-Path -PathType Container '.\Logs')) { | |
New-Item -Path ".\" -Name "Logs" -ItemType "directory" -Force | |
} | |
<# | |
Import-Csv '.\Hosts.csv' | Where {$_.hostname -eq "COMPUTER1" } | |
$a = Import-Csv '.\Hosts.csv' | Where {$_.hostname -eq "COMPUTER1" } | |
[datetime]$a.last_seen | |
#> | |
# Lift containment by time - 30 minutes timeout | |
# $session = Start-FalconSession -HostIds 936d7029ea -Timeout 30 | |
# $contain = Invoke-FalconHostAction -Name lift_containment -Ids 936d7029ea | |
# Import-Csv '.\Hosts.csv' | Where {$_.status -eq "contained" } | |
LogWrite "" | |
if (Test-Path '.\ContainedHosts.csv') { | |
$alreadyContained = Import-Csv -Path .\ContainedHosts.csv | |
foreach ($containedHost in $alreadyContained) { | |
foreach ($workstation in $workstations) { | |
if(($workstation.device_id -eq $containedHost.device_id) -and ($workstation.status -eq "contained")) { | |
if($workstation.local_ip -ne $containedHost.local_ip) { | |
# Try to create a session to test if available | |
$session = Start-FalconSession -HostIds $containedHost.device_id -Timeout 30 | |
if($session.batch_id -ne $null) { | |
$contain = Invoke-FalconHostAction -Name lift_containment -Ids $containedHost.device_id | |
} | |
# work with error codes | |
Import-Csv '.\ContainedHosts.csv' | Where { $_.device_id -ne $containedHost.device_id } | Export-Csv -Force .\ContainedHosts.csv -NoTypeInformation | |
LogWrite "WORKSTATION ID REMOVED FROM CONTAINMENT - CHANGED IP: $containedHost" | |
LogWrite "" | |
} | |
if((Get-Date).AddMinutes(-30) -gt [datetime]$containedHost.containment_date) { | |
# Try to create a session to test if available | |
$session = Start-FalconSession -HostIds $containedHost.device_id -Timeout 30 | |
if($session.batch_id -ne $null) { | |
$contain = Invoke-FalconHostAction -Name lift_containment -Ids $containedHost.device_id | |
} | |
# work with error codes | |
Import-Csv '.\ContainedHosts.csv' | Where { $_.device_id -ne $containedHost.device_id } | Export-Csv -Force .\ContainedHosts.csv -NoTypeInformation | |
LogWrite "WORKSTATION ID REMOVED FROM CONTAINMENT: $containedHost" | |
LogWrite "" | |
} | |
} | |
} | |
# Timed out - Remove from list | |
if((Get-Date).AddMinutes(-30) -gt [datetime]$containedHost.containment_date) { | |
Import-Csv '.\ContainedHosts.csv' | Where { $_.device_id -ne $containedHost.device_id } | Export-Csv -Force .\ContainedHosts.csv -NoTypeInformation | |
LogWrite "WORKSTATION ID REMOVED FROM CONTAINMENT - CSV: $containedHost" | |
LogWrite "" | |
} | |
} | |
$alreadyContained = Import-Csv -Path .\ContainedHosts.csv | |
} | |
$externalIP = $false | |
foreach ($workstation in $workstations) { | |
if($workstation.local_ip -eq 'local_ip') { continue } | |
if(!(IPinRange $workstation.local_ip '192.168.0.0/16')) { | |
if(!(IPinRange $workstation.local_ip '10.0.0.0/8')) { | |
if(!(IPinRange $workstation.local_ip '172.16.0.0/12')) { | |
if(!(IPinRange $workstation.local_ip '127.0.0.1/32')) { | |
if(!(IPinRange $workstation.local_ip '169.254.0.0/16')) { | |
if(!(IPinRange $workstation.local_ip '0.0.0.0/32')) { | |
LogWrite "GOT ONE: $workstation" | |
LogWrite "" | |
# If host hasn't been contained earlier | |
$result = $alreadyContained | Where-Object{ ($_.device_id -eq $workstation.device_id) -and ($_.local_ip -eq $workstation.local_ip) } | |
if($result -eq $null){ | |
LogWrite "WORKSTATION ID IDENTIFIED FOR CONTAINMENT: $workstation" | |
LogWrite "" | |
$EncodedScript = [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($detectInternal)) | |
$session = Start-FalconSession -HostIds $workstation.device_id -Timeout 60 | |
if($session.batch_id -ne $null) { | |
$command = Invoke-FalconAdminCommand -BatchId $session.batch_id -Command runscript -Arguments "-Raw=`" powershell.exe -EncodedCommand $EncodedScript `"" | |
if(($command.stdout -eq 'Match') -or ($command.stdout -eq 'TestMatch')) { | |
$contain = Invoke-FalconHostAction -Name contain -Ids $workstation.device_id | |
# Verify containment | |
if($contain.id -eq $workstation.device_id) { | |
$workstation | Add-Member -MemberType NoteProperty -Name 'containment_date' -Value $(Get-Date) -Force | |
$workstation | Export-Csv -Append -Force .\ContainedHosts.csv -NoTypeInformation | |
LogWrite "WORKSTATION ID HAS BEEN CONTAINED: $workstation" | |
LogWrite "" | |
$externalIP = $true | |
} | |
} | |
# If didn't match, store processed host in contained list to avoid future checks / Remove host every 30 minutes | |
if($command.stdout -eq 'NotMatch') { | |
$workstation | Add-Member -MemberType NoteProperty -Name 'containment_date' -Value $(Get-Date) -Force | |
$workstation | Export-Csv -Append -Force .\ContainedHosts.csv -NoTypeInformation | |
LogWrite "WORKSTATION ID HAS NOT BEEN CONTAINED, BUT HAS BEEN ADDED: $workstation" | |
LogWrite "" | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment