Multiple users forwarding emails to the same destination may indicate a compromised mailbox or a collection point used by an adversary to exfiltrate data. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify potential data exfiltration or reconnaissance activities early.
KQL Query
OfficeActivity
| where OfficeWorkload =~ "Exchange"
| where Parameters has_any ("ForwardTo", "RedirectTo", "ForwardingSmtpAddress")
| mv-apply DynamicParameters = todynamic(Parameters) on (summarize ParsedParameters = make_bag(pack(tostring(DynamicParameters.Name), DynamicParameters.Value)))
| evaluate bag_unpack(ParsedParameters, columnsConflict='replace_source')
| extend DestinationMailAddress = tolower(case(
isnotempty(column_ifexists("ForwardTo", "")), column_ifexists("ForwardTo", ""),
isnotempty(column_ifexists("RedirectTo", "")), column_ifexists("RedirectTo", ""),
isnotempty(column_ifexists("ForwardingSmtpAddress", "")), trim_start(@"smtp:", column_ifexists("ForwardingSmtpAddress", "")),
""))
| where isnotempty(DestinationMailAddress)
| mv-expand split(DestinationMailAddress, ";")
| extend ClientIPValues = extract_all(@'\[?(::ffff:)?(?P<IPAddress>(\d+\.\d+\.\d+\.\d+)|[^\]]+)\]?([-:](?P<Port>\d+))?', dynamic(["IPAddress", "Port"]), ClientIP)[0]
| extend ClientIP = tostring(ClientIPValues[0]), Port = tostring(ClientIPValues[1])
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), DistinctUserCount = dcount(UserId), UserId = make_set(UserId, 250), Ports = make_set(Port, 250), EventCount = count() by tostring(DestinationMailAddress), ClientIP
| where DistinctUserCount > 1
| mv-expand UserId to typeof(string)
id: 3b05727d-a8d1-477d-bbdd-d957da96ac7b
name: NRT Multiple users email forwarded to same destination
description: |
'Identifies when multiple (more than one) users mailboxes are configured to forward to the same destination.
This could be an attacker-controlled destination mailbox configured to collect mail from multiple compromised user accounts.'
severity: Medium
requiredDataConnectors:
- connectorId: Office365
dataTypes:
- OfficeActivity
tactics:
- Collection
- Exfiltration
relevantTechniques:
- T1114
- T1020
query: |
OfficeActivity
| where OfficeWorkload =~ "Exchange"
| where Parameters has_any ("ForwardTo", "RedirectTo", "ForwardingSmtpAddress")
| mv-apply DynamicParameters = todynamic(Parameters) on (summarize ParsedParameters = make_bag(pack(tostring(DynamicParameters.Name), DynamicParameters.Value)))
| evaluate bag_unpack(ParsedParameters, columnsConflict='replace_source')
| extend DestinationMailAddress = tolower(case(
isnotempty(column_ifexists("ForwardTo", "")), column_ifexists("ForwardTo", ""),
isnotempty(column_ifexists("RedirectTo", "")), column_ifexists("RedirectTo", ""),
isnotempty(column_ifexists("ForwardingSmtpAddress", "")), trim_start(@"smtp:", column_ifexists("ForwardingSmtpAddress", "")),
""))
| where isnotempty(DestinationMailAddress)
| mv-expand split(DestinationMailAddress, ";")
| extend ClientIPValues = extract_all(@'\[?(::ffff:)?(?P<IPAddress>(\d+\.\d+\.\d+\.\d+)|[^\]]+)\]?([-:](?P<Port>\d+))?', dynamic(["IPAddress", "Port"]), ClientIP)[0]
| extend ClientIP = tostring(ClientIPValues[0]), Port = tostring(ClientIPValues[1])
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), DistinctUserCount = dcount(UserId), UserId = make_set(UserId, 250), Ports = make_set(Port, 250), EventCount = count() by tostring(DestinationMailAddress), ClientIP
| where DistinctUserCount > 1
| mv-expand UserId to typeof(string)
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UserId
- entityType: IP
fieldMappings:
- identifier: Address
columnName: ClientIP
version: 1.0.3
kind: NRT
metadata:
source:
kind: Community
author:
name: Pete Bryan
support:
tier: Community
categories:
domains: [ "Security - Threat Protection" ]
| Sentinel Table | Notes |
|---|---|
OfficeActivity | Ensure this data connector is enabled |
Scenario: Email Forwarding for Team Collaboration
Description: A team uses a shared mailbox (e.g., [email protected]) where multiple users forward their emails to collect and manage customer inquiries.
Filter/Exclusion: Exclude emails forwarded to shared mailboxes (e.g., [email protected]) using the to field or a custom tag like X-Forwarded-To: shared-mailbox.
Scenario: Scheduled Job Email Notification
Description: A system administrator configures a scheduled job (e.g., using PowerShell or Ansible) to send email notifications to a central mailbox for monitoring purposes.
Filter/Exclusion: Exclude emails sent from known automation tools or scripts (e.g., [email protected]) or filter by the Subject field containing keywords like “Scheduled Job” or “System Alert”.
Scenario: Email Forwarding for Internal Distribution Lists
Description: Multiple users forward emails to an internal distribution list (e.g., [email protected]) for company-wide announcements.
Filter/Exclusion: Exclude emails sent to distribution lists using the to field or a custom header like X-Distribution-List: true.
Scenario: Email Forwarding for Backup Purposes
Description: A backup system (e.g., Veeam, Commvault) is configured to forward emails to a backup mailbox for archiving.
Filter/Exclusion: Exclude emails sent from backup systems by checking the From field (e.g., [email protected]) or using a custom header like X-Backup-Forward: true.
Scenario: Email Forwarding for User Delegation
Description: A user delegates their mailbox to another user (e.g., for vacation coverage) and forwards emails to a delegated