Home / Blog / Incident Response

Intermediate Incident Response

How to Investigate a Malicious Inbox Rule in Microsoft 365

A practical playbook for investigating a suspicious Microsoft 365 inbox rule: read the rule, scope the account, check what mail was exposed and contain it cleanly.

A new inbox rule is often the first visible sign of a business email compromise, because attackers use rules to hide replies and quietly copy mail out of the mailbox. This guide helps SOC analysts and incident responders work from a single rule alert to a clear answer: what the attacker did, what they could read, and how to remove their access. It focuses on Exchange Online, Microsoft Defender XDR and Microsoft Sentinel.

TechniquesT1564.008 Email Hiding Rules and T1114.003 Email Forwarding Rule
Key evidenceRule parameters, sign-in logs, MailItemsAccessed and message trace
OutcomeA scoped incident, a clean mailbox and a tenant wide check
>

Scenario: Defender raises a suspicious inbox manipulation alert for finance.user@example.com. A rule named . moves any message containing "invoice" or "payment" to RSS Feeds and marks it as read. The rule was created from 203.0.113.50, an address the user has never signed in from. Nobody has reported anything yet, which is exactly why you need to move quickly.

Preserve the rule before you touch it

Your first instinct may be to delete the rule. Export it first, because the rule's name, keywords and destination are evidence. They show what the attacker cared about, and they give you search terms for the rest of the tenant.

Exchange Online PowerShellPowerShell
Connect-ExchangeOnline

Get-InboxRule -Mailbox finance.user@example.com -IncludeHidden |
    Select-Object Name, Enabled, Priority, RuleIdentity, Description, From,
        SubjectContainsWords, BodyContainsWords, SubjectOrBodyContainsWords,
        ForwardTo, ForwardAsAttachmentTo, RedirectTo, MoveToFolder, DeleteMessage, MarkAsRead |
    Export-Csv .\inbox-rules-finance-user.csv -NoTypeInformation

Get-Mailbox finance.user@example.com |
    Select-Object ForwardingSmtpAddress, ForwardingAddress, DeliverToMailboxAndForward

The second command matters because forwarding can be configured on the mailbox itself, with no inbox rule at all. Attackers who know that a rule review is coming may use both.

!

Watch out: Get-InboxRule does not work for members of the View-Only Organization Management role group or for the Global Reader role, so check your access before an incident. ATT&CK also notes that attackers can hide rules by changing their properties through MAPI. If activity continues after a clean result, examine the mailbox with a MAPI editor.

Find out when and how the rule was made

Next, get the audit record. Exchange records rule changes under several operations, and you need all of them. New-InboxRule and Set-InboxRule usually come from Outlook on the web or PowerShell. UpdateInboxRules records changes made from the Outlook client, and it may not show the same detail, so always compare it with the export above.

Defender XDR version

Advanced hunting, based on the Microsoft alert playbookKQL
let user_id = "00000000-0000-0000-0000-000000000000";
CloudAppEvents
| where Timestamp > ago(30d)
| where AccountObjectId == user_id
| where Application == @"Microsoft Exchange Online"
| where ActionType in ("Set-Mailbox", "New-InboxRule", "Set-InboxRule", "UpdateInboxRules")
| project Timestamp, ActionType, CountryCode, City, ISP, IPAddress, RuleConfig = RawEventData.Parameters, RawEventData
| order by Timestamp asc

Sentinel version

Microsoft Sentinel, Microsoft 365 connectorKQL
OfficeActivity
| where TimeGenerated > ago(30d)
| where OfficeWorkload == "Exchange"
| where Operation in ("New-InboxRule", "Set-InboxRule", "UpdateInboxRules", "Set-Mailbox")
| where UserId =~ "finance.user@example.com"
| project TimeGenerated, Operation, UserId, ClientIP, ResultStatus, OfficeObjectId, Parameters
| order by TimeGenerated asc
i

Note: ClientIP in OfficeActivity can include a port number, so normalise it before you compare it with sign-in logs. The query above keeps the raw value for your notes.

Read the rule like an attacker would

Microsoft's alert playbooks focus on three things: keyword filters, the destination folder or address, and rules that act on every message. Compare what you see against what this user would plausibly set up for themselves.

External forwarding

High
Indicator

ForwardTo, RedirectTo or ForwardAsAttachmentTo pointing to an address outside the company, especially with no filter.

Response

Search for the same destination across all mailboxes and trace what was sent there.

Financial keyword hiding

High
Indicator

SubjectOrBodyContainsWords with terms like invoice, payment or bank, plus MoveToFolder and MarkAsRead.

Response

Treat as likely payment fraud and alert the finance team through a separate channel.

Delete everything

High
Indicator

DeleteMessage with no keyword filter, which hides replies and security warnings from the user.

Response

Check the Deleted Items and Recoverable Items folders for what the rule removed.

Odd name or folder

Medium
Indicator

Names like . or .., or messages moved to rarely opened folders such as RSS Feeds or Archive.

Response

Not proof on its own. Confirm with the source IP and the user's sign-in history.

Forward to a personal address

Medium
Indicator

The user may have forwarded mail home, or the address may look personal but belong to the attacker.

Response

Ask the user through a verified channel, and raise a policy breach if they did it themselves.

Scope the compromise

The rule is a symptom, so now look for the access behind it. Start from the IP address on the rule event. Check it across every user, because the same attacker often holds several accounts in one tenant.

Microsoft Sentinel, interactive and non-interactive sign-ins from the suspect IPKQL
let suspectIp = "203.0.113.50";
union SigninLogs, AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(14d)
| where IPAddress == suspectIp
| summarize Attempts = count(), Apps = make_set(AppDisplayName), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated) by UserPrincipalName, ResultType
| order by FirstSeen asc

Non-interactive sign-ins matter here. A stolen session or refresh token often shows up there, and looking only at interactive sign-ins can make the attacker's activity seem much shorter than it was.

Then find other mailboxes with the same rule logic. Search for the forwarding address, and for any keywords from the rule that look unusual.

Microsoft Sentinel, the same destination across the tenantKQL
let destination = "collector@example.net";
OfficeActivity
| where TimeGenerated > ago(30d)
| where OfficeWorkload == "Exchange"
| where Operation in ("New-InboxRule", "Set-InboxRule", "Set-Mailbox")
| where Parameters has destination
| project TimeGenerated, Operation, UserId, ClientIP, Parameters

Finally, check what happened to the account before and after the rule. Look for new authentication methods and application consents, which are common ways of keeping access after a password reset.

Microsoft Sentinel, Entra ID audit logKQL
let account = "finance.user@example.com";
AuditLogs
| where TimeGenerated > ago(14d)
| where OperationName has_any ("security info", "Consent to application", "delegated permission grant")
| where tostring(InitiatedBy) has account or tostring(TargetResources) has account
| project TimeGenerated, OperationName, Result, InitiatedBy, TargetResources
| order by TimeGenerated asc

Work out what mail was exposed

Managers, legal and privacy teams will all ask this question. You need three sources to answer it well.

MailItemsAccessedShows mail accessed by the session. Enabled by default for users with E3 or E5 licences.
Message traceShows messages sent to the forwarding address or to outside recipients from this mailbox.
Sent and deleted itemsShows lures the attacker sent, and the replies the rule hid from the user.
Exchange Online PowerShell, last seven daysPowerShell
Search-UnifiedAuditLog -StartDate 2026-10-28 -EndDate 2026-11-04 `
    -UserIds finance.user@example.com -Operations MailItemsAccessed -ResultSize 1000

Get-MessageTraceV2 -RecipientAddress collector@example.net `
    -StartDate 2026-10-28 -EndDate 2026-11-04 -ResultSize 5000

Get-MessageTraceV2 -SenderAddress finance.user@example.com `
    -StartDate 2026-10-28 -EndDate 2026-11-04 -ResultSize 5000

Get-MessageTraceV2 can search the last 90 days, but each query returns a maximum of 10 days of data. For a longer window, run several queries and combine the results. If you hit the result limit, narrow your dates, as the cmdlet is also throttled to 100 requests in five minutes.

+

Tip: Be careful how you word your conclusion. Say "no evidence of access in the available logs", and state your log coverage, rather than "no mail was read". MailItemsAccessed can be throttled for very active mailboxes, and audit retention depends on your licence and settings.

Contain the account in the right order

If you delete the rule first, you tell the attacker you have noticed, and they still have access. Containment works best as a short planned sequence, ideally completed in a single sitting.

  1. 1
    PreserveSave the rule export, mailbox forwarding settings and your query results to the case record.
    Analyst
  2. 2
    Cut accessReset the password and revoke all sessions so that existing refresh tokens stop working.
    Identity team
  3. 3
    Remove persistenceDisable the rules, clear mailbox forwarding, and review MFA methods and application consents.
    Messaging team
  4. 4
    MonitorWatch the account and the suspect IP for at least several days, and expect another attempt.
    SOC
Containment commands, run after preservation and approvalPowerShell
# Revoke sessions (reset the password as part of the same step)
Connect-MgGraph -Scopes "User.RevokeSessions.All"
Revoke-MgUserSignInSession -UserId finance.user@example.com

# Disable the malicious rule, keeping it available for review
Disable-InboxRule -Mailbox finance.user@example.com -Identity "."

# Clear any mailbox level forwarding
Set-Mailbox finance.user@example.com -ForwardingSmtpAddress $null -ForwardingAddress $null

Microsoft notes that there may be a delay of a few minutes before tokens are revoked after you call revokeSignInSessions, so check for later activity rather than assuming it has stopped. Use the rule's RuleIdentity rather than the name if several rules share the same name.

!

Watch out: A password reset alone may not remove an attacker who registered their own MFA method or got a user to consent to an application. Complete the audit log review above before you hand the account back, or you may have to do this all over again.

Close the loop

The technical work is only part of a business email compromise. Before you close the case, work through the items below.

  • Tell the finance team and any affected suppliers through a verified channel, such as a known phone number, not by replying to the mail thread.
  • Check whether any payments or bank detail changes were requested or approved during the attacker's window of access.
  • Give your legal and privacy teams the list of mail exposed, so that they can decide about any notification duties.
  • Search the tenant for other sign-ins from the same IP and ISP, and for lures sent from the compromised mailbox to colleagues.
  • Block external forwarding by default if you have not done so. ATT&CK lists this as a mitigation, and Set-HostedOutboundSpamFilterPolicy -AutoForwardingMode Off is the Exchange Online setting to review.
  • Record the rule pattern in your hunting library, and turn it into a detection if your existing alert did not catch it first.

A simple hunting query can run on a schedule after the incident. It will produce some false positives, but it surfaces rules worth a quick look. Add terms that are relevant to your organisation.

Microsoft Sentinel, scheduled hunt for suspicious rule patternsKQL
OfficeActivity
| where TimeGenerated > ago(1d)
| where OfficeWorkload == "Exchange"
| where Operation in ("New-InboxRule", "Set-InboxRule")
| where Parameters has_any ("ForwardTo", "RedirectTo", "ForwardAsAttachmentTo", "DeleteMessage", "MoveToFolder", "MarkAsRead")
| where Parameters has_any ("invoice", "payment", "bank", "remittance", "security", "phish")
| project TimeGenerated, Operation, UserId, ClientIP, Parameters

Key takeaways

  1. Export the rule and mailbox forwarding settings before you change anything, including hidden rules with -IncludeHidden.
  2. Check all four audit operations: New-InboxRule, Set-InboxRule, UpdateInboxRules and Set-Mailbox.
  3. Read the rule for external destinations, financial keywords, hidden folders and rules that apply to every message.
  4. Scope from the IP and the rule pattern across the whole tenant, including non-interactive sign-ins.
  5. Use MailItemsAccessed and message trace to describe exposure, and state your log coverage plainly.
  6. Reset, revoke and remove persistence together, then contact affected parties through a separate verified channel.

Save these queries as a ready-made playbook before you need them, and test that your team has the right roles to run Get-InboxRule. The time you save during the first hour of a real incident can be what separates a contained account from a fraudulent payment.