Skip to content

Instantly share code, notes, and snippets.

@m2021acct
Created November 16, 2021 20:03
Show Gist options
  • Save m2021acct/ee15fccd297065d8b422ea515cb4385f to your computer and use it in GitHub Desktop.
Save m2021acct/ee15fccd297065d8b422ea515cb4385f to your computer and use it in GitHub Desktop.
PSContain - Crowdsrike RTR
##################################################################################
# ____ _____ ______ __ _
# / __ \ / ___/ / ____/ ____ ____ / /_ ____ _ (_) ____
# / /_/ / \__ \ / / / __ \ / __ \ / __/ / __ `/ / / / __ \
# / ____/ ___/ / / /___ / /_/ / / / / // /_ / /_/ / / / / / / /
# /_/ /____/ \____/ \____/ /_/ /_/ \__/ \__,_/ /_/ /_/ /_/
#
##################################################################################
#
# 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