Identify suspect mailbox actions and correlate with IP addresses in risky sign-in events.
Exchange mailbox exfiltration refers to the misuse of legitimate features in Exchange Online, Exchange Server, Outlook, and Outlook on the web by threat actors. These actors manipulate these features to forward, move, or delete messages. Their primary objective is to secretly transfer messages from compromised accounts within the organization to an account controlled by the threat actor. Tenant administrators should be vigilant for signs of this data exfiltration activity.
Prerequisites for this KQL query
The tables for this query (AADSignInEventsBeta
and CloudAppEvents
) are available in Microsoft Defender. Perform query using Microsoft Defender > Hunting > Advanced hunting. Advanced hunting has previous 30 days of logs available.
KQL query to identify suspicious mailbox rule actions
The following query identifies certain mailbox actions and compares the user IP address to Entra ID-identified risky sign-in events.
let mailBoxActions = pack_array("New-InboxRule", "Set-InboxRule", "Set-Mailbox", "Set-TransportRule", "New-TransportRule", "Enable-InboxRule");
let impactedUsers = AlertEvidence
| where isnotempty(AccountObjectId)
| distinct AccountObjectId;
let hasImpactedUsers = isnotempty(toscalar(impactedUsers));
let riskySignInSession = AADSignInEventsBeta
| where hasImpactedUsers
| where isnotempty(AccountObjectId)
| where isnotempty(IPAddress)
| where AccountObjectId in (impactedUsers)
| where IsManaged != 1
| where IsCompliant != 1
| where isempty(DeviceTrustType)
| where RiskLevelDuringSignIn >= 10 // Risk score is medium/high/low
| where isnotempty(SessionId)
| distinct SessionId;
let hasRiskySessions = isnotempty(toscalar(riskySignInSession));
CloudAppEvents
| where hasRiskySessions
| where isnotempty(AccountObjectId)
| where ApplicationId == 20893 // Microsoft Exchange Online
| where AccountObjectId in (impactedUsers)
| where ActionType in (mailBoxActions) // Filter only for Inbox rule creation activities
| where RawEventData has_any (riskySignInSession) // RawEventData has the session Id for the risky sign-in
| project Timestamp, ReportId, ActionType, IPAddress, AccountObjectId, ISP, CountryCode
// credit: Microsoft Defender Threat Intelligence
Deconstructing the KQL query
Read on to understand HOW the query above works.
This KQL query helps identify risky sign-in sessions related to mailbox actions in Microsoft Exchange Online for impacted users. It provides relevant details for further investigation or monitoring.
Defining variables
let mailBoxActions = pack_array("New-InboxRule", "Set-InboxRule", "Set-Mailbox", "Set-TransportRule", "New-TransportRule", "Enable-InboxRule");
mailBoxActions
: This variable is an array containing specific mailbox actions such as “New-InboxRule”, “Set-InboxRule”, etc.
let impactedUsers = AlertEvidence
| where isnotempty(AccountObjectId)
| distinct AccountObjectId;
impactedUsers
: It identifies distinct user account object IDs from the AlertEvidence
dataset where the AccountObjectId
field is not empty.
let hasImpactedUsers = isnotempty(toscalar(impactedUsers));
hasImpactedUsers
: A boolean variable that checks if there are any impacted users (i.e., if the impactedUsers
variable is not empty).
Identifying Risky Sign-In Sessions
let riskySignInSession = AADSignInEventsBeta
| where hasImpactedUsers
| where isnotempty(AccountObjectId)
| where isnotempty(IPAddress)
| where AccountObjectId in (impactedUsers)
| where IsManaged != 1
| where IsCompliant != 1
| where isempty(DeviceTrustType)
| where RiskLevelDuringSignIn >= 10 // Risk score is medium/high/low
| where isnotempty(SessionId)
| distinct SessionId;
riskySignInSession
: This variable filters the AADSignInEventsBeta
dataset based on the following criteria:
- It involves impacted users.
- The
AccountObjectId
andIPAddress
fields are not empty. - The user is not managed (
IsManaged != 1
). - The user is not compliant (
IsCompliant != 1
). - The
DeviceTrustType
field is empty. - The risk level during sign-in is at least 10 (medium/high/low).
- The
SessionId
is not empty. - It selects distinct session IDs.
Checking for risky sessions
let hasRiskySessions = isnotempty(toscalar(riskySignInSession));
hasRiskySessions
: A boolean variable that checks if there are any risky sign-in sessions (i.e., if the riskySignInSession
variable is not empty).
Filtering Cloud App events
CloudAppEvents
| where hasRiskySessions
| where isnotempty(AccountObjectId)
| where ApplicationId == 20893 // Microsoft Exchange Online
| where AccountObjectId in (impactedUsers)
| where ActionType in (mailBoxActions) // Filter only for Inbox rule creation activities
| where RawEventData has_any (riskySignInSession) // RawEventData has the session Id for the risky sign-in
The query filters the CloudAppEvents
dataset based on the following conditions:
- There are risky sessions (
hasRiskySessions
is true). - The
AccountObjectId
field is not empty. - The
ApplicationId
is 20893 (indicating Microsoft Exchange Online). - The user account object ID is among the impacted users.
- The action type is one of the specified mailbox actions (e.g., Inbox rule creation).
- The
RawEventData
contains any of the risky session IDs.
Displaying actionable output
| project Timestamp, ReportId, ActionType, IPAddress, AccountObjectId, ISP, CountryCode
Finally, it projects specific fields: Timestamp
, ReportId
, ActionType
, IPAddress
, AccountObjectId
, ISP
, and CountryCode
.
Reference
- All you need to know about automatic email forwarding in Exchange Online | techcommunity.microsoft.com
- Email Collection: Email Forwarding Rule (T1114.003) | attack.mitre.org