A user who clicked on a phishing link or opened a malicious attachment in a reported phishing message may be acting on behalf of another user, indicating potential delegate or shared mailbox misuse. SOC teams should proactively hunt for this behavior to identify unauthorized access or impersonation attempts in their Azure Sentinel environment.
KQL Query
let ExtractInternetMessageId = (af: string) {
coalesce(
tostring(parse_json(af).InternetMessageId),
extract('\"InternetMessageId\":\"(<[^>]+@[^>]+>)\"', 1, af)
)
};
let ReportedMail = materialize(
AlertEvidence
| where Timestamp >= ago(7d)
| where EntityType == "MailMessage"
| where Title == "Email reported by user as malware or phish"
| extend AF = tostring(AdditionalFields)
| extend InternetMessageId = ExtractInternetMessageId(AF)
| where isnotempty(InternetMessageId)
| extend Recipient = tostring(parse_json(AF).Recipient)
| extend Subject = tostring(parse_json(AF).Subject)
| extend NetworkMessageId = coalesce(tostring(parse_json(AF).NetworkMessageId), NetworkMessageId)
| summarize arg_max(Timestamp, *) by AlertId, InternetMessageId
| project AlertTimestamp = Timestamp,
AlertId,
InternetMessageId,
NetworkMessageId,
Recipient,
Subject
);
let DeletedItemsActivity = materialize(
CloudAppEvents
| where Timestamp >= ago(7d)
| where ActionType in ("MovedToDeletedItems", "MoveToDeletedItems")
| where RawEventData has "AffectedItems"
| extend Raw = parse_json(RawEventData)
| mv-expand with_itemindex = AffectedItemIndex AffectedItem = Raw.AffectedItems to typeof(dynamic)
| extend InternetMessageId = tostring(AffectedItem.InternetMessageId)
| where isnotempty(InternetMessageId)
| extend ActorUser = coalesce(
tostring(Raw.UserId),
tostring(Raw.ActorUserId),
tostring(AccountId)
)
| extend ActorIp = coalesce(
tostring(Raw.ClientIP),
tostring(IPAddress)
)
| project AppTimestamp = Timestamp,
ActionType,
InternetMessageId,
AffectedItemIndex,
ActorUser,
ActorIp,
AccountDisplayName,
AccountObjectId,
ReportId
);
ReportedMail
| join kind=inner DeletedItemsActivity on InternetMessageId
| extend RecipientNorm = tolower(extract(@'([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})', 1, tostring(Recipient)))
| extend ActorUserNorm = tolower(extract(@'([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})', 1, tostring(ActorUser)))
| extend RecipientActorMatch = case(
isnotempty(RecipientNorm) and isnotempty(ActorUserNorm) and RecipientNorm == ActorUserNorm, "Match",
isnotempty(RecipientNorm) and isnotempty(ActorUserNorm) and RecipientNorm != ActorUserNorm, "Different",
"Unknown"
)
| project
AppTimestamp,
AlertTimestamp,
AlertId,
InternetMessageId,
NetworkMessageId,
Recipient,
ActorUser,
RecipientActorMatch,
Subject,
ActionType,
AffectedItemIndex,
AccountDisplayName,
AccountObjectId,
ActorIp,
ReportId
| order by AppTimestamp desc
id: a5888069-ad78-4ac5-9241-5ee83eb19d5d
name: Identify acting user for reported phish
description: |
Identifies the user who acted on a reported phishing message and compares that actor with the original recipient, helping investigate delegate or shared mailbox reporting scenarios.
description-detailed: |
This query correlates user-reported phishing alerts with mailbox activity for the same InternetMessageId. It helps analysts determine the actual acting user associated with the report action and compare that user with the message recipient, which is useful when delegates or shared mailbox users report messages on behalf of another mailbox.
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- AlertEvidence
- CloudAppEvents
tactics:
- InitialAccess
relevantTechniques:
- T1566
query: |
let ExtractInternetMessageId = (af: string) {
coalesce(
tostring(parse_json(af).InternetMessageId),
extract('\"InternetMessageId\":\"(<[^>]+@[^>]+>)\"', 1, af)
)
};
let ReportedMail = materialize(
AlertEvidence
| where Timestamp >= ago(7d)
| where EntityType == "MailMessage"
| where Title == "Email reported by user as malware or phish"
| extend AF = tostring(AdditionalFields)
| extend InternetMessageId = ExtractInternetMessageId(AF)
| where isnotempty(InternetMessageId)
| extend Recipient = tostring(parse_json(AF).Recipient)
| extend Subject = tostring(parse_json(AF).Subject)
| extend NetworkMessageId = coalesce(tostring(parse_json(AF).NetworkMessageId), NetworkMessageId)
| summarize arg_max(Timestamp, *) by AlertId, InternetMessageId
| project AlertTimestamp = Timestamp,
AlertId,
InternetMessageId,
NetworkMessageId,
Recipient,
Subject
);
let DeletedItemsActivity = materialize(
CloudAppEvents
| where Timestamp >= ago(7d)
| where ActionType in ("MovedToDeletedItems", "MoveToDeletedItems")
| where RawEventData has "AffectedItems"
| extend Raw = parse_json(RawEventData)
| mv-expand with_itemindex = AffectedItemIndex AffectedItem = Raw.AffectedItems to typeof(dynamic)
| extend InternetMessageId = tostring(AffectedItem.InternetMessageId)
| where isnotempty(InternetMessageId)
| extend ActorUser = coalesce(
tostring(Raw.UserId),
tostring(Raw.ActorUserId),
tostring(AccountId)
)
| extend ActorIp = coalesce(
tostring(Raw.ClientIP),
tostring(IPAddress)
)
| project AppTimestamp = Timestamp,
ActionType,
InternetMessageId,
AffectedItemIndex,
ActorUser,
ActorIp,
AccountDisplayName,
AccountObjectId,
ReportId
);
ReportedMail
| join kind=inner DeletedItemsActivity on InternetMessageId
| extend RecipientNorm = tolower(extract(@'([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,
| Sentinel Table | Notes |
|---|---|
AlertEvidence | Ensure this data connector is enabled |
CloudAppEvents | Ensure this data connector is enabled |
Scenario: A system administrator runs a scheduled job to archive old phishing reports using Microsoft Purview or Exchange Online Archiving.
Filter/Exclusion: Exclude activity from the Microsoft Purview admin or Exchange Online admin user accounts during scheduled maintenance windows.
Scenario: A user receives a phishing report via Microsoft Teams and manually forwards it to the IT security team for review.
Filter/Exclusion: Exclude activity from the IT Security team email address or Teams admin accounts when the message is forwarded from a reporting mailbox.
Scenario: A Power Automate flow is configured to automatically process and report phishing incidents, acting on behalf of the original recipient.
Filter/Exclusion: Exclude flows initiated by Power Automate or Microsoft Flow with specific flow names or runbook IDs associated with phishing reporting.
Scenario: A shared mailbox used for phishing reporting is accessed by a delegate user to review and forward the message to the security team.
Filter/Exclusion: Exclude activity from users who are delegates of the phishing reporting mailbox or shared mailbox during normal reporting hours.
Scenario: A third-party security tool (e.g., Cisco SecureX, CrowdStrike, or SentinelOne) automatically reports phishing incidents to the internal security team, triggering the rule.
Filter/Exclusion: Exclude activity from third-party integration accounts or SIEM tool user accounts that are known to report phishing incidents.