Full incident response lifecycle: detection, investigation, containment, eradication, and recovery across endpoints, email, identity, and cloud apps in Microsoft Defender XDR.
In this advanced hands-on lab you will walk through a complete incident response (IR) lifecycle inside Microsoft Defender XDR. You will investigate a multi-stage Advanced Persistent Threat (APT) attack that spans email, endpoint, identity, and cloud application vectors. Beginning with initial alert triage in the incident queue, you will reconstruct the full attack timeline, correlate evidence across all four Defender workloads, execute containment and eradication actions, guide recovery, and produce a post-incident review with actionable recommendations. Every step mirrors the workflow a Tier-2 / Tier-3 SOC analyst would follow in a real-world breach investigation.
Globex Corporation is a multinational financial services company with 12,000 employees across 15 countries. On a Monday morning the SOC receives a high-severity incident in Defender XDR that correlates alerts from Defender for Office 365, Defender for Endpoint, Defender for Identity, and Defender for Cloud Apps. Initial indicators suggest an advanced threat actor: designated STRONTIUM / APT28 in MITRE ATT&CK: has launched a multi-stage campaign: a spear-phishing email delivered a weaponized document to a finance director, which executed a macro-based payload, established persistence via a scheduled task, harvested credentials through LSASS memory access, moved laterally to a domain controller, and exfiltrated sensitive data to a cloud storage service. Your mission is to investigate, contain, eradicate, and recover: all within the unified Defender XDR portal.
APT attacks are not single-alert events: they are coordinated campaigns that unfold across multiple vectors over hours or days. Investigating them in siloed tools leads to slow response times, missed evidence, and incomplete remediation. Microsoft Defender XDR unifies telemetry from endpoints, email, identity, and cloud apps into a single incident, enabling analysts to see the complete attack story and respond decisively. This lab trains you to leverage that unified view, correlate evidence across workloads, and execute the full IR lifecycle: skills that directly translate to faster mean-time-to-respond (MTTR) and reduced breach impact in production environments.
Microsoft.Graph module for remediation scriptsBefore diving into investigation, you need a clear mental model of the attack you are hunting. This simulated APT follows the classic cyber kill chain and maps directly to MITRE ATT&CK tactics and techniques. Understanding the expected attack flow lets you prioritize your investigation and recognize evidence when you encounter it.
The simulated attack proceeds through these phases:
j.smith@globex.com (Finance Director). The email impersonates a known vendor and references an overdue invoice.update-service[.]cc.WindowsUpdateCheck that runs every 4 hours, ensuring the implant survives reboot.Invoke-Mimikatz or direct LSASS memory access to harvest NTLM hashes and Kerberos tickets from the compromised endpoint.DC01) and a file server (FS02).Use the following KQL query to view MITRE ATT&CK technique tags on alerts in the current incident:
// LIST MITRE ATT&CK TECHNIQUES across all alerts (last 7 days)
// Purpose: Identify which adversary techniques are most active in your environment
// and validate that alerts map to specific ATT&CK techniques.
//
// How it works:
// 1. Filters AlertInfo to only alerts that have ATT&CK technique tags
// 2. Parses the AttackTechniques JSON array into individual rows (mv-expand)
// 3. Counts alerts per technique and shows associated alert titles
//
// Output columns:
// Technique β ATT&CK technique ID (e.g., T1566.001, T1059.001)
// AlertCount β how many alerts mapped to this technique
// Titles β list of alert names for context
// A multi-technique incident spanning Initial Access through Exfiltration
// signals a coordinated APT attack rather than commodity malware.
AlertInfo
| where Timestamp > ago(7d)
| where AttackTechniques != ""
| extend Techniques = parse_json(AttackTechniques)
| mv-expand Technique = Techniques
| summarize AlertCount = count(), Titles = make_set(Title) by tostring(Technique)
| sort by AlertCount descThe incident queue is your starting point. Defender XDR automatically correlates related alerts from all workloads into a single incident. Your first task is to locate the incident, assess its severity, and take ownership.
APT, IR-Active, Phishing, Credential-TheftUse Advanced Hunting to gather a consolidated view of all alerts correlated to the incident:
// CONSOLIDATED ALERT VIEW for the target incident
// Purpose: Get all alerts correlated to an incident in one table,
// including the evidence entities attached to each alert.
//
// How it works:
// Inner join of AlertInfo + AlertEvidence on AlertId links each alert
// to its evidence (devices, users, files, IPs, URLs involved).
// Title filter narrows to alerts related to the attack phases.
//
// Output columns:
// Title β alert name describing the detection
// Category β MITRE tactic (InitialAccess, Execution, LateralMovement, etc.)
// EntityType β type of evidence (Device, User, File, IP, URL)
// RemediationStatus β whether automated remediation has addressed this alert
// DetectionSource β which Defender product generated the alert
// Sort by Timestamp ascending to see the attack progression chronologically.
AlertInfo
| where Timestamp > ago(7d)
| join kind=inner (
AlertEvidence
| where Timestamp > ago(7d)
) on AlertId
| where Title has_any ("phishing", "credential", "lateral", "exfiltration", "suspicious")
| project Timestamp, AlertId, Title, Severity, Category,
EntityType, RemediationStatus, DetectionSource
| sort by Timestamp ascThe attack timeline reconstructs the sequence of events across all workloads. Using the incident graph, alert story, and entity details you will build a chronological narrative of how the attack unfolded: from the initial phishing email to data exfiltration.
Use Advanced Hunting to construct a unified cross-workload timeline for the compromised user:
// ============================================================
// CROSS-WORKLOAD INVESTIGATION TIMELINE
// Purpose: Build a unified chronological view of ALL activity for the
// compromised user across Email, Endpoint, Identity, and Cloud Apps.
// This is the most important query during incident investigation.
//
// How it works:
// - union combines 4 sub-queries (one per Defender product)
// - Each sub-query targets the same user and 48h investigation window
// - Workload column identifies the source product
// - Action column provides a human-readable event summary
// - Results sorted chronologically to reveal the attack narrative
//
// Reading the output:
// MDO row first β phishing email was the entry point
// MDE rows after β process execution on the endpoint (payload)
// MDI rows β credential usage on other devices (lateral movement)
// MDA rows β cloud app access (data exfiltration)
// ============================================================
let targetUser = "j.smith@globex.com";
let investigationWindow = ago(48h);
// Email events: phishing delivery and any other mail to/from target
EmailEvents
| where Timestamp > investigationWindow
| where RecipientEmailAddress =~ targetUser
| project Timestamp, Workload = "MDO", Action = strcat("Email: ", Subject),
Detail = SenderFromAddress, Entity = RecipientEmailAddress
| union (
// Endpoint process events: commands executed on devices by target user
DeviceProcessEvents
| where Timestamp > investigationWindow
| where AccountUpn =~ targetUser
| project Timestamp, Workload = "MDE", Action = strcat("Process: ", FileName),
Detail = ProcessCommandLine, Entity = DeviceName
)
| union (
// Identity logon events: authentication attempts by target user
// DeviceName = source device, DestinationDeviceName = target device
IdentityLogonEvents
| where Timestamp > investigationWindow
| where AccountUpn =~ targetUser
| project Timestamp, Workload = "MDI", Action = strcat("Logon: ", LogonType),
Detail = strcat(DeviceName, " β ", DestinationDeviceName), Entity = AccountUpn
)
| union (
// Cloud app activity: SaaS application usage (file access, uploads, sharing)
CloudAppEvents
| where Timestamp > investigationWindow
| where AccountId =~ targetUser
| project Timestamp, Workload = "MDA", Action = strcat("CloudApp: ", ActionType),
Detail = Application, Entity = AccountId
)
| sort by Timestamp ascThe phishing email is the entry point of this attack. In this step you will use Defender for Office 365 capabilities: Threat Explorer and the email entity page: to fully analyze the malicious message, understand why it bypassed filters, and determine if additional recipients were targeted.
vendor-invoices[.]cc)Use Advanced Hunting to find all emails from the same sender domain and identify other potential victims:
// HUNT FOR ADDITIONAL PHISHING TARGETS from the same sender domain
// Purpose: Determine if the phishing campaign targeted other users beyond
// the known victim. Other recipients may have also opened the email.
// Replace "vendor-invoices.cc" with the actual malicious sender domain.
// Output: Each row = an email from the phishing domain
// DeliveryAction = "Delivered" means the email reached the inbox
// DeliveryAction = "Blocked" means filtering caught it
// If additional users received delivered emails, check their identity
// and endpoint telemetry for post-compromise activity.
EmailEvents
| where Timestamp > ago(7d)
| where SenderFromDomain =~ "vendor-invoices.cc"
or SenderMailFromDomain =~ "vendor-invoices.cc"
| project Timestamp, SenderFromAddress, RecipientEmailAddress,
Subject, DeliveryAction, DeliveryLocation,
ThreatTypes, DetectionMethods
| sort by Timestamp asc// CORRELATE EMAIL ATTACHMENTS with endpoint file creation
// Purpose: Determine if the malicious attachment was saved/opened on any endpoint.
// How it works:
// 1. Gets all attachments from the phishing sender domain
// 2. Left outer joins with DeviceFileEvents on SHA256 hash
// 3. If a match exists, the file was written to disk on that device
//
// Output columns:
// SHA256 β file hash (search in VirusTotal for malware classification)
// DeviceName β if populated, the file was opened/saved on this endpoint
// FolderPath β where the file was saved (Downloads, Temp = user opened it)
// A NULL DeviceName means the attachment was in the email but not opened.
EmailAttachmentInfo
| where Timestamp > ago(7d)
| where SenderFromDomain =~ "vendor-invoices.cc"
| project Timestamp, SenderFromAddress, RecipientEmailAddress,
FileName, FileType, SHA256,
ThreatTypes, DetectionMethods
| join kind=leftouter (
DeviceFileEvents
| where Timestamp > ago(7d)
| where ActionType == "FileCreated"
| project SHA256, DeviceName, FolderPath, FileName
) on SHA256Once the phishing email delivered its payload, the attack moved to the endpoint. In this step you will use the device timeline, process tree, and file entity page to trace exactly what happened on the compromised workstation after the user opened the malicious attachment.
WS-FINANCE-01) to open the Device pageWINWORD.EXE → cmd.exe → powershell.exe: this indicates macro execution launching a PowerShell payloadWINWORD.EXE to the final payloadpowershell.exe with encoded commands, certutil.exe for file download, schtasks.exe for persistence// TRACE THE MACRO ATTACK CHAIN: Word β cmd β PowerShell β payload
// MITRE ATT&CK: T1204.002 (User Execution: Malicious File)
// T1059.001 (PowerShell) + T1059.003 (Windows Command Shell)
// Purpose: Reconstruct the exact process execution chain on the compromised
// endpoint after the user opened the malicious Word document.
//
// Detection logic:
// 1. WINWORD.EXE as InitiatingProcessFileName = macro launched a process
// 2. cmd.exe as InitiatingProcessFileName = command shell launched by macro
// 3. FileName matches interpreters or LOLBins = payload execution
//
// The process chain reveals the attack vector:
// WINWORD.EXE β cmd.exe β powershell.exe -enc [base64] = classic macro attack
// WINWORD.EXE β mshta.exe = HTML Application payload
// WINWORD.EXE β certutil.exe -urlcache = file download via LOLBin
DeviceProcessEvents
| where Timestamp > ago(48h)
| where DeviceName =~ "WS-FINANCE-01"
| where InitiatingProcessFileName =~ "WINWORD.EXE"
or InitiatingProcessFileName =~ "cmd.exe"
or FileName in~ ("powershell.exe", "pwsh.exe", "certutil.exe", "schtasks.exe", "mshta.exe")
| project Timestamp, DeviceName, FileName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine,
AccountName, SHA256
| sort by Timestamp asc// DETECT MALICIOUS SCHEDULED TASK PERSISTENCE
// MITRE ATT&CK: T1053.005 (Scheduled Task/Job: Scheduled Task)
// Purpose: Attackers create scheduled tasks to survive reboots and maintain
// persistent access. Common disguise names mimic Windows Update or browser updates.
//
// How it works:
// Filters DeviceEvents for ScheduledTaskCreated action type on the target device.
// Parses AdditionalFields JSON to extract TaskName and TaskContent (the command).
// Filters for suspicious task names that impersonate legitimate Windows services.
//
// Suspicious task names to look for:
// "WindowsUpdateCheck" / "SystemHealthMonitor" / "ChromeUpdate" = common attacker names
// Real Windows tasks have specific naming patterns (e.g., "\Microsoft\Windows\...")
// Check TaskContent for the actual command β PowerShell or executable paths confirm malice.
DeviceEvents
| where Timestamp > ago(48h)
| where DeviceName =~ "WS-FINANCE-01"
| where ActionType == "ScheduledTaskCreated"
| project Timestamp, DeviceName, ActionType,
AdditionalFields = parse_json(AdditionalFields)
| extend TaskName = tostring(AdditionalFields.TaskName),
TaskContent = tostring(AdditionalFields.TaskContent)
| where TaskName has_any ("WindowsUpdateCheck", "SystemHealthMonitor", "ChromeUpdate")// DETECT MALWARE FILE DROPS from the attack process chain
// MITRE ATT&CK: T1105 (Ingress Tool Transfer)
// Purpose: Find files written to disk by the malicious processes
// (PowerShell, cmd, certutil) that form the attack chain.
//
// Detection logic:
// ActionType in (FileCreated, FileModified) β new or changed files
// InitiatingProcessFileName β only files created by suspicious processes
//
// Key output columns:
// SHA256 β file hash for VirusTotal/TI lookup and IoC blocking
// FileSize β very small files may be scripts; large files may be tools
// FolderPath β files in Temp, ProgramData, or AppData\Local are suspicious;
// files in System32 or Program Files indicate deeper compromise
// Use the SHA256 values to create block indicators in Settings β Indicators.
DeviceFileEvents
| where Timestamp > ago(48h)
| where DeviceName =~ "WS-FINANCE-01"
| where ActionType in ("FileCreated", "FileModified")
| where InitiatingProcessFileName in~ ("powershell.exe", "cmd.exe", "certutil.exe")
| project Timestamp, DeviceName, ActionType, FileName, FolderPath,
SHA256, FileSize, InitiatingProcessFileName,
InitiatingProcessCommandLine
| sort by Timestamp ascAfter gaining initial access to the endpoint, the attacker harvested credentials. Now you need to investigate the identity vector: detecting sign-in anomalies, credential theft indicators, and evidence that stolen credentials were used to access additional resources.
j.smith) by clicking the user entity// DETECT LSASS MEMORY ACCESS β Credential Dumping Indicator
// MITRE ATT&CK: T1003.001 (OS Credential Dumping: LSASS Memory)
// Why: LSASS (Local Security Authority Subsystem Service) stores hashed
// credentials in memory. Tools like Mimikatz, procdump, or direct API
// calls access LSASS to extract NTLM hashes and Kerberos tickets.
//
// Detection logic:
// Finds processes that access or reference lsass.exe.
// Excludes legitimate system processes that normally interact with LSASS:
// svchost.exe, csrss.exe, wininit.exe = Windows core processes
// MsMpEng.exe = Defender Antivirus engine
//
// Key output:
// InitiatingProcessFileName β WHAT accessed LSASS (if not in exclusion list,
// itβs suspicious). Common malicious: rundll32.exe, unknown.exe, procdump.exe
// InitiatingProcessParentFileName β what launched the accessing process
// CRITICAL: If LSASS access is confirmed, ALL credentials on that device
// are potentially compromised (all logged-in users, service accounts, etc.)
DeviceProcessEvents
| where Timestamp > ago(48h)
| where FileName =~ "lsass.exe" or ProcessCommandLine has "lsass"
| where InitiatingProcessFileName !in~ ("svchost.exe", "csrss.exe", "wininit.exe", "MsMpEng.exe")
| project Timestamp, DeviceName, AccountName,
InitiatingProcessFileName,
InitiatingProcessCommandLine,
InitiatingProcessParentFileName
| sort by Timestamp asc// DETECT PASS-THE-HASH / PASS-THE-TICKET via anomalous logons
// MITRE ATT&CK: T1550.002 (Pass the Hash) / T1550.003 (Pass the Ticket)
// Purpose: After credential theft, trace how stolen credentials were used.
// Look for logons to devices the user doesnβt normally access, unusual
// protocols, and logon types indicating remote access.
//
// Key columns to examine:
// LogonType:
// Interactive = direct console logon (normal for userβs own device)
// RemoteInteractive = RDP session (suspicious if to a server)
// Network = SMB/NTLM authentication (PtH lateral movement)
// DestinationDeviceName β where the credentials were used
// Protocol β "NTLM" with Network logon to multiple devices = PtH
// FailureReason β empty for success; populated for failures (brute force)
IdentityLogonEvents
| where Timestamp > ago(48h)
| where AccountUpn =~ "j.smith@globex.com"
| where LogonType in ("Interactive", "RemoteInteractive", "Network")
| project Timestamp, AccountUpn, DeviceName,
DestinationDeviceName, LogonType,
Application, Protocol, FailureReason
| sort by Timestamp asc// DETECT KERBEROS TICKET ANOMALIES β Overpass-the-Hash / Golden Ticket
// MITRE ATT&CK: T1558 (Steal or Forge Kerberos Tickets)
// Purpose: After stealing NTLM hashes, attackers may forge Kerberos tickets
// to access any resource without knowing the password.
//
// IdentityQueryEvents captures AD queries including Kerberos ticket requests.
// Suspicious patterns:
// - Kerberos TGT requests from unusual devices
// - LDAP queries for sensitive groups (Domain Admins, Enterprise Admins)
// - Abnormal QueryTarget values (targeting service accounts or DCs)
//
// Golden Ticket indicator: if QueryType shows Kerberos activity from a
// device where the user never normally authenticates, this may indicate
// a forged ticket being used.
IdentityQueryEvents
| where Timestamp > ago(48h)
| where ActionType has_any ("Kerberos", "LDAP")
| where AccountUpn =~ "j.smith@globex.com"
| project Timestamp, AccountUpn, ActionType,
DeviceName, DestinationDeviceName,
QueryType, QueryTarget
| sort by Timestamp ascWith stolen credentials in hand, the attacker moves laterally to high-value targets: domain controllers, file servers, and cloud applications. This step traces cross-device activity and identifies cloud app abuse that leads to data exfiltration.
WS-FINANCE-01) and other devices (DC01, FS02)// DETECT LATERAL MOVEMENT via remote logons from compromised account
// MITRE ATT&CK: T1021.002 (Remote Services: SMB/Windows Admin Shares)
// Purpose: After credential theft, attackers use stolen credentials to
// authenticate to other machines. This query finds logons from the
// compromised user to devices OTHER than their primary workstation.
//
// Key filter: DestinationDeviceName !~ "WS-FINANCE-01" excludes the
// userβs own device to focus on lateral movement to NEW devices.
// LogonType explanation:
// RemoteInteractive = RDP session to the target (interactive control)
// Network = SMB/NTLM authentication (file share access, PsExec, WMI)
// Each row = the compromised account accessed a different device.
// Multiple devices in a short time = active lateral movement campaign.
IdentityLogonEvents
| where Timestamp > ago(48h)
| where AccountUpn =~ "j.smith@globex.com"
| where LogonType in ("RemoteInteractive", "Network")
| where DestinationDeviceName !~ "WS-FINANCE-01" // Exclude the source device
| project Timestamp, AccountUpn, DeviceName,
DestinationDeviceName, LogonType,
Protocol, IPAddress
| sort by Timestamp asc// DETECT REMOTE PROCESS EXECUTION on secondary target machines
// MITRE ATT&CK: T1047 (WMI) / T1569.002 (Service Execution) / T1021.006 (WinRM)
// Purpose: After lateral movement, attackers execute commands on target devices.
// This query detects when remote execution frameworks spawn suspicious processes
// on the domain controller (DC01) and file server (FS02).
//
// InitiatingProcessFileName values that indicate remote execution:
// wmiprvse.exe = WMI remote execution
// wsmprovhost.exe = WinRM / PowerShell Remoting
// PSEXESVC.exe = PsExec service (created on target machine)
// services.exe = new service installation (SCM-based execution)
//
// Exclusion: svchost.exe and taskhostw.exe are normal child processes of
// services.exe and shouldnβt trigger alerts.
// Output: FileName + ProcessCommandLine = what the attacker actually ran.
DeviceProcessEvents
| where Timestamp > ago(48h)
| where DeviceName in~ ("DC01", "FS02")
| where InitiatingProcessFileName in~ ("wmiprvse.exe", "wsmprovhost.exe", "PSEXESVC.exe", "services.exe")
| where FileName !in~ ("svchost.exe", "taskhostw.exe")
| project Timestamp, DeviceName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AccountName, AccountDomain
| sort by Timestamp ascj.smith@globex.com and time range: past 48 hours// DETECT DATA EXFILTRATION via Cloud App Abuse
// MITRE ATT&CK: T1567.002 (Exfiltration Over Web Service: Exfiltration to Cloud Storage)
// Purpose: Detect when the compromised account performs bulk file operations
// (mass downloads, external sharing, uploads to third-party storage).
//
// ActionTypes indicating exfiltration:
// FileUploaded β files uploaded to cloud storage (OneDrive, Box, etc.)
// FileDownloaded β mass downloads from SharePoint/OneDrive
// FileSyncDownloadedFull β full sync download (entire library)
// SharingSet β files shared externally (to outside recipients)
// AddedToGroup β added to group with access to sensitive data
//
// Threshold: EventCount > 20 per hour flags anomalous volume.
// Normal daily operation for one user is typically < 10 file operations/hour.
// Values > 50 strongly suggest automated/scripted data collection.
// UniqueFiles shows how many distinct files were accessed β high values = bulk exfil.
CloudAppEvents
| where Timestamp > ago(48h)
| where AccountId =~ "j.smith@globex.com"
| where ActionType in ("FileUploaded", "FileDownloaded", "FileSyncDownloadedFull",
"SharingSet", "AddedToGroup")
| summarize EventCount = count(),
UniqueFiles = dcount(ObjectName),
Apps = make_set(Application)
by AccountId, ActionType, bin(Timestamp, 1h)
| where EventCount > 20 // Threshold for anomalous volume
| sort by Timestamp asc// DETECT LARGE OUTBOUND DATA TRANSFERS from compromised devices
// MITRE ATT&CK: T1041 (Exfiltration Over C2 Channel) / T1048 (Exfiltration Over Alternative Protocol)
// Purpose: Detect when compromised endpoints send unusually large amounts
// of data to public IP addresses, indicating data exfiltration.
//
// How it works:
// 1. Filters to successful outbound connections from compromised devices
// 2. RemoteIPType == "Public" excludes internal traffic (only internet-facing)
// 3. Aggregates TotalBytesSent per destination per hour
// 4. Threshold: > 50 MB (50,000,000 bytes) in a single hour is anomalous
//
// Output columns:
// TotalBytesSent β volume of data sent (high values = exfiltration)
// RemoteIP/RemoteUrl β where data was sent (check threat intel / VirusTotal)
// ConnectionCount β many connections = beaconing or chunked transfer
// UniqueDestinations β data sent to many IPs = distributed exfil or scanning
// Cross-reference RemoteIP with your threat intelligence feeds.
DeviceNetworkEvents
| where Timestamp > ago(48h)
| where DeviceName in~ ("WS-FINANCE-01", "FS02")
| where ActionType == "ConnectionSuccess"
| where RemoteIPType == "Public"
| summarize TotalBytesSent = sum(SentBytes),
ConnectionCount = count(),
UniqueDestinations = dcount(RemoteIP)
by DeviceName, RemoteIP, RemoteUrl, bin(Timestamp, 1h)
| where TotalBytesSent > 50000000 // >50 MB threshold
| sort by TotalBytesSent descRemoteIP addresses from network events with threat intelligence feeds. In Defender XDR, click on any IP address entity to view its reputation, geographic location, ASN, and whether it has been observed in other incidents across your tenant or the broader Microsoft threat intelligence graph.With the investigation complete and the attack scope identified, it is time to contain the threat. Containment stops the attacker from extending their reach while you prepare eradication. Speed is critical: every minute of delay gives the adversary more time to exfiltrate data or establish additional persistence.
WS-FINANCE-01FS02 if compromise was confirmed)j.smith@globex.com)# CONTAINMENT: Disable compromised user accounts in Microsoft Entra ID
# Scope: User.ReadWrite.All β required to modify user account properties
# Purpose: Immediately prevent the attacker from using stolen credentials
# to access ANY Microsoft 365 or Azure resource.
# Impact: The user will be unable to log in until the account is re-enabled.
# All active sessions continue until tokens expire or are revoked (next step).
# Expected output: "Disabled account: j.smith@globex.com" for each user
Connect-MgGraph -Scopes "User.ReadWrite.All"
$compromisedUsers = @(
"j.smith@globex.com"
# Add additional compromised accounts as identified
)
foreach ($user in $compromisedUsers) {
Update-MgUser -UserId $user -AccountEnabled:$false
Write-Host "Disabled account: $user" -ForegroundColor Yellow
}# CONTAINMENT: Revoke all refresh tokens and active sessions
# Purpose: Even after disabling the account, existing access tokens
# remain valid until they expire (typically 1 hour for access tokens).
# Revoke-MgUserSignInSession forces immediate invalidation of ALL
# refresh tokens, requiring re-authentication on every session.
# This ensures the attacker cannot use any previously issued tokens.
# Expected output: "Revoked sessions for: j.smith@globex.com"
# followed by account status verification showing Enabled=False
foreach ($user in $compromisedUsers) {
Revoke-MgUserSignInSession -UserId $user
Write-Host "Revoked sessions for: $user" -ForegroundColor Yellow
}
# Verify account status β confirm all accounts show Enabled=False
foreach ($user in $compromisedUsers) {
$userObj = Get-MgUser -UserId $user -Property AccountEnabled, DisplayName
Write-Host "$($userObj.DisplayName): Enabled=$($userObj.AccountEnabled)"
}update-service[.]cc (C2 domain) and vendor-invoices[.]cc (phishing sender domain)Containment stops the bleeding; eradication removes the infection. In this step you will remove all persistence mechanisms, reset compromised credentials, re-enable accounts with fresh credentials, and establish monitoring to confirm the threat is fully neutralized.
WS-FINANCE-01), use the Live Response feature to connect remotely# ERADICATION: Live Response commands β run on the isolated device
# Purpose: Remove all persistence mechanisms and malware artifacts identified
# during investigation. Live Response connects to the isolated device via
# the Defender for Endpoint cloud channel (works even when device is isolated).
#
# Step 1: Remove the malicious scheduled task used for persistence
# The "remediate" command removes the task and logs the action
remediate scheduledtask "WindowsUpdateCheck"
# Step 2: Verify the task was removed by listing all scheduled tasks
scheduledtasks
# Step 3: Remove the dropped malware files
# Remediate moves files to quarantine rather than permanently deleting,
# preserving them as evidence for post-incident forensic analysis
remediate file "C:\ProgramData\Microsoft\WindowsUpdateCheck.exe"
remediate file "C:\Users\j.smith\AppData\Local\Temp\payload.ps1"
# Step 4: Verify file removal β should return "file not found"
dir "C:\ProgramData\Microsoft\WindowsUpdateCheck.exe"
dir "C:\Users\j.smith\AppData\Local\Temp\payload.ps1"# ERADICATION: Reset passwords for all compromised accounts
# Scope: User.ReadWrite.All β required to reset passwords
# Purpose: Replace stolen credentials with new, strong passwords.
# ForceChangePasswordNextSignIn ensures the user sets their own
# permanent password at next login.
#
# Security: The temporary password is generated using cryptographically
# random characters (letters, numbers, special chars, 24 chars long).
# IMPORTANT: Communicate the temporary password via a secure
# out-of-band channel (phone call, in-person). NEVER send via email
# (the attacker may still have access to the mailbox).
#
# Also lists authentication methods to help plan MFA re-registration.
# If the attacker registered their own MFA method, you must remove it
# before re-enabling the account.
Connect-MgGraph -Scopes "User.ReadWrite.All"
$compromisedUsers = @("j.smith@globex.com")
foreach ($user in $compromisedUsers) {
# Generate a strong temporary password
$tempPassword = -join ((65..90) + (97..122) + (48..57) + (33..38) |
Get-Random -Count 24 | ForEach-Object { [char]$_ })
$passwordProfile = @{
Password = $tempPassword
ForceChangePasswordNextSignIn = $true
}
Update-MgUser -UserId $user -PasswordProfile $passwordProfile
Write-Host "Password reset for: $user (temp password generated)" -ForegroundColor Green
# Force MFA re-registration
# Remove existing MFA methods so the user must re-register
$authMethods = Get-MgUserAuthenticationMethod -UserId $user
Write-Host " Authentication methods count: $($authMethods.Count)"
}# ERADICATION: Reset KRBTGT password (run on domain controller)
# MITRE ATT&CK: T1558.001 (Golden Ticket defense)
# Purpose: The KRBTGT account is used to encrypt all Kerberos tickets in AD.
# If the attacker stole the KRBTGT hash, they can forge Golden Tickets
# granting unrestricted access to any resource in the domain.
#
# CRITICAL: Reset KRBTGT TWICE with a 10+ hour gap between resets.
# - First reset: invalidates tickets issued with the current hash
# - Wait 10+ hours: allows maximum ticket lifetime (10h default) to expire
# - Second reset: invalidates tickets forged with the first new hash
# Without the second reset, attackers who captured the new hash during
# the first reset window can still forge valid tickets.
#
# WARNING: Monitor domain authentication closely after reset.
# Some services using cached Kerberos tickets may experience brief disruptions.
# First reset:
Set-ADAccountPassword -Identity krbtgt -Reset `
-NewPassword (ConvertTo-SecureString -AsPlainText (
-join ((65..90) + (97..122) + (48..57) | Get-Random -Count 32 |
ForEach-Object { [char]$_ })
) -Force)
Write-Host "KRBTGT password reset #1 complete. Wait 10+ hours before second reset." -ForegroundColor Yellow
# After 10+ hours, run the second reset to fully invalidate old tickets# RECOVERY: Re-enable user accounts after credential reset is confirmed
# Purpose: Once new passwords are set and MFA re-registration is required,
# re-enable the accounts so users can log in with fresh credentials.
# Expected output: "Re-enabled account: j.smith@globex.com"
#
# IMPORTANT: Communicate the temporary password via a SECURE out-of-band channel:
# - Phone call (verify user identity first)
# - In-person delivery
# - Encrypted messaging app
# NEVER send credentials via email β the attacker may still have
# inbox access via forwarding rules or OAuth app consents.
foreach ($user in $compromisedUsers) {
Update-MgUser -UserId $user -AccountEnabled:$true
Write-Host "Re-enabled account: $user" -ForegroundColor Green
}
# Communicate temporary password to user via secure out-of-band channel
# (phone call, in-person, encrypted message. NEVER via email)// POST-ERADICATION VERIFICATION: Monitor for persistence re-creation
// Purpose: After removing all malware and persistence mechanisms, run this
// query to verify nothing reappears. Schedule this as a daily check for
// 24-48 hours after eradication is complete.
//
// What to watch for:
// ScheduledTaskCreated β new scheduled tasks (attacker's persistence returning)
// ScheduledTaskUpdated β modification of existing tasks (hiding in legitimate tasks)
// RegistryValueSet β autorun registry keys (Run, RunOnce, Services)
// ServiceInstalled β new Windows services (common persistence method)
//
// The Detail column extracts the specific artifact name from AdditionalFields JSON.
// If ANY results appear after eradication, the threat was NOT fully removed β
// re-investigate immediately and look for secondary backdoors.
// Empty results = successful eradication confirmed.
DeviceEvents
| where Timestamp > ago(24h)
| where DeviceName =~ "WS-FINANCE-01"
| where ActionType in ("ScheduledTaskCreated", "ScheduledTaskUpdated",
"RegistryValueSet", "ServiceInstalled")
| project Timestamp, DeviceName, ActionType,
AdditionalFields = parse_json(AdditionalFields)
| extend Detail = case(
ActionType == "ScheduledTaskCreated", tostring(AdditionalFields.TaskName),
ActionType == "RegistryValueSet", tostring(AdditionalFields.RegistryValueName),
ActionType == "ServiceInstalled", tostring(AdditionalFields.ServiceName),
"N/A"
)The incident is contained and eradicated, but the work is not done. A thorough post-incident review (PIR) documents what happened, identifies gaps in detection and response, and drives improvements. This step produces the deliverables that transform a painful incident into a learning opportunity.
Generate a comprehensive mapping of all observed techniques to include in the report:
// Comprehensive MITRE ATT&CK mapping for the incident
AlertInfo
| where Timestamp > ago(7d)
| where AttackTechniques != ""
| extend Techniques = parse_json(AttackTechniques)
| mv-expand Technique = Techniques
| extend TechniqueStr = tostring(Technique)
| summarize
AlertCount = count(),
AlertTitles = make_set(Title),
Severities = make_set(Severity),
DetectionSources = make_set(DetectionSource),
FirstSeen = min(Timestamp),
LastSeen = max(Timestamp)
by TechniqueStr
| sort by FirstSeen ascBlock all Office applications from creating child processes)Based on lessons learned, create a custom detection rule to catch this attack pattern in the future:
// Custom detection: Office app spawning PowerShell with download cradle
DeviceProcessEvents
| where Timestamp > ago(1h)
| where InitiatingProcessFileName in~ ("WINWORD.EXE", "EXCEL.EXE", "POWERPNT.EXE")
| where FileName in~ ("powershell.exe", "pwsh.exe", "cmd.exe", "wscript.exe",
"cscript.exe", "mshta.exe")
| where ProcessCommandLine has_any (
"DownloadString", "DownloadFile", "Invoke-WebRequest",
"IEX", "Invoke-Expression", "Net.WebClient",
"-EncodedCommand", "-enc ", "FromBase64String",
"Start-BitsTransfer", "certutil"
)
| project Timestamp, DeviceName, AccountName, FileName,
ProcessCommandLine, InitiatingProcessFileName,
InitiatingProcessCommandLine, SHA256
// Entity mapping for custom detection rule:
// DeviceName β Device, AccountName β User, SHA256 β FileUpdate-MgUser -UserId "user@domain.com" -AccountEnabled:$trueIR-Active tags from resolved test incidents| Resource | Description |
|---|---|
| Incidents overview: Microsoft Defender XDR | How incidents are created, correlated, and prioritized in XDR |
| Investigate incidents in Microsoft Defender XDR | Step-by-step guide to investigating and analyzing XDR incidents |
| Respond to your first incident in Microsoft Defender XDR | Walkthrough for responding to your first security incident |
| Advanced hunting overview: Microsoft Defender XDR | Proactively search for threats using KQL across workloads |
| Take response actions on a device: Microsoft Defender for Endpoint | Isolate, scan, and remediate devices through Defender for Endpoint |
| Threat Explorer and Real-time detections: Microsoft Defender for Office 365 | Investigate email threats using Explorer and real-time detection tools |
| Credential access and lateral movement alerts: Microsoft Defender for Identity | Detect credential theft and lateral movement with Defender for Identity |
| Investigate activities: Microsoft Defender for Cloud Apps | Review and investigate user and app activities in Cloud Apps |