Business email compromise is the most financially damaging attack type that hits M365 tenants. Not ransomware. Not data theft. BEC — because the attacker uses your own email system to redirect a wire transfer, and once the money moves, it is gone.
The difference between catching a BEC mid-operation and writing the post-mortem after the payment clears comes down to what you do in the first 15 minutes. Not the first day. The first 15 minutes.
Minute 0–3: How did they get in?
Your first query is sign-in logs. Not to confirm the compromise — you already know the account is compromised from the alert. The sign-in logs tell you how they got in, which determines what you are dealing with.
SigninLogs
| where TimeGenerated > ago(7d)
| where UserPrincipalName == "compromised.user@northgate-eng.com"
| where ResultType == 0
| project TimeGenerated, IPAddress, Location, AppDisplayName,
AuthenticationRequirement, ConditionalAccessStatus,
DeviceDetail, RiskLevelDuringSignIn
| sort by TimeGenerated descYou are looking for three things:
The initial compromise sign-in. This is usually the first successful sign-in from an unfamiliar IP or location. If AuthenticationRequirement shows multiFactorAuthentication was satisfied from an attacker IP, you are likely dealing with an AiTM phishing attack or a token theft — not a simple password spray.
Post-compromise sign-ins. How many times has the attacker signed in since the initial compromise? If you see a single sign-in followed by sustained activity, they captured a session token. If you see repeated sign-ins over multiple days, they have the password and possibly registered their own MFA method.
Which applications they accessed. AppDisplayName tells you what the attacker opened. If you see Outlook Web Access and nothing else, the scope might be limited to email. If you see SharePoint, OneDrive, Teams, or Azure Portal, the scope is wider than mailbox compromise.
AuditLogs
| where TimeGenerated > ago(7d)
| where OperationName has_any ("authentication method",
"Register device", "Add registered owner to device",
"Add registered user to device")
| where TargetResources[0].userPrincipalName == "compromised.user@northgate-eng.com"
or InitiatedBy.user.userPrincipalName == "compromised.user@northgate-eng.com"
| project TimeGenerated, OperationName, InitiatedBy, AdditionalDetailsMFA registration and device registration are the same persistence class — the attacker establishes both in the initial burst. If you check one and not the other, you miss half the persistence. A registered device lets the attacker satisfy Conditional Access policies that require device registration, effectively bypassing your access controls from their own hardware.
Minute 3–6: What persistence did they establish?
BEC operators do three things immediately after access: they set up mail forwarding to copy conversations to an external address, they create inbox rules to hide their activity from the legitimate user, and they grant OAuth permissions to maintain access after the session expires. All three are silent. The user sees nothing.
Forwarding first — this is the one teams miss. The attacker sets up SMTP forwarding or a transport rule to copy all inbound email to an external address. This gives them continuous visibility into the mailbox even if you detect and revoke their direct access later. Check forwarding before inbox rules because forwarding is harder for the user to notice.
OfficeActivity
| where TimeGenerated > ago(7d)
| where UserId == "compromised.user@northgate-eng.com"
| where Operation in ("Set-Mailbox", "New-TransportRule", "New-InboxRule",
"Set-InboxRule", "Enable-InboxRule")
| where Parameters has_any ("ForwardingSmtpAddress", "ForwardTo",
"RedirectTo", "MoveToFolder", "DeleteMessage")
| project TimeGenerated, Operation, ParametersThis single query catches all three forwarding and suppression paths: SMTP forwarding (Set-Mailbox with ForwardingSmtpAddress), transport rules (New-TransportRule with ForwardTo or RedirectTo), and inbox rules that move or delete messages. If any of these return results with an external address, the attacker has a copy of everything — and they will keep receiving email even after you lock them out of the account.
Inbox rules. If forwarding was the surveillance mechanism, inbox rules are the cover. The attacker creates rules that move, delete, or mark-as-read any email containing keywords like "fraud", "payment", "suspicious", "security", "MFA", or the attacker's own domain. This prevents the legitimate user from seeing the financial conversation the attacker is about to have. The rule name is often blank or something innocuous like "." or "RSS". The action is usually MoveToFolder: Deleted Items or MoveToFolder: RSS Subscriptions.
OAuth consent grants. The attacker consents to a third-party application that gives them persistent access to the mailbox via Graph API. Even after password reset and session revocation, the OAuth app retains delegated access until you explicitly revoke it.
AuditLogs
| where TimeGenerated > ago(7d)
| where OperationName in ("Consent to application",
"Add app role assignment to service principal",
"Add delegated permission grant")
| where TargetResources[0].userPrincipalName == "compromised.user@northgate-eng.com"
or InitiatedBy.user.userPrincipalName == "compromised.user@northgate-eng.com"
| project TimeGenerated, OperationName,
TargetResources[0].displayName,
AdditionalDetailsThis expanded query catches three OAuth persistence paths: direct consent grants, app role assignments, and delegated permission grants. The basic Consent to application query misses the latter two, which are alternate paths to persistent mailbox access. Any result you do not recognize is the attacker establishing persistence. Revoke it before you reset the password — otherwise the app survives the reset.
Minute 6–10: What did they do with the mailbox?
This is where the investigation forks. If the attacker only read email, you might be dealing with espionage or reconnaissance for a future attack. If the attacker sent email — particularly to finance, suppliers, or customers — you are in an active BEC campaign and the clock is running on wire recall.
OfficeActivity
| where TimeGenerated > ago(7d)
| where UserId == "compromised.user@northgate-eng.com"
| where Operation in ("MailItemsAccessed", "Send", "SendAs", "SendOnBehalf",
"MoveToDeletedItems", "SoftDelete", "HardDelete")
| summarize Count = count() by Operation, bin(TimeGenerated, 1h)
| sort by TimeGenerated ascThe pattern that signals active BEC:
- Attacker reads recent email threads involving payments, invoices, or financial discussions
- Attacker creates an inbox rule to suppress replies from the target
- Attacker sends a reply into an existing thread — changing the payment details, bank account, or wire instructions
- Attacker deletes the sent item from the Sent folder
- The legitimate user never sees the conversation
If you see Send operations from the attacker's session combined with HardDelete operations in the same time window, you have an active BEC with evidence destruction. Notify your finance team immediately — do not wait for the investigation to complete.
Minute 10–15: The scope decision
By minute 10, you have the sign-in method, the persistence mechanisms, and the mail activity. Now you make the decision that determines the next 48 hours of your life.
Path A — Credential theft only. The attacker signed in, read some email, did not create inbox rules, did not grant OAuth apps, did not send any email. This is reconnaissance or a harvested credential that was not yet operationalised. Containment: password reset, MFA re-registration, session revocation, 30-day monitoring. Closed within hours.
Path B — Active BEC campaign. The attacker created inbox rules or forwarding, sent email into financial threads, deleted evidence. Containment: everything in Path A plus immediate finance team notification, eDiscovery litigation hold on the mailbox (the attacker is actively deleting evidence — if you do not preserve now, the recoverable items folder will purge within 14 days and your evidence is gone), preservation of all audit logs, and a check on whether any wire transfers were initiated in the past 72 hours. The litigation hold takes 30 seconds to apply in Purview and preserves everything the attacker deleted. Do it before the password reset — not after.
The difference between these two paths is not severity classification. It is whether someone in your finance department is about to approve a fraudulent payment based on an email that looks like it came from a trusted colleague.
The queries most teams run too late
Three queries that should be in your first 15 minutes but typically do not get run until day two:
1. Unified Audit Log with mailbox access type. The OfficeActivity table catches most mail operations, but for BEC specifically you need the MailItemsAccessed operation with MailAccessType == "Bind" — this catches the attacker reading specific emails via Graph API or sync protocol, not just OWA. If the attacker is using a tool to programmatically scrape the mailbox for payment-related threads, Bind access is how you see it:
OfficeActivity
| where TimeGenerated > ago(7d)
| where UserId == "compromised.user@northgate-eng.com"
| where Operation == "MailItemsAccessed"
| mv-expand OperationProperties
| where OperationProperties.Name == "MailAccessType"
and OperationProperties.Value == "Bind"
| summarize Count = count() by bin(TimeGenerated, 1h), ClientIPAddress
| sort by TimeGenerated asc2. MFA method and device registration. Already covered in Minute 0–3, but it bears repeating because teams forget: if the attacker registered their own MFA method or enrolled a device, your password reset is insufficient. They will re-authenticate with their own credentials from their own hardware.
3. Other accounts compromised by the same session. If the compromised user has admin privileges, check whether the attacker pivoted to other accounts. The attacker who compromises one mailbox often uses that access to phish internal targets — and internal phishing has a much higher success rate than external.
SigninLogs
| where TimeGenerated > ago(7d)
| where IPAddress in (
SigninLogs
| where UserPrincipalName == "compromised.user@northgate-eng.com"
| where ResultType == 0
| distinct IPAddress
)
| where UserPrincipalName != "compromised.user@northgate-eng.com"
| where ResultType == 0
| distinct UserPrincipalName, IPAddressIf this returns other accounts, your investigation just expanded. Each one needs the same 15-minute triage.
What to do this week
- Run the inbox rule query against your tenant right now. Not as an investigation — as a baseline. Know what legitimate inbox rules exist so you recognize the malicious ones during an incident.
- Confirm your audit log retention covers at least 180 days. BEC dwell time can be weeks. If your logs only go back 90 days, you might miss the initial compromise.
- Check whether your Conditional Access policies require compliant devices (not just registered) for Exchange Online access. The "Registered Isn't Compliant" gap is the most common post-AiTM persistence path.
- Verify that your finance team has a callback verification process for wire transfer changes. If they do not, your IR playbook is missing the most important control.
- Pre-stage the six queries from this post in a Sentinel workbook or a saved query library. When the BEC alert fires at 3am, you do not want to be writing KQL from scratch.
Take this further
If you want to run these queries against realistic campaign telemetry in a lab environment — not your production tenant — the free modules in Practical Incident Response walk through BEC investigation methodology with full dataset support. No account required to start: Start the free module →
The M365 Security Operations course covers the Defender XDR and Sentinel operational context these queries run in — cross-workload investigation, incident management, and Advanced Hunting across all Microsoft security data tables: Start the free module →
Next week: The three Sentinel analytics rules that catch BEC operators before they send the fraudulent email — and the tuning that keeps the false positive rate manageable.