Incident Response Workflow: Phishing Investigation in Microsoft Defender XDR.
The user reported the email.
The subject looked familiar. The sender looked convincing. The link looked like it belonged to a real service.
But Agent Foskett did not start with the screenshot.
He started with the evidence: who received it, where it landed, who clicked, what Defender saw and whether the attacker gained access after the message arrived.
Lesson overview
Learn how to investigate a suspected phishing incident from the first report through message scoping, URL clicks, attachment analysis, identity checks, endpoint pivots and remediation.
Why phishing investigations need a workflow
The phishing investigation workflow
Step 1 — Locate the original message
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
let TimeFrame = 7d;
let ReportedSubject = "invoice";
EmailEvents
| where Timestamp > ago(TimeFrame)
| where Subject has ReportedSubject
| project Timestamp,
NetworkMessageId,
SenderFromAddress,
SenderMailFromAddress,
RecipientEmailAddress,
Subject,
DeliveryAction,
DeliveryLocation
| order by Timestamp descStep 2 — Scope all recipients
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
let TimeFrame = 7d;
let SuspiciousSender = "alerts@example.com";
EmailEvents
| where Timestamp > ago(TimeFrame)
| where SenderFromAddress =~ SuspiciousSender
| summarize RecipientCount = dcount(RecipientEmailAddress),
Recipients = make_set(RecipientEmailAddress, 100),
DeliveryActions = make_set(DeliveryAction, 20),
DeliveryLocations = make_set(DeliveryLocation, 20),
FirstSeen = min(Timestamp),
LastSeen = max(Timestamp)
by SenderFromAddress,
Subject
| order by RecipientCount descStep 3 — Review URLs in the message
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
let TimeFrame = 7d;
let SuspiciousMessages =
EmailEvents
| where Timestamp > ago(TimeFrame)
| where Subject has "invoice"
| project NetworkMessageId;
EmailUrlInfo
| where Timestamp > ago(TimeFrame)
| where NetworkMessageId in (SuspiciousMessages)
| project Timestamp,
NetworkMessageId,
Url,
UrlDomain
| order by Timestamp descStep 4 — Identify who clicked
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
let TimeFrame = 7d;
let SuspiciousDomains = dynamic(["example-login.com", "secure-update.example"]);
UrlClickEvents
| where Timestamp > ago(TimeFrame)
| where Url has_any (SuspiciousDomains)
| project Timestamp,
AccountUpn,
Url,
ActionType,
Workload,
IPAddress,
ThreatTypes
| order by Timestamp descStep 5 — Review attachments
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
let TimeFrame = 7d;
let SuspiciousMessages =
EmailEvents
| where Timestamp > ago(TimeFrame)
| where Subject has "invoice"
| project NetworkMessageId;
EmailAttachmentInfo
| where Timestamp > ago(TimeFrame)
| where NetworkMessageId in (SuspiciousMessages)
| project Timestamp,
NetworkMessageId,
FileName,
FileType,
SHA256,
ThreatTypes,
DetectionMethods
| order by Timestamp descStep 6 — Check endpoint execution after the click
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
let TimeFrame = 7d;
let UsersWhoClicked =
UrlClickEvents
| where Timestamp > ago(TimeFrame)
| where ActionType has_any ("ClickAllowed", "ClickedThrough", "Allowed")
| distinct AccountUpn;
DeviceProcessEvents
| where Timestamp > ago(TimeFrame)
| where AccountUpn in~ (UsersWhoClicked)
| where FileName in~ ("powershell.exe", "cmd.exe", "wscript.exe", "mshta.exe", "rundll32.exe")
| project Timestamp,
DeviceName,
AccountUpn,
FileName,
ProcessCommandLine,
InitiatingProcessFileName
| order by Timestamp descStep 7 — Check for account compromise
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
let TimeFrame = 7d;
let UsersWhoClicked =
UrlClickEvents
| where Timestamp > ago(TimeFrame)
| distinct AccountUpn;
IdentityLogonEvents
| where Timestamp > ago(TimeFrame)
| where AccountUpn in~ (UsersWhoClicked)
| project Timestamp,
AccountUpn,
ActionType,
IPAddress,
Location,
DeviceName,
FailureReason
| order by Timestamp descStep 8 — Build a phishing incident timeline
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
let TimeFrame = 7d;
let TargetUser = "user@contoso.com";
let EmailTimeline =
EmailEvents
| where Timestamp > ago(TimeFrame)
| where RecipientEmailAddress =~ TargetUser
| project Timestamp, EventType = "Email", Detail = Subject, Entity = SenderFromAddress;
let ClickTimeline =
UrlClickEvents
| where Timestamp > ago(TimeFrame)
| where AccountUpn =~ TargetUser
| project Timestamp, EventType = "URL Click", Detail = Url, Entity = ActionType;
let SignInTimeline =
IdentityLogonEvents
| where Timestamp > ago(TimeFrame)
| where AccountUpn =~ TargetUser
| project Timestamp, EventType = "Sign-in", Detail = IPAddress, Entity = ActionType;
union EmailTimeline, ClickTimeline, SignInTimeline
| order by Timestamp ascContainment and remediation actions
Investigation checklist
Related Agent Foskett Academy lessons
Coming next
Final thought
Incident Response Workflow Phishing Investigation in Microsoft Defender XDR
Agent Foskett Academy Lesson 111 teaches defenders how to investigate phishing incidents using Microsoft Defender XDR, EmailEvents, EmailUrlInfo, EmailAttachmentInfo, UrlClickEvents, IdentityLogonEvents and KQL workflows.
Learn phishing incident response with KQL and Microsoft Defender XDR
Phishing investigation workflows help Microsoft security analysts locate suspicious messages, scope recipients, analyse URL clicks, review attachments, validate identity compromise and perform containment.
Microsoft Defender XDR phishing investigation tutorial
This lesson explains how to investigate reported phishing emails, determine delivery, identify users who clicked, review endpoint activity, check account compromise and document incident response actions.
