In this section
PowerShell for Incident Response
Collection tools are installed. Cloud access is configured. This section adds the automation and response layer — PowerShell 7 with the Microsoft Graph and Exchange Online modules. If PowerShell 7 and the Graph modules are already installed, skip to the validation commands.
PowerShell is the universal fallback — and for cloud identity operations, it is the primary tool. Every Windows system and every M365 tenant supports it.
Scenario
Every Windows system has PowerShell. Every M365 tenant supports PowerShell management modules. When KAPE is unavailable, Velociraptor is not deployed, and the Defender portal is down for maintenance, PowerShell still works. For cloud investigation operations like bulk session revocation, audit log export, and identity queries, it is the primary tool.
PowerShell for Incident Response
The tool that is always available — even when nothing else is
You are responding to an incident on a system where KAPE has not been deployed. Velociraptor is not installed. You have no forensic tools on this endpoint. But you have PowerShell — and with PowerShell, you can collect running processes, network connections, scheduled tasks, service lists, registry values, event log entries, and file metadata. It is not as efficient as purpose-built forensic tools, but it is universally available on every Windows system and it can keep the investigation moving while you deploy the proper toolkit.
PowerShell is not a forensic tool in the same way KAPE and EZTools are forensic tools. It does not parse binary artifacts or build timelines. What PowerShell provides is direct access to the operating system and the M365 management plane — the ability to query, collect, and act on any system the investigator can authenticate to.
In the IR context, PowerShell serves three roles:
Live evidence collection. When KAPE is not available on the target system, PowerShell can collect the same artifacts — event logs, running processes, network connections, scheduled tasks, service configurations, registry keys — through native cmdlets. The collection is less structured than KAPE output, but it provides immediate evidence without deploying any tool.
Containment actions. Disabling a compromised user account, revoking active sessions, blocking an IP address, isolating a device, removing a malicious inbox rule — these containment actions are executed through PowerShell cmdlets and Microsoft Graph API calls. The investigator who can execute containment in PowerShell does not need to navigate three different admin portals to contain a single incident.
Automation. Repetitive investigation tasks — checking 50 mailboxes for a specific inbox rule, querying sign-in logs for 20 compromised accounts, collecting Prefetch files from 10 endpoints — can be scripted in PowerShell and executed in minutes instead of hours.
PowerShell remoting setup
PowerShell remoting enables command execution on remote systems. This is how you collect evidence from an endpoint across the network without physically accessing it (when Velociraptor is not deployed). Understanding remoting is critical because it underpins multiple IR workflows: remote KAPE deployment (IR1.2), remote evidence collection when dedicated tools are unavailable, batch containment across multiple accounts, and automated scope determination across the fleet.
PowerShell remoting uses the WinRM (Windows Remote Management) protocol, which communicates over HTTP (port 5985) or HTTPS (port 5986). In Active Directory domain environments, WinRM is typically enabled by default or through Group Policy. In standalone environments, it must be enabled manually on the target. The connection uses Kerberos authentication in domain environments or NTLM with CredSSP in workgroup environments.
# Enable PowerShell remoting on the forensic workstation
# Run as Administrator
Enable-PSRemoting -Force
# Verify WinRM service is running
Get-Service WinRM | Select-Object Status, StartType
# Expected: Running, Automatic
# Test connectivity to a remote system before attempting a session
Test-WSMan -ComputerName "DESKTOP-NGE042"
# If this fails: the target's WinRM is not enabled, the firewall
# blocks port 5985, or the target is offline
# Open a remote session (Kerberos auth in domain environments)
$session = New-PSSession -ComputerName "DESKTOP-NGE042" -Credential (Get-Credential)
# Run commands remotely — the ScriptBlock executes on the target
Invoke-Command -Session $session -ScriptBlock {
Get-Process | Sort-Object CPU -Descending | Select-Object -First 10
}
# Run commands on MULTIPLE systems simultaneously
$targets = @("DESKTOP-NGE042", "DESKTOP-NGE001", "SERVER-NGE-DC01")
Invoke-Command -ComputerName $targets -Credential $cred -ScriptBlock {
Get-ScheduledTask | Where-Object { $_.State -eq 'Ready' -and
$_.Actions.Execute -match 'powershell|cmd|wscript' }
}
# Results include PSComputerName — you can see which system each result came from
# Close the session when done
Remove-PSSession $sessionWinRM in non-domain environments. For standalone systems or workgroup environments, additional configuration is required. The target must be added to the TrustedHosts list on the forensic workstation, and the connection uses NTLM instead of Kerberos:
# Configure TrustedHosts for non-domain environments
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "192.168.1.50,192.168.1.51" -Force
# Connect using IP address with explicit credentials
$session = New-PSSession -ComputerName "192.168.1.50" -Credential (Get-Credential) -Authentication NegotiateFor M365 investigation, install the required PowerShell modules:
# Install M365 management modules for IR
# Run as Administrator
# Exchange Online (email forensics — IR9)
Install-Module -Name ExchangeOnlineManagement -Force
# Microsoft Graph (Entra ID investigation — IR8, IR11)
Install-Module -Name Microsoft.Graph -Force
# Azure AD (legacy — some IR scripts still use this)
Install-Module -Name AzureAD -Force
# Verify installations
Get-Module -ListAvailable ExchangeOnlineManagement
Get-Module -ListAvailable Microsoft.Graph
Get-Module -ListAvailable AzureAD
# Connect to Exchange Online (test connection)
Connect-ExchangeOnline -UserPrincipalName admin@yourtenant.onmicrosoft.com
# Run a test command
Get-Mailbox | Select-Object -First 5 DisplayName, UserPrincipalName
Disconnect-ExchangeOnline -Confirm:$falseUnderstanding the M365 PowerShell modules and when each is used:
ExchangeOnlineManagement (IR9, IR14) — provides cmdlets for mailbox investigation and containment. Search-UnifiedAuditLog searches the Purview audit trail. Get-InboxRule lists inbox rules (attacker-created forwarding). Get-Mailbox retrieves mailbox configuration (forwarding addresses, delegation, audit status). Remove-InboxRule removes attacker-created rules. Set-Mailbox modifies mailbox configuration (disable forwarding, enable enhanced auditing). This module is used in every BEC, AiTM, and email compromise investigation. Install it and test the connection before you need it — ExchangeOnlineManagement module updates occasionally break authentication, and discovering this during an active investigation wastes critical time.
Microsoft.Graph (IR8, IR11) — provides cmdlets for Entra ID investigation and identity containment. Revoke-MgUserSignInSession breaks stolen session tokens (the single most important containment action for AiTM attacks). Update-MgUser disables accounts and resets passwords. Get-MgUser retrieves user profile and authentication methods. Get-MgApplication and Get-MgServicePrincipal investigate OAuth app registrations and service principals — critical for detecting consent phishing persistence (T1098.003) and service principal abuse (T1078.004). Microsoft.Graph is the successor to the legacy AzureAD module and provides access to the full Microsoft Graph API surface. Connection requires scopes that determine which API operations are permitted — specify the minimum scopes needed for each investigation task.
# Microsoft Graph connection with investigation-appropriate scopes
# Each scope grants specific API access — use minimum necessary
# For identity investigation (IR8):
Connect-MgGraph -Scopes "User.Read.All", "AuditLog.Read.All", "Directory.Read.All"
# For identity containment (IR8, IR14):
Connect-MgGraph -Scopes "User.ReadWrite.All"
# For OAuth app investigation (IR11):
Connect-MgGraph -Scopes "Application.Read.All", "ServicePrincipalEndpoint.Read.All"
# For comprehensive IR (all of the above):
Connect-MgGraph -Scopes "User.ReadWrite.All", "AuditLog.Read.All", "Directory.Read.All", "Application.Read.All"
# Verify the connection and available scopes
Get-MgContext | Select-Object Scopes, Account, TenantIdAzureAD module (legacy). The AzureAD module is deprecated in favor of Microsoft.Graph. However, many existing IR scripts and community playbooks still reference AzureAD cmdlets (Get-AzureADUser, Set-AzureADUser, Get-AzureADServicePrincipal). Install it for backward compatibility with existing scripts. New investigation scripts should use Microsoft.Graph exclusively. The cmdlet names map predictably: Get-AzureADUser → Get-MgUser, Set-AzureADUser → Update-MgUser, etc.
Module version management. PowerShell modules update frequently, and version mismatches cause authentication failures during investigations. Before each investigation, verify module versions and update if needed:
# Check installed module versions
Get-Module -ListAvailable ExchangeOnlineManagement, Microsoft.Graph, AzureAD |
Select-Object Name, Version | Format-Table
# Update modules to latest (run before a major investigation)
Update-Module ExchangeOnlineManagement -Force
Update-Module Microsoft.Graph -Force
# If Update-Module fails (common with Microsoft.Graph due to
# submodule dependencies), uninstall and reinstall:
# Uninstall-Module Microsoft.Graph -AllVersions -Force
# Install-Module Microsoft.Graph -ForceCore IR cmdlets
These PowerShell commands appear throughout the course. Knowing them before starting Phase 2 and Phase 3 saves time during investigation exercises.
Live process collection (endpoint):
# Collect running processes with command lines and network connections
# Use when KAPE/Velociraptor are unavailable
Get-Process | Select-Object Id, ProcessName, Path, StartTime,
@{Name='CommandLine'; Expression={(Get-CimInstance Win32_Process -Filter "ProcessId=$($_.Id)").CommandLine}} |
Export-Csv "C:\IR\Evidence\processes.csv" -NoTypeInformation
# Collect active network connections with owning process
Get-NetTCPConnection |
Where-Object { $_.State -eq 'Established' } |
Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort,
@{Name='Process'; Expression={(Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).ProcessName}} |
Export-Csv "C:\IR\Evidence\connections.csv" -NoTypeInformationContainment actions (identity):
# Revoke all active sessions for a compromised user
# Forces re-authentication — breaks stolen session tokens
Connect-MgGraph -Scopes "User.ReadWrite.All"
Revoke-MgUserSignInSession -UserId "jmorrison@northgateeng.com"
# Disable the account (prevents new sign-ins)
Update-MgUser -UserId "jmorrison@northgateeng.com" -AccountEnabled:$false
# Reset the password (invalidates cached credentials)
$newPassword = @{
Password = [System.Web.Security.Membership]::GeneratePassword(16, 4)
ForceChangePasswordNextSignIn = $true
}
Update-MgUser -UserId "jmorrison@northgateeng.com" -PasswordProfile $newPasswordContainment actions (email):
# Remove malicious inbox rules created by the attacker
Connect-ExchangeOnline -UserPrincipalName admin@yourtenant.onmicrosoft.com
# List all inbox rules for the compromised user
Get-InboxRule -Mailbox "jmorrison@northgateeng.com" | Format-List Name, Description, Enabled, ForwardTo, RedirectTo, DeleteMessage
# Remove a specific malicious rule (attacker-created forwarding)
Remove-InboxRule -Mailbox "jmorrison@northgateeng.com" -Identity "Update Rule" -Confirm:$false
# Check for mail forwarding (SMTP forwarding, not inbox rules)
Get-Mailbox "jmorrison@northgateeng.com" | Select-Object ForwardingSmtpAddress, ForwardingAddress, DeliverToMailboxAndForwardEvidence collection (mailbox audit):
# Search mailbox audit log for the compromised user
# Identify what the attacker accessed
Search-UnifiedAuditLog -StartDate "2026-03-15" -EndDate "2026-03-16" -UserIds "jmorrison@northgateeng.com" -Operations MailItemsAccessed, Send, MoveToDeletedItems, New-InboxRule -ResultSize 5000 |
Select-Object CreationDate, UserIds, Operations, AuditData |
Export-Csv "C:\IR\Evidence\mailbox_audit.csv" -NoTypeInformationEach of these commands is used in the investigation modules. IR8 uses the identity containment cmdlets. IR9 uses the mailbox forensics cmdlets. IR13-IR16 use all of them in context. Having them tested and validated before starting those modules eliminates setup friction during investigation exercises.
Endpoint evidence collection (when KAPE is unavailable)
In situations where KAPE cannot be deployed — restricted endpoints, systems without USB access, environments where running third-party tools requires approval — PowerShell collects the same evidence natively. The collection is less structured than KAPE output but provides the critical investigation artifacts.
# Comprehensive endpoint evidence collection via PowerShell
# Run as Administrator on the target system (or remotely via Invoke-Command)
$outputDir = "C:\IR\Evidence\PS_Collection_$(Get-Date -Format 'yyyyMMdd_HHmm')"
New-Item -ItemType Directory -Path $outputDir -Force
# Scheduled tasks — persistence mechanism (T1053.005)
Get-ScheduledTask | Where-Object { $_.State -ne 'Disabled' } |
Select-Object TaskName, TaskPath, State,
@{Name='Actions'; Expression={($_.Actions | ForEach-Object { $_.Execute + " " + $_.Arguments }) -join "; "}},
@{Name='Triggers'; Expression={($_.Triggers | ForEach-Object { $_.GetType().Name }) -join "; "}} |
Export-Csv "$outputDir\scheduled_tasks.csv" -NoTypeInformation
# Services — persistence and lateral movement artifacts
Get-CimInstance Win32_Service |
Select-Object Name, DisplayName, State, StartMode, PathName, StartName |
Export-Csv "$outputDir\services.csv" -NoTypeInformation
# Startup programs — persistence via Run keys and Startup folders
Get-CimInstance Win32_StartupCommand |
Select-Object Name, Command, Location, User |
Export-Csv "$outputDir\startup_programs.csv" -NoTypeInformation
# Local user accounts — look for attacker-created accounts
Get-LocalUser | Select-Object Name, Enabled, LastLogon, PasswordLastSet,
Description, SID |
Export-Csv "$outputDir\local_users.csv" -NoTypeInformation
# Local group membership — check Administrators group for unauthorized members
Get-LocalGroupMember -Group "Administrators" |
Select-Object Name, ObjectClass, PrincipalSource |
Export-Csv "$outputDir\local_admins.csv" -NoTypeInformation
# DNS cache — reveals recently resolved domains (C2 indicators)
Get-DnsClientCache |
Select-Object Entry, RecordName, RecordType, Data, TimeToLive |
Export-Csv "$outputDir\dns_cache.csv" -NoTypeInformation
# Recent event log entries — last 24 hours of Security log
Get-WinEvent -FilterHashtable @{LogName='Security'; StartTime=(Get-Date).AddHours(-24)} -MaxEvents 10000 |
Select-Object TimeCreated, Id, LevelDisplayName, Message |
Export-Csv "$outputDir\security_events_24h.csv" -NoTypeInformation
# PowerShell ScriptBlock log — what PowerShell commands were executed
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-PowerShell/Operational'; Id=4104} -MaxEvents 500 -ErrorAction SilentlyContinue |
Select-Object TimeCreated, @{Name='ScriptBlock'; Expression={$_.Properties[2].Value}} |
Export-Csv "$outputDir\powershell_scriptblocks.csv" -NoTypeInformation
Write-Host "Collection complete: $outputDir" -ForegroundColor Green
Get-ChildItem $outputDir | ForEach-Object { Write-Host " $($_.Name) — $([math]::Round($_.Length/1KB, 1)) KB" }This script collects the same categories of evidence that KAPE's !SANS_Triage target collects — scheduled tasks, services, startup items, local accounts, DNS cache, event logs, and PowerShell history — but through native cmdlets rather than file-level collection. The output is immediately analysis-ready CSV files that open in Timeline Explorer or Excel.
The key difference from KAPE: PowerShell collects live system state (running services, active DNS cache, current scheduled task definitions), while KAPE collects the underlying files (registry hives, Prefetch binaries, $MFT). Both produce valuable evidence. In an ideal investigation, you use both — KAPE for the forensic artifacts and PowerShell for the live state that may differ from what the files show (a scheduled task that was just created may not yet be reflected in all file-level artifacts).
Automation: batch investigation across multiple targets
When an investigation reveals that multiple systems may be affected, PowerShell automation scales the evidence collection across all targets simultaneously.
# Batch evidence collection across multiple endpoints
# Use when scoping an incident across the organization
$targetComputers = @(
"DESKTOP-NGE001",
"DESKTOP-NGE042",
"LAPTOP-NGE015",
"SERVER-NGE-DC01"
)
$credential = Get-Credential -Message "Enter domain admin credentials for remote collection"
$iocTaskName = "ChromeUpdate" # Suspicious scheduled task from initial investigation
$results = Invoke-Command -ComputerName $targetComputers -Credential $credential -ScriptBlock {
param($taskName)
$task = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
[PSCustomObject]@{
ComputerName = $env:COMPUTERNAME
TaskFound = $null -ne $task
TaskState = if ($task) { $task.State } else { "N/A" }
TaskAction = if ($task) { ($task.Actions | ForEach-Object { $_.Execute }) -join "; " } else { "N/A" }
LastRunTime = if ($task) { (Get-ScheduledTaskInfo -TaskName $taskName -ErrorAction SilentlyContinue).LastRunTime } else { "N/A" }
}
} -ArgumentList $iocTaskName -ErrorAction SilentlyContinue
$results | Format-Table -AutoSize
$results | Export-Csv "C:\IR\Evidence\scope_check_$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation
# Report: which endpoints have the indicator?
$affected = ($results | Where-Object { $_.TaskFound -eq $true }).Count
Write-Host "`n$affected of $($targetComputers.Count) endpoints have the '$iocTaskName' scheduled task" -ForegroundColor $(if ($affected -gt 0) { 'Red' } else { 'Green' })This script runs a single investigation query (checking for a specific scheduled task) across four endpoints simultaneously. The entire operation completes in seconds. Without automation, the analyst would need to log into each system individually, run the query, record the result, and move to the next — a process that takes 5-10 minutes per system. With 50 endpoints to check, that is 4-8 hours of manual work versus 30 seconds of scripted execution.
Phase 4 scenarios use PowerShell automation extensively for scope determination. When the initial investigation identifies an indicator of compromise, the responder needs to know: how many other systems are affected? PowerShell answers that question at enterprise scale.
Entra ID investigation cmdlets
Microsoft Graph PowerShell provides investigation capabilities for identity compromise that are not available in the web portal.
# Entra ID investigation: check for attacker-created persistence
Connect-MgGraph -Scopes "Application.Read.All", "AuditLog.Read.All"
# Check for recently registered applications (attacker may register
# a malicious app for persistent OAuth access)
Get-MgApplication -Filter "createdDateTime ge 2026-03-14T00:00:00Z" |
Select-Object DisplayName, AppId, CreatedDateTime,
@{Name='Owners'; Expression={(Get-MgApplicationOwner -ApplicationId $_.Id).AdditionalProperties.userPrincipalName -join "; "}} |
Format-Table -AutoSize
# Check for service principal credentials (attacker may add a
# secret to an existing service principal for persistence)
Get-MgServicePrincipal -All | ForEach-Object {
$sp = $_
$creds = Get-MgServicePrincipal -ServicePrincipalId $sp.Id -Property "passwordCredentials,keyCredentials"
if ($creds.PasswordCredentials -or $creds.KeyCredentials) {
[PSCustomObject]@{
DisplayName = $sp.DisplayName
AppId = $sp.AppId
PasswordCredentials = ($creds.PasswordCredentials | Measure-Object).Count
KeyCredentials = ($creds.KeyCredentials | Measure-Object).Count
LatestCredential = ($creds.PasswordCredentials | Sort-Object StartDateTime -Descending | Select-Object -First 1).StartDateTime
}
}
} | Where-Object { $_.LatestCredential -gt (Get-Date).AddDays(-7) } |
Format-Table -AutoSize
# Check for suspicious MFA method additions
# Attacker may add their own phone number or authenticator app
Get-MgUserAuthenticationMethod -UserId "jmorrison@northgateeng.com" |
Select-Object Id, @{Name='Type'; Expression={$_.AdditionalProperties.'@odata.type'}}These cmdlets are central to IR11 (Entra ID and Azure AD Investigation). The service principal credential check is particularly important — service principal secrets persist through user password resets, meaning the attacker retains access even after you contain the compromised user account. This is one of the most commonly missed persistence mechanisms in BEC investigations.
Device containment via Defender API
For organizations with Defender for Endpoint, PowerShell can trigger device containment actions through the Microsoft 365 Defender API without navigating the portal.
# Isolate a compromised device via Defender for Endpoint API
# This blocks all network connections except to the Defender service
# The device can still be managed and investigated remotely
$deviceId = "abc123def456" # Device ID from Defender portal
$body = @{
Comment = "INC-NE-2026-0315-001: Isolating compromised workstation for investigation"
IsolationType = "Full" # Full = block all traffic. Selective = allow Outlook/Teams
} | ConvertTo-Json
Invoke-MgGraphRequest -Method POST -Uri "https://api.securitycenter.microsoft.com/api/machines/$deviceId/isolate" -Body $body -ContentType "application/json"The device isolation API is faster than navigating the Defender portal, especially during a multi-endpoint incident where you need to isolate several machines in rapid succession. The Comment field creates an audit trail — every containment action should reference the case ID for chain of custody.
Troubleshooting PowerShell IR setup
"Connect-ExchangeOnline fails with authentication error." Ensure the ExchangeOnlineManagement module is version 3.0+ (run Get-Module ExchangeOnlineManagement -ListAvailable). Older versions use deprecated authentication methods. If using MFA, the module handles modern authentication automatically — do not pass credentials, let it prompt for interactive login.
"Connect-MgGraph scope error." The first time you connect with a new scope, the Graph application requires admin consent. In a Developer Tenant, you are the admin — consent when prompted. In production, the Graph app registration must have the required API permissions assigned and admin-consented.
"Invoke-Command fails with WinRM access denied." PowerShell remoting requires the target machine to have WinRM enabled and the caller to have admin credentials on the target. In domain environments, this works with domain admin credentials. For non-domain machines, the target must be added to the TrustedHosts list: Set-Item WSMan:\localhost\Client\TrustedHosts -Value "TARGET-PC" -Force.
"Get-WinEvent returns no results for PowerShell ScriptBlock logging." ScriptBlock logging (Event ID 4104) must be enabled via Group Policy or registry: HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging\EnableScriptBlockLogging = 1. If this policy is not enabled, no ScriptBlock events are generated — and a critical evidence source is missing. IR5 covers enabling comprehensive PowerShell logging as part of event log analysis.
The IR script library
Maintain a library of tested, documented PowerShell scripts for common IR tasks. These scripts live in C:\IR\Tools\Scripts\ on the forensic workstation and in the jump bag. Each script is a self-contained tool for a specific investigation or containment action.
# IR Script: Complete Identity Containment
# Save as: C:\IR\Tools\Scripts\Contain-Identity.ps1
# Usage: .\Contain-Identity.ps1 -UserPrincipalName "jmorrison@northgateeng.com" -CaseID "INC-NE-2026-0315-001"
#
# This script performs the complete identity containment sequence
# for a compromised M365 account in the correct order:
param(
[Parameter(Mandatory)][string]$UserPrincipalName,
[Parameter(Mandatory)][string]$CaseID
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss UTC" -AsUTC
$log = @("=== Identity Containment Log ===", "Case: $CaseID", "Target: $UserPrincipalName", "Started: $timestamp", "")
Write-Host "=== Containing $UserPrincipalName ===" -ForegroundColor Red
# Step 1: Revoke all active sessions (breaks stolen tokens immediately)
Connect-MgGraph -Scopes "User.ReadWrite.All" -NoWelcome
Revoke-MgUserSignInSession -UserId $UserPrincipalName
$log += "$(Get-Date -Format HH:mm:ss) — Sessions revoked"
Write-Host " [1/5] Sessions revoked" -ForegroundColor Yellow
# Step 2: Disable the account (prevents new sign-ins)
Update-MgUser -UserId $UserPrincipalName -AccountEnabled:$false
$log += "$(Get-Date -Format HH:mm:ss) — Account disabled"
Write-Host " [2/5] Account disabled" -ForegroundColor Yellow
# Step 3: Reset password (invalidates cached credentials)
$newPw = -join ((65..90) + (97..122) + (48..57) + (33..38) | Get-Random -Count 20 | ForEach-Object { [char]$_ })
$pwProfile = @{ Password = $newPw; ForceChangePasswordNextSignIn = $true }
Update-MgUser -UserId $UserPrincipalName -PasswordProfile $pwProfile
$log += "$(Get-Date -Format HH:mm:ss) — Password reset (new pw in secure log)"
Write-Host " [3/5] Password reset" -ForegroundColor Yellow
# Step 4: Check and remove suspicious inbox rules
Connect-ExchangeOnline -UserPrincipalName admin@yourtenant.onmicrosoft.com -ShowBanner:$false
$rules = Get-InboxRule -Mailbox $UserPrincipalName
$suspiciousRules = $rules | Where-Object {
$_.ForwardTo -or $_.RedirectTo -or $_.ForwardAsAttachmentTo -or $_.DeleteMessage -eq $true
}
foreach ($rule in $suspiciousRules) {
Remove-InboxRule -Mailbox $UserPrincipalName -Identity $rule.Identity -Confirm:$false
$log += "$(Get-Date -Format HH:mm:ss) — Removed inbox rule: $($rule.Name) (Forward: $($rule.ForwardTo))"
Write-Host " [4/5] Removed suspicious rule: $($rule.Name)" -ForegroundColor Yellow
}
if (-not $suspiciousRules) {
$log += "$(Get-Date -Format HH:mm:ss) — No suspicious inbox rules found"
Write-Host " [4/5] No suspicious inbox rules found" -ForegroundColor Green
}
# Step 5: Check for SMTP forwarding
$mbx = Get-Mailbox $UserPrincipalName
if ($mbx.ForwardingSmtpAddress) {
Set-Mailbox $UserPrincipalName -ForwardingSmtpAddress $null -ForwardingAddress $null
$log += "$(Get-Date -Format HH:mm:ss) — Removed SMTP forwarding: $($mbx.ForwardingSmtpAddress)"
Write-Host " [5/5] Removed SMTP forwarding" -ForegroundColor Yellow
} else {
$log += "$(Get-Date -Format HH:mm:ss) — No SMTP forwarding configured"
Write-Host " [5/5] No SMTP forwarding found" -ForegroundColor Green
}
Disconnect-ExchangeOnline -Confirm:$false
Disconnect-MgGraph
# Write containment log
$log += "", "Completed: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss UTC' -AsUTC)"
$logPath = "C:\IR\Cases\$CaseID\Notes\containment_$($UserPrincipalName.Replace('@','_')).txt"
$log | Out-File $logPath -Force
Write-Host "`nContainment complete. Log: $logPath" -ForegroundColor GreenThis script executes the complete BEC/AiTM identity containment sequence in the correct order: revoke sessions first (stops active attacker access immediately), then disable the account (prevents re-authentication), then reset the password (invalidates all cached credentials), then remove malicious inbox rules and forwarding (eliminates persistent email access). Each step is logged with a timestamp and the case ID for chain of custody. The script is used in IR8 (Identity Compromise), IR13 (Ransomware — if credential theft is involved), and IR14 (BEC).
PowerShell vs dedicated tools: when to use each
PowerShell is the universal fallback — it works when KAPE is unavailable, when Velociraptor is not deployed, when the Defender portal is down, and when you need to execute containment actions in seconds rather than minutes. But it is not always the best choice for evidence collection.
Evidence collection: KAPE produces structured output organized by artifact category, preserves file metadata and timestamps, handles locked files via raw disk access, and integrates with EZTools for automated parsing. PowerShell native collection (Get-Process, Get-ScheduledTask, Get-WinEvent) produces live system state in CSV format but does not access locked files, does not preserve original file timestamps, and requires manual correlation rather than automated parsing. Use KAPE when available. Use PowerShell when KAPE is not available or when you need specific live system data (DNS cache, active connections, running processes) that KAPE's file-based collection does not capture.
Containment: PowerShell is the primary containment tool. The Contain-Identity.ps1 script above executes the complete identity containment sequence in 30 seconds. Performing the same actions through the Entra ID portal, Exchange admin center, and Defender portal takes 5-10 minutes of manual navigation. During an active BEC where the attacker is reading emails and redirecting payments, those 5-10 minutes matter. Always use PowerShell for containment actions — speed is the priority.
Automation: PowerShell is the only option for batch operations across multiple targets. Checking 50 mailboxes for a specific inbox rule, querying sign-in logs for 20 compromised accounts, collecting evidence from 10 endpoints — all require scripted automation. Manual portal-based investigation does not scale beyond 3-5 targets. Use PowerShell automation for any operation that touches more than 5 targets.
"PowerShell is an attacker tool — security teams should disable it." Production reality: PowerShell is the primary management tool for Windows and M365 environments. Disabling it cripples both administration and incident response. The correct approach is not to disable PowerShell but to log it comprehensively (ScriptBlock logging, Module logging, Transcription) so that both attacker and defender activity is visible. Constrained Language Mode and WDAC (Windows Defender Application Control) policies can restrict PowerShell to approved scripts while maintaining full capability for administrators. IR5 covers PowerShell logging in depth; IR7 covers detecting attacker PowerShell usage through those logs.
Investigation Principle
Every tool in the IR toolkit covers evidence that no other tool can reach. Remove one category and you lose visibility into the evidence type it handles. The toolkit is complete when every evidence source from every environment in the IR0.1 incident walkthrough has a tool that can collect, parse, or query it.
Get weekly detection and investigation techniques
KQL queries, detection rules, and investigation methods — the same depth as this course, delivered every Tuesday.
No spam. Unsubscribe anytime. ~2,000 security practitioners.