🎣 Phishing — IR Cheatsheet

29 nodes | 8 stages | Generated for analyst role

Triage

  1. 1.Query SIEM for the earliest alert or IOC match and record the timestamp as T-start; search across all log sources (EDR, firewall, proxy, authentication) for corroborating events within +/- 24 hours.
  2. 2.Use PowerShell to pull the $MFT timeline: `Get-ForensicTimeline -VolumeName C: | Where-Object { $_.Date -ge $tStart -and $_.Date -le $tEnd } | Export-Csv mft_timeline.csv` -- compare with SIEM timestamps.
  3. 3.Run Velociraptor hunt `Windows.Timeline.MFT` across suspected hosts to identify file creation/modification clusters that may push T-start earlier.
index=* (sourcetype=crowdstrike OR sourcetype=defender OR sourcetype=sysmon) earliest=-30d | stats earliest(_time) as fi...
SecurityEvent | where TimeGenerated between (ago(30d) .. now()) | where EventID in (4624, 4625, 4648, 4672) | summarize ...
Patient ZeroP1 | 60m
  1. 1.Pivot from the earliest IOC timestamp to identify the originating host: correlate source IPs, user accounts, and process trees across EDR telemetry.
  2. 2.Run Velociraptor artifact `Windows.EventLogs.Evtx` with a time filter around T-start on candidate hosts to find logon events (4624 Type 3/10), service installations (7045), and scheduled task creation (4698).
  3. 3.Check email logs for the earliest malicious delivery if phishing is suspected: `sourcetype=o365:messageTrace | search directionality=Inbound status=Delivered | where _time >= T_start | stats earliest(_time) by sender, recipient, subject`.
DeviceLogonEvents | where Timestamp between (datetime(T_START) .. datetime(T_END)) | where LogonType in ("RemoteInteract...
index=proxy sourcetype=bluecoat OR sourcetype=zscaler dest IN (ioc_ip_list) OR url IN (ioc_url_list) | stats earliest(_t...
  1. 1.Extract and analyze full email headers: check Received headers for origin, Authentication-Results for SPF/DKIM/DMARC pass/fail, and X-Originating-IP for sender location.
  2. 2.Check the sender domain for typosquatting/lookalike indicators: character substitution (rn→m, l→1), added/removed characters, different TLD (.com vs .co). Use whois to check domain registration date.
  3. 3.Search for similar emails sent to other recipients: `Get-MessageTrace -SenderAddress [email protected] -StartDate T_START -EndDate T_END | Select-Object Received, SenderAddress, RecipientAddress, Subject, Status`.
EmailEvents | where Timestamp between (datetime(T_START) .. datetime(T_END)) | where SenderFromDomain has_any (SUSPICIOU...
EmailUrlInfo | where Timestamp between (datetime(T_START) .. datetime(T_END)) | where Url has_any (SUSPICIOUS_DOMAINS) |...
  1. 1.Extract and defang all IOCs from the phishing email: sender address, reply-to address, sending IP, embedded URLs (defang with hxxp://), attachment hashes (SHA256), and any referenced domains
  2. 2.Analyze email authentication headers: check SPF result (pass/fail/softfail), DKIM signature validity, and DMARC policy alignment to determine if the email is spoofed or from a compromised legitimate account
  3. 3.Submit embedded URLs to urlscan.io and VirusTotal for reputation check and detonation results -- capture screenshots of the landing page without visiting directly
// KQL -- Delivery scope: all recipients of the phishing email
EmailEvents
| where SenderFromAddress == "<PHISHING_SENDE...
// KQL -- URL click tracking for the phishing campaign
UrlClickEvents
| where Timestamp > ago(7d)
| where Url has "<PHIS...
  1. 1.On patient zero, parse Outlook OST/PST for the phishing email and extract attachment hashes. Use `oletools` to analyze macros: `olevba -a malicious.docm | tee olevba_output.txt`.
  2. 2.For web-app compromise, review IIS/Apache/Nginx access logs around T-start: look for exploit patterns (path traversal, SQLi, deserialization payloads) targeting the specific CVE.
  3. 3.Check for credential-based access: query Azure AD sign-in logs for impossible travel, legacy auth protocols, or sign-ins from anonymizing infrastructure (Tor, VPN providers).
SigninLogs | where TimeGenerated between (datetime(T_START) .. datetime(T_END)) | where UserPrincipalName == "compromise...
DeviceProcessEvents | where DeviceName == "PATIENT_ZERO" | where Timestamp between (datetime(T_START) .. datetime(T_END)...

Containment

  1. 1.Disable the compromised user account in AD: `Disable-ADAccount -Identity compromised_user` and in Azure AD: `Set-AzureADUser -ObjectId <user_id> -AccountEnabled $false`.
  2. 2.Revoke all Azure AD refresh tokens immediately: `Revoke-AzureADUserAllRefreshToken -ObjectId <user_id>`. For M365, also run: `Get-AzureADUserRegisteredDevice -ObjectId <user_id> | ForEach-Object { Remove-AzureADDeviceRegisteredUser -ObjectId $_.ObjectId }`.
  3. 3.Force Kerberos ticket expiry for on-prem AD: reset the user password twice (this invalidates all cached TGTs). Then reset the KRBTGT account if Golden Ticket is suspected: `Reset-KrbtgtKeys -Server DC01 -Force`.
let compromised_user = "[email protected]"; SigninLogs | where UserPrincipalName == compromised_user | where TimeGenerated...
let compromised_user = "[email protected]"; AuditLogs | where InitiatedBy has compromised_user | where TimeGenerated > ago...
  1. 1.Revoke all Azure AD refresh tokens: `Revoke-MgUserSignInSession -UserId <user_id>`. This invalidates all active sessions and forces re-authentication across all devices and applications.
  2. 2.Revoke specific OAuth app tokens: `Remove-AzureADOAuth2PermissionGrant -ObjectId <grant_id>` for each malicious grant identified. Also remove service principal assignments: `Remove-AzureADServiceAppRoleAssignment`.
  3. 3.Force re-registration of all user devices: `Get-AzureADUserRegisteredDevice -ObjectId <user_id> | ForEach-Object { Set-AzureADDevice -ObjectId $_.ObjectId -AccountEnabled $false }`. This invalidates device-based SSO tokens.
SigninLogs | where TimeGenerated > ago(2h) | where UserPrincipalName == "[email protected]" | where ResultType...
AuditLogs | where TimeGenerated > ago(24h) | where OperationName in ("Revoke user all refresh tokens","Invalidate all re...
  1. 1.Block the sender domain/address at the email gateway: add to Proofpoint/Mimecast blocklist, or create an Exchange Online transport rule to reject messages from the sender
  2. 2.Purge the phishing email from all recipient mailboxes using M365 Compliance Content Search and Purge action: use SoftDelete to preserve in recoverable items, or HardDelete if no evidence hold is needed
  3. 3.Block the phishing URL(s) and domain(s) at the web proxy, firewall, and DNS: add to proxy blocklist, create firewall deny rules, and sinkhole the domain in DNS
// PowerShell -- Purge phishing emails from all mailboxes
# Step 1: Create and run content search
New-ComplianceSearch -...
// PowerShell -- Block sender via transport rule
New-TransportRule -Name "Block Phishing Sender $(Get-Date -f yyyyMMdd)"...

Preservation

  1. 1.Windows -- Use WinPmem from a trusted USB or network share: `winpmem_mini_x64.exe --output F:\case\%COMPUTERNAME%_memdump.raw --format raw`. Verify the output file size matches expected RAM.
  2. 2.Windows alternative -- Use Magnet RAM Capture or Belkasoft Live RAM Capturer if WinPmem fails. For remote collection via Velociraptor: deploy `Windows.Memory.Acquisition` artifact.
  3. 3.Linux -- Use AVML (Acquire Volatile Memory for Linux): `./avml --compress output.lime.gz`. Alternative: `sudo insmod lime.ko "path=/case/mem.lime format=lime"`. Verify module unloads cleanly after capture.
DeviceProcessEvents | where DeviceName == "TARGET_HOST" | where Timestamp > ago(1h) | where FileName in~ ("winpmem","ram...
SELECT pid, name, ppid, cmdline, create_time FROM processes WHERE on_disk = 0 OR name IN ("powershell.exe","cmd.exe","ru...
Log SnapshotP1 | 45m
  1. 1.Windows -- Export critical event logs: `wevtutil epl Security C:\case\Security.evtx`, `wevtutil epl System C:\case\System.evtx`, `wevtutil epl "Microsoft-Windows-PowerShell/Operational" C:\case\PowerShell.evtx`, `wevtutil epl "Microsoft-Windows-Sysmon/Operational" C:\case\Sysmon.evtx`.
  2. 2.Linux -- Preserve auth and system logs: `cp -p /var/log/auth.log* /var/log/syslog* /var/log/secure* /var/log/audit/audit.log* /case/logs/`. For journald: `journalctl --since "T_START" --until "T_END" -o json > /case/logs/journal_export.json`.
  3. 3.M365/Azure -- Export Azure AD sign-in and audit logs via PowerShell: `Get-AzureADAuditSignInLogs -Filter "createdDateTime ge T_START" -All $true | Export-Csv azure_signins.csv`. Export UAL: `Search-UnifiedAuditLog -StartDate T_START -EndDate T_END -ResultSize 5000 | Export-Csv ual_export.csv`.
index=_internal sourcetype=splunkd component=HotBucketRoller OR component=WarmToColdManager | stats latest(data_size) as...
// Velociraptor: collect all EVTX files from target host
SELECT FullPath, Size, Mtime FROM glob(globs="C:/Windows/System...
  1. 1.Export the original phishing email(s) in EML/MSG format with full internet headers preserved: use Outlook "Save As" or Graph API to extract the raw message including X-headers and authentication results (SPF/DKIM/DMARC)
  2. 2.Capture email delivery logs from Exchange message trace: Get-MessageTrace for the past 10 days or submit historical search for 11-90 days to identify all recipients who received the phishing email
  3. 3.Preserve embedded URLs without clicking: extract all URLs from the email body and HTML source, capture them in a text file, and submit to URL analysis tools (urlscan.io, VirusTotal) from an isolated analysis workstation
// PowerShell -- Export phishing email metadata via message trace
Get-MessageTrace -SenderAddress "<PHISHING_SENDER>" -S...
// PowerShell -- Content Search to find and soft-delete phishing email
New-ComplianceSearch -Name "Phish-$(Get-Date -f y...
  1. 1.For each evidence item collected, record: item description, source system, collection method, collector name, date/time (UTC), and storage location.
  2. 2.Hash all evidence files immediately upon collection using SHA-256: `sha256sum /case/evidence/* > /case/evidence_hashes.sha256` (Linux) or `Get-FileHash -Algorithm SHA256 -Path C:\case\evidence\* | Export-Csv C:\case\evidence_hashes.csv` (PowerShell).
  3. 3.Store evidence on write-protected media or in a secured evidence repository with access logging. Document any transfers between storage locations.
Get-ChildItem -Path C:\case\evidence -Recurse | Get-FileHash -Algorithm SHA256 | Select-Object Hash, Path, @{Name="SizeK...

Collection

  1. 1.Collect all unique phishing email variants (there may be multiple with slight variations): export each variant as EML with full headers and catalogue sender IPs, URLs, and attachment hashes
  2. 2.Download and archive the phishing landing page using wget --mirror or HTTrack: capture the full credential harvesting kit for analysis and IOC extraction
  3. 3.Collect email gateway logs for all delivery attempts (successful and blocked) to understand the full scope of the campaign targeting your organization
// KQL -- Comprehensive phishing campaign artifact collection
let phish_recipients = EmailEvents
| where SenderFromAddre...
// KQL -- Post-click sign-in activity (credential compromise check)
let clickers = UrlClickEvents
| where Url has "<PHIS...
  1. 1.CrowdStrike -- Export full RTR session data and detection details via the Falcon API: `GET /detects/queries/detects/v1?filter=device.hostname:"TARGET_HOST"`. Pull process tree JSON for each detection.
  2. 2.Microsoft Defender -- Run Advanced Hunting queries to extract process execution history, file events, and network connections for the investigation window. Export results to CSV for offline analysis.
  3. 3.Deploy a Velociraptor hunt across all in-scope hosts using the SANS triage collection: `velociraptor-v0.7.0 --config client.config.yaml artifacts collect Windows.KapeFiles.Targets --args target="!SANS_Triage" --output /case/host_triage/`.
DeviceProcessEvents | where DeviceName in ("HOST1","HOST2","HOST3") | where Timestamp between (datetime(T_START) .. date...
DeviceFileEvents | where DeviceName in ("HOST1","HOST2","HOST3") | where Timestamp between (datetime(T_START) .. datetim...
  1. 1.Export the UAL using PowerShell for the investigation window. Note the 5000-record per query limit: `$results = Search-UnifiedAuditLog -StartDate "T_START" -EndDate "T_END" -ResultSize 5000 -SessionCommand ReturnLargeSet`. Page through all results and export to JSON for parsing.
  2. 2.Use the CISA Sparrow tool or CrowdStrike CRT (Cloud Response Tool) for automated collection: `sparrow.ps1` performs bulk UAL export, Azure AD analysis, and inbox rule enumeration.
  3. 3.Export Azure AD sign-in logs (requires P1/P2): `Get-MgAuditLogSignIn -Filter "createdDateTime ge T_START" -All | Export-Csv signins.csv`. Also export `Get-MgAuditLogDirectoryAudit` for admin activity.
CloudAppEvents | where Timestamp between (datetime(T_START) .. datetime(T_END)) | where AccountObjectId == "<user_object...
AuditLogs | where TimeGenerated between (datetime(T_START) .. datetime(T_END)) | where Category in ("ApplicationManageme...
  1. 1.Export Azure AD sign-in logs via Microsoft Graph: `Get-MgAuditLogSignIn -Filter "createdDateTime ge T_START and createdDateTime le T_END" -All | Export-Csv signins.csv -NoTypeInformation`. Include both interactive and non-interactive sign-ins.
  2. 2.Export Azure AD audit logs: `Get-MgAuditLogDirectoryAudit -Filter "activityDateTime ge T_START" -All | Export-Csv audit_logs.csv`. Focus on user management, application management, and role management categories.
  3. 3.Collect risky sign-ins and risk detections (requires AAD P2): `Get-MgRiskyUser -Filter "riskLevel eq 'high'" | Export-Csv risky_users.csv` and `Get-MgRiskDetection -Filter "detectedDateTime ge T_START" | Export-Csv risk_detections.csv`.
SigninLogs | where TimeGenerated between (datetime(T_START) .. datetime(T_END)) | where ResultType == 0 | summarize Sign...
AuditLogs | where TimeGenerated between (datetime(T_START) .. datetime(T_END)) | where OperationName in ("Add member to ...
  1. 1.Identify which log sources are missing and the affected time window. Document the gap: source name, expected retention, actual available range, and suspected reason for absence.
  2. 2.For missing Windows Event Logs, check Volume Shadow Copies: `vssadmin list shadows` then mount and extract .evtx files from shadow copies. Also check `C:\Windows\System32\winevt\Logs` for partially overwritten logs that may contain older entries.
  3. 3.Harvest NTFS metadata as a timeline substitute: `MFTECmd.exe -f C:\$MFT --csv /case/output/` provides file creation/modification timestamps even when application logs are gone. The $UsnJrnl provides granular file change records.
SecurityEvent | where TimeGenerated > ago(90d) | summarize MinTime=min(TimeGenerated), MaxTime=max(TimeGenerated), Count...
DeviceEvents | where Timestamp > ago(30d) | summarize EarliestEvent=min(Timestamp), LatestEvent=max(Timestamp), EventCou...
  1. 1.Identify which third-party vendors hold relevant logs: ISP flow data, cloud hosting provider logs, SaaS application audit trails, MSP monitoring data, CDN/WAF provider logs.
  2. 2.Draft a formal log preservation and production request specifying: timeframe (T-30d to present), log types needed, format requirements (CSV, JSON, syslog), and delivery method (SFTP, encrypted email).
  3. 3.Include in the request: case reference number, legal basis for the request (contract clause, legal process), contact person, and urgency level.
Review vendor contracts and SLAs for log retention periods and incident response support obligations.

Analysis

  1. 1.Query for RDP lateral movement: filter for Event ID 4624 (LogonType 10) and 4778/4779 (RDP session reconnect/disconnect) across all domain controllers and target hosts during the investigation window.
  2. 2.Identify pass-the-hash/pass-the-ticket: look for 4624 LogonType 9 (NewCredentials) and 4648 (explicit credential logon). Cross-reference with EDR for suspicious LSASS access (Mimikatz, ProcDump, comsvcs.dll MiniDump).
  3. 3.Map SMB lateral movement via admin shares: `DeviceNetworkEvents | where RemotePort == 445 | where InitiatingProcessFileName in~ ("cmd.exe","powershell.exe","wmic.exe","psexec.exe","smbclient")` and correlate with service installation events (7045) on target hosts.
SecurityEvent | where TimeGenerated between (datetime(T_START) .. datetime(T_END)) | where EventID in (4624, 4648) | whe...
DeviceProcessEvents | where Timestamp between (datetime(T_START) .. datetime(T_END)) | where FileName in~ ("psexec.exe",...
  1. 1.Correlate email delivery data with URL click data and sign-in logs to build a complete interaction chain: delivered → clicked → credential entered → successful sign-in from attacker IP
  2. 2.For each confirmed-compromised user, map all post-compromise activity: emails read/sent, files accessed, applications used, and admin actions performed using the stolen credentials
  3. 3.Analyze whether the attacker used compromised accounts to send internal phishing emails (lateral phishing): search for emails sent from compromised accounts with suspicious URLs or attachments
// KQL -- Full phishing interaction chain analysis
let delivered = EmailEvents
| where SenderFromAddress == "<PHISHING_S...
// KQL -- Post-compromise activity by compromised users
let compromised_users = dynamic(["<USER1>","<USER2>"]);
let atta...
OAuth AbuseP2 | 60m
  1. 1.Enumerate all OAuth permission grants for compromised users: `Get-AzureADUserOAuth2PermissionGrant -ObjectId <user_id> -All $true | Select ClientId, ConsentType, Scope, PrincipalId`. Cross-reference ClientId with known legitimate applications.
  2. 2.Identify suspicious application registrations created during the investigation window: `Get-AzureADApplication -Filter "createdDateTime ge T_START" | Select DisplayName, AppId, CreatedDateTime, Homepage, ReplyUrls`.
  3. 3.Check for high-privilege consent grants (Mail.ReadWrite, Files.ReadWrite.All, Directory.ReadWrite.All): `AuditLogs | where OperationName == "Consent to application" | where TargetResources has "Mail.ReadWrite" or TargetResources has "Files.ReadWrite"`. These are the most dangerous grants.
AuditLogs | where TimeGenerated between (datetime(T_START) .. datetime(T_END)) | where OperationName in ("Consent to app...
CloudAppEvents | where Timestamp between (datetime(T_START) .. datetime(T_END)) | where ActionType == "Consent to applic...
Inbox RulesP2 | 45m
  1. 1.Enumerate all inbox rules for compromised mailboxes: `Get-InboxRule -Mailbox [email protected] | Select Name, Description, Enabled, Priority, ForwardTo, ForwardAsAttachmentTo, RedirectTo, DeleteMessage, MoveToFolder | Format-List`.
  2. 2.Check for mailbox forwarding and delegates: `Get-Mailbox -Identity compromised_user | Select ForwardingAddress, ForwardingSMTPAddress, DeliverToMailboxAndForward` and `Get-MailboxPermission -Identity compromised_user | Where-Object { $_.IsInherited -eq $false }`.
  3. 3.Search for rules that delete or move security notifications: look for rules with conditions matching "security","alert","password","suspicious","MFA" that move to Deleted Items or RSS Feeds folder.
OfficeActivity | where TimeGenerated between (datetime(T_START) .. datetime(T_END)) | where Operation in ("New-InboxRule...
CloudAppEvents | where Timestamp between (datetime(T_START) .. datetime(T_END)) | where ActionType in ("Set-InboxRule","...

Eradication

  1. 1.Run a full Autoruns sweep on all Windows hosts previously in scope -- compare against pre-incident baseline and flag any new entries added during the compromise window
  2. 2.Execute YARA rules for all known malware hashes and families identified during analysis across every endpoint using EDR live-response or Velociraptor hunts
  3. 3.Verify all compromised credentials have been reset: cross-reference the credential exposure list against Azure AD password-last-changed timestamps and on-prem AD pwdLastSet attributes
// KQL -- Verify no ransomware/malware re-execution post-eradication
DeviceProcessEvents
| where Timestamp > ago(48h)
| ...
// KQL -- Check for residual C2 communication
DeviceNetworkEvents
| where Timestamp > ago(48h)
| where RemoteIP in ("<C2...
  1. 1.Execute final email purge sweep: re-run Content Search with expanded criteria (additional sender variations, subject variations, URL patterns) to catch any phishing emails that escaped the initial quarantine
  2. 2.Reset passwords and revoke all sessions for every confirmed-compromised account: ensure MFA re-enrollment from a trusted device, not just password change
  3. 3.Remove all attacker-created persistence: delete malicious inbox rules, revoke illicit OAuth consent grants, remove unauthorized MFA methods, and disable any rogue app registrations
// PowerShell -- Final purge sweep with expanded criteria
New-ComplianceSearch -Name "PhishFinalSweep-$(Get-Date -f yyyy...
// PowerShell -- Remove attacker persistence from all compromised accounts
$compromised = @("<USER1>","<USER2>","<USER3>...
  1. 1.Disable legacy authentication protocols exploited during the attack: NTLMv1, LLMNR, NBT-NS, WPAD, SMBv1, and unencrypted LDAP binds
  2. 2.Enable Windows Attack Surface Reduction (ASR) rules relevant to the observed TTPs: block Office macro child processes, block credential theft from LSASS, block unsigned/untrusted executables from USB
  3. 3.Harden Azure AD conditional access: enforce MFA on all accounts, block legacy auth, require compliant devices, implement sign-in risk and user risk policies
// PowerShell -- Enable ASR rules
$rules = @(
  "BE9BA2D9-53EA-4CDC-84E5-9B1EEEE46550", # Block Office child processes
 ...
// KQL -- Verify hardening changes took effect
DeviceEvents
| where Timestamp > ago(24h)
| where ActionType startswith "...

Recovery

  1. 1.Create service restoration tiers: Tier 1 (authentication, email, ERP), Tier 2 (file shares, internal apps), Tier 3 (development, non-essential). Restore one tier at a time.
  2. 2.Before each tier, verify: security tooling installed and reporting, monitoring rules deployed, containment controls updated for legitimate traffic.
  3. 3.Deploy enhanced monitoring rules: alert on C2 connections, new services, admin account creation, PowerShell/WMI remote execution.
DeviceNetworkEvents | where Timestamp > ago(4h) | where DeviceName in ("RESTORED_HOSTS") | where RemoteUrl !endswith ".m...
SecurityEvent | where TimeGenerated > ago(4h) | where Computer in ("RESTORED_HOSTS") | where EventID in (4720, 4732, 704...

Post-Incident Review

  1. 1.Document all attacker TTPs observed with specific indicators: process names, command lines, file paths, registry keys, network destinations.
  2. 2.Write Sigma rules for each TTP that can be shared and converted to multiple SIEM platforms.
  3. 3.Create custom KQL/SPL detection rules for the organization SIEM targeting the specific attack patterns.
SecurityEvent | where TimeGenerated between (datetime(T_START) .. datetime(T_END)) | where EventID == 4688 | where Proce...
  1. 1.Write the technical narrative covering: initial detection, investigation scope, evidence collected, attacker TTPs, impact assessment, containment actions, eradication steps, and recovery.
  2. 2.Create the final incident timeline with all key events in chronological order.
  3. 3.Compile the IOC appendix: file hashes, IP addresses, domains, email addresses, user agents, and other indicators.
Compile all queries used during the investigation for the report appendix.
  1. 1.Compile the complete incident timeline from detection through recovery with all key events, decisions, and timestamps.
  2. 2.Document all evidence gaps encountered and how they were addressed (or could not be addressed).
  3. 3.Identify detection gaps: what alerts should have fired but did not? What telemetry would have enabled faster detection?
Review all investigation notes, evidence logs, and timeline entries compiled during the incident.