ThreatHunting/Find-LivingOffLand.ps1
|
function Find-LivingOffLand { <# .SYNOPSIS Detects Living Off The Land (LOLBins) abuse .DESCRIPTION Identifies suspicious use of legitimate Windows binaries (LOLBins/LOLBas) that are commonly abused by attackers for execution, download, and evasion .PARAMETER CheckRunningProcesses Scan currently running processes .PARAMETER CheckRecentExecution Check recent process execution history .PARAMETER CheckScheduledTasks Check scheduled tasks for LOLBins .EXAMPLE Find-LivingOffLand -CheckRunningProcesses Find-LivingOffLand -CheckRunningProcesses -CheckRecentExecution -CheckScheduledTasks .OUTPUTS PSCustomObject with LOLBins detection results #> [CmdletBinding()] param( [Parameter()] [switch]$CheckRunningProcesses, [Parameter()] [switch]$CheckRecentExecution, [Parameter()] [switch]$CheckScheduledTasks ) begin { Write-Host "=== LIVING OFF THE LAND (LOLBins) DETECTION ===" -ForegroundColor Cyan Write-Host "Start Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Yellow if (-not ($CheckRunningProcesses -or $CheckRecentExecution -or $CheckScheduledTasks)) { $CheckRunningProcesses = $true Write-Host "No specific check selected - defaulting to CheckRunningProcesses" -ForegroundColor Yellow } $results = [PSCustomObject]@{ ScanDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" ComputerName = $env:COMPUTERNAME DetectedLOLBins = @() Summary = @{ TotalDetections = 0 CriticalFindings = 0 HighFindings = 0 MediumFindings = 0 } Recommendations = @() } # LOLBins Database with suspicious usage patterns $lolbinsDB = @{ 'certutil.exe' = @{ Description = 'Certificate utility - can download files and encode/decode' SuspiciousPatterns = @( '-urlcache.*http', '-decode', '-encode', 'split.*http' ) Severity = 'High' } 'bitsadmin.exe' = @{ Description = 'Background Intelligent Transfer Service - can download files' SuspiciousPatterns = @( '/transfer', '/download', '/addfile' ) Severity = 'High' } 'mshta.exe' = @{ Description = 'Microsoft HTML Application host - executes HTA/scripts' SuspiciousPatterns = @( 'http', 'javascript:', 'vbscript:', '\.hta' ) Severity = 'Critical' } 'regsvr32.exe' = @{ Description = 'Register Server - can execute scriptlets' SuspiciousPatterns = @( '/u /s /i:http', '/i:http', '\.sct', 'scrobj.dll' ) Severity = 'Critical' } 'rundll32.exe' = @{ Description = 'Run DLL - can execute DLL functions' SuspiciousPatterns = @( 'javascript:', 'http', 'mshtml', 'url\.dll.*OpenURL', 'advpack\.dll.*LaunchINFSection' ) Severity = 'High' } 'msiexec.exe' = @{ Description = 'Windows Installer - can download and execute MSI' SuspiciousPatterns = @( '/i.*http', '/quiet.*http', '/qn.*http' ) Severity = 'High' } 'wmic.exe' = @{ Description = 'Windows Management Instrumentation - can execute commands' SuspiciousPatterns = @( 'process call create', 'os get', '/node:', '/format:.*http' ) Severity = 'High' } 'cscript.exe' = @{ Description = 'Windows Script Host - executes scripts' SuspiciousPatterns = @( '\.vbs.*http', '\.js.*http', '//e:jscript' ) Severity = 'Medium' } 'wscript.exe' = @{ Description = 'Windows Script Host - executes scripts' SuspiciousPatterns = @( '\.vbs.*http', '\.js.*http' ) Severity = 'Medium' } 'regasm.exe' = @{ Description = 'Assembly Registration Tool - can execute code' SuspiciousPatterns = @( '/u', '\.dll' ) Severity = 'Medium' } 'regsvcs.exe' = @{ Description = '.NET Services Installation Tool - can execute code' SuspiciousPatterns = @( '/u', '\.dll' ) Severity = 'Medium' } 'installutil.exe' = @{ Description = '.NET Installer Tool - can execute code' SuspiciousPatterns = @( '/logfile=', '/logtoconsole=false', '\.dll' ) Severity = 'Medium' } 'odbcconf.exe' = @{ Description = 'ODBC Configuration - can execute DLLs' SuspiciousPatterns = @( '/a.*regsvr' ) Severity = 'Medium' } 'mavinject.exe' = @{ Description = 'Windows process injector' SuspiciousPatterns = @( '/injectrunning' ) Severity = 'Critical' } 'forfiles.exe' = @{ Description = 'Execute commands on files - can run arbitrary commands' SuspiciousPatterns = @( '/c', 'cmd' ) Severity = 'Medium' } } } process { try { # 1. Check Running Processes if ($CheckRunningProcesses) { Write-Host "`n[1/3] Scanning running processes..." -ForegroundColor Cyan try { $processes = Get-CimInstance Win32_Process | Where-Object { $null -ne $_.CommandLine } | Select-Object Name, ProcessId, ExecutablePath, CommandLine foreach ($process in $processes) { $processName = $process.Name.ToLower() if ($lolbinsDB.ContainsKey($processName)) { $lolbin = $lolbinsDB[$processName] $commandLine = $process.CommandLine # Check for suspicious patterns $matchedPatterns = @() foreach ($pattern in $lolbin.SuspiciousPatterns) { if ($commandLine -match $pattern) { $matchedPatterns += $pattern } } if ($matchedPatterns.Count -gt 0) { $detection = [PSCustomObject]@{ DetectionType = 'RunningProcess' Binary = $processName ProcessId = $process.ProcessId Description = $lolbin.Description CommandLine = if ($commandLine.Length -gt 300) { $commandLine.Substring(0, 300) + "..." } else { $commandLine } MatchedPatterns = $matchedPatterns -join ', ' Severity = $lolbin.Severity Path = $process.ExecutablePath Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" } $results.DetectedLOLBins += $detection switch ($lolbin.Severity) { 'Critical' { $results.Summary.CriticalFindings++ } 'High' { $results.Summary.HighFindings++ } 'Medium' { $results.Summary.MediumFindings++ } } Write-Host " [!] Detected: $processName (PID: $($process.ProcessId))" -ForegroundColor $( if ($lolbin.Severity -eq 'Critical') { 'Red' } elseif ($lolbin.Severity -eq 'High') { 'Red' } else { 'Yellow' } ) Write-Host " Severity: $($lolbin.Severity)" -ForegroundColor Gray Write-Host " Pattern: $($matchedPatterns -join ', ')" -ForegroundColor Gray } } } if ($results.DetectedLOLBins.Count -eq 0) { Write-Host " No suspicious LOLBins detected in running processes" -ForegroundColor Green } } catch { Write-Warning "Error checking running processes: $_" } } # 2. Check Recent Execution (Prefetch) if ($CheckRecentExecution) { Write-Host "`n[2/3] Checking recent execution history..." -ForegroundColor Cyan try { # Check prefetch folder for LOLBins execution $prefetchPath = "$env:SystemRoot\Prefetch" if (Test-Path $prefetchPath) { $prefetchFiles = Get-ChildItem -Path $prefetchPath -Filter "*.pf" -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 100 $foundCount = 0 foreach ($file in $prefetchFiles) { $fileName = $file.Name.ToLower() foreach ($lolbin in $lolbinsDB.Keys) { # Prefetch files are named "<BINARY>-<hash>.pf" (e.g. CERTUTIL.EXE-1A2B3C4D.pf) if ($fileName -like "$lolbin-*") { $detection = [PSCustomObject]@{ DetectionType = 'RecentExecution' Binary = $lolbin ProcessId = 'N/A' Description = $lolbinsDB[$lolbin].Description CommandLine = "N/A (Prefetch evidence only)" MatchedPatterns = "Execution detected via Prefetch" Severity = 'Low' Path = $file.FullName Timestamp = $file.LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss") } $results.DetectedLOLBins += $detection $foundCount++ Write-Host " [!] Recent execution: $lolbin" -ForegroundColor Yellow Write-Host " Last executed: $($file.LastWriteTime)" -ForegroundColor Gray break } } } if ($foundCount -eq 0) { Write-Host " No LOLBins detected in recent execution history" -ForegroundColor Green } } else { Write-Host " Prefetch folder not accessible" -ForegroundColor Gray } } catch { Write-Warning "Error checking recent execution: $_" } } # 3. Check Scheduled Tasks if ($CheckScheduledTasks) { Write-Host "`n[3/3] Checking scheduled tasks..." -ForegroundColor Cyan try { $tasks = Get-ScheduledTask | Where-Object { $_.State -ne 'Disabled' } $foundCount = 0 foreach ($task in $tasks) { foreach ($action in $task.Actions) { if ($action.PSObject.Properties.Name -contains 'Execute' -and $action.Execute) { # Safe executable name extraction - FIX: Handle environment variables and illegal characters $executable = $null try { # Try to expand environment variables $expandedPath = [Environment]::ExpandEnvironmentVariables($action.Execute) $executable = [System.IO.Path]::GetFileName($expandedPath).ToLower() } catch { # If path parsing fails, try regex extraction if ($action.Execute -match '([^\\/:*?"<>|]+\.exe)') { $executable = $matches[1].ToLower() } } if (-not $executable) { continue } if ($lolbinsDB.ContainsKey($executable)) { $lolbin = $lolbinsDB[$executable] $arguments = if ($action.Arguments) { $action.Arguments } else { "" } # Check for suspicious patterns $matchedPatterns = @() foreach ($pattern in $lolbin.SuspiciousPatterns) { if ($arguments -match $pattern) { $matchedPatterns += $pattern } } if ($matchedPatterns.Count -gt 0) { $detection = [PSCustomObject]@{ DetectionType = 'ScheduledTask' Binary = $executable ProcessId = 'N/A' Description = $lolbin.Description CommandLine = "$($action.Execute) $arguments" MatchedPatterns = $matchedPatterns -join ', ' Severity = $lolbin.Severity Path = "Scheduled Task: $($task.TaskName)" Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" } $results.DetectedLOLBins += $detection $foundCount++ switch ($lolbin.Severity) { 'Critical' { $results.Summary.CriticalFindings++ } 'High' { $results.Summary.HighFindings++ } 'Medium' { $results.Summary.MediumFindings++ } } Write-Host " [!] Detected in task: $($task.TaskName)" -ForegroundColor Red Write-Host " Binary: $executable" -ForegroundColor Gray Write-Host " Severity: $($lolbin.Severity)" -ForegroundColor Gray } } } } } if ($foundCount -eq 0) { Write-Host " No suspicious LOLBins detected in scheduled tasks" -ForegroundColor Green } } catch { Write-Warning "Error checking scheduled tasks: $_" } } # Calculate total detections $results.Summary.TotalDetections = $results.DetectedLOLBins.Count # Generate recommendations if ($results.Summary.TotalDetections -gt 0) { if ($results.Summary.CriticalFindings -gt 0) { $results.Recommendations += "CRITICAL: Investigate critical LOLBins detections immediately" $results.Recommendations += "Isolate affected systems from network" } $results.Recommendations += "Review command lines and execution context" $results.Recommendations += "Check for lateral movement indicators" $results.Recommendations += "Enable Sysmon or enhanced process logging" $results.Recommendations += "Implement application whitelisting (AppLocker/WDAC)" $results.Recommendations += "Monitor LOLBins usage with behavioral analytics" } } catch { Write-Error "Error during LOLBins detection: $_" throw } } end { Write-Host "`n=== SCAN COMPLETE ===" -ForegroundColor $( if ($results.Summary.CriticalFindings -gt 0) { 'Red' } elseif ($results.Summary.HighFindings -gt 0) { 'Yellow' } else { 'Green' } ) Write-Host "`nDetection Summary:" -ForegroundColor Cyan Write-Host " Total Detections: $($results.Summary.TotalDetections)" -ForegroundColor White Write-Host " Critical: $($results.Summary.CriticalFindings)" -ForegroundColor Red Write-Host " High: $($results.Summary.HighFindings)" -ForegroundColor Yellow Write-Host " Medium: $($results.Summary.MediumFindings)" -ForegroundColor Yellow if ($results.DetectedLOLBins.Count -gt 0) { Write-Host "`nDetected LOLBins:" -ForegroundColor Cyan $results.DetectedLOLBins | Group-Object Binary | Sort-Object Count -Descending | ForEach-Object { Write-Host " $($_.Name): $($_.Count) detection(s)" -ForegroundColor White } Write-Host "`nTop Detections by Severity:" -ForegroundColor Cyan $results.DetectedLOLBins | Sort-Object { switch ($_.Severity) { 'Critical' { 1 } 'High' { 2 } 'Medium' { 3 } 'Low' { 4 } } } | Select-Object -First 5 | ForEach-Object { Write-Host " [$($_.Severity)] $($_.Binary) - $($_.DetectionType)" -ForegroundColor White } } else { Write-Host "`nNo suspicious LOLBins activity detected" -ForegroundColor Green } if ($results.Recommendations.Count -gt 0) { Write-Host "`nRecommendations:" -ForegroundColor Cyan $results.Recommendations | ForEach-Object { Write-Host " ! $_" -ForegroundColor Yellow } } Write-Host "`nLOLBAS Project: https://lolbas-project.github.io/" -ForegroundColor Gray return $results } } |