A user reports that colleagues are receiving strange invoices from them, or you notice a mailbox quietly forwarding everything to an outside address. The next hour matters: attackers who get into a mailbox set up persistence quickly, and the order in which you contain, investigate and recover determines whether they get back in. In this post I'll follow the order Microsoft documents for a compromised cloud mailbox, add the short scripts that check every mailbox rather than just the one reported, and finish with the hardening that makes the next incident smaller.
Prerequisites#
- Exchange Online PowerShell V3 (
Connect-ExchangeOnline) and the Microsoft Graph PowerShell SDK with theUser.ReadWrite.AllandUser.RevokeSessions.Allscopes. - Rights to search the audit log (the Audit Logs or View-Only Audit Logs role in Microsoft Purview) and to read sign-in logs in the Microsoft Entra admin center.
- Know the signs Microsoft lists: the mailbox is blocked from sending, mail goes missing, new inbox rules forward externally or move messages to Notes, Junk Email or RSS Subscriptions, Sent Items contains messages the user never wrote, the GAL entry or signature changed, repeated password resets or lockouts, and newly added external forwarding.
Step-by-step#
1. Contain the account#
Microsoft's first step is to disable the account for the duration of the investigation; if you can't, reset the password instead. Never send the new password by email, because the attacker may still be reading the mailbox. For synced accounts, reset in Active Directory and reset twice; delete and recreate any app passwords.
Connect-MgGraph -Scopes "User.ReadWrite.All","User.RevokeSessions.All"
$user = Get-MgUser -Search "UserPrincipalName:adele@contoso.com" -ConsistencyLevel Eventual
Update-MgUser -UserId $user.Id -AccountEnabled:$false
Revoke-MgUserSignInSession -UserId adele@contoso.comRevoking sessions invalidates refresh tokens, so an attacker holding a token is signed out rather than waiting for it to expire.
2. Remove the persistence#
Next, in Microsoft's order: review the user's registered MFA methods and remove anything you don't recognise; review applications the user consented to and revoke suspicious ones; remove admin roles the account shouldn't hold; then clear mailbox forwarding and rules.
Connect-ExchangeOnline
Get-Mailbox -Identity adele@contoso.com | Format-List ForwardingAddress, ForwardingSmtpAddress, DeliverToMailboxAndForward
Get-InboxRule -Mailbox adele@contoso.com -IncludeHidden |
Format-List Name, Enabled, RedirectTo, ForwardTo, ForwardAsAttachmentTo, DeleteMessage, MoveToFolder, Identity
# Clear admin-level forwarding and remove or disable the rule
Set-Mailbox -Identity adele@contoso.com -ForwardingAddress $null -ForwardingSmtpAddress $null -DeliverToMailboxAndForward $false
Remove-InboxRule -Mailbox adele@contoso.com -Identity "Suspicious Rule Name"
Disable-InboxRule -Mailbox adele@contoso.com -Identity "Keep for evidence"Attackers rarely stop at one mailbox, so sweep the tenant. The first block lists mailbox-level forwarding; the second lists every rule that forwards, redirects or deletes.
Get-Mailbox -ResultSize Unlimited |
Where-Object { $_.ForwardingSmtpAddress -or $_.ForwardingAddress } |
Select-Object DisplayName, ForwardingAddress, ForwardingSmtpAddress, DeliverToMailboxAndForward
$rules = foreach ($m in Get-Mailbox -ResultSize Unlimited) {
Get-InboxRule -Mailbox $m.UserPrincipalName -IncludeHidden -ErrorAction SilentlyContinue |
Where-Object { $_.ForwardTo -or $_.RedirectTo -or $_.ForwardAsAttachmentTo -or $_.DeleteMessage }
}
$rules | Select-Object MailboxOwnerId, Name, Enabled, ForwardTo, RedirectTo, DeleteMessage, MoveToFolder |
Export-Csv .\SuspiciousInboxRules.csv -NoTypeInformationRules with an action that starts an application or references an .exe, .zip or URL are a different, older persistence technique; Microsoft's archived Get-AllTenantRulesAndForms.ps1 script dumps rules and custom forms for every mailbox (connect to Exchange Online first and remove the obsolete connection lines it still contains).
3. Investigate what happened#
Start with Sign-in logs in the Microsoft Entra admin center for the user: look at IP address, location, time, client app and whether each attempt succeeded, and work back to the first unfamiliar success. If you have Microsoft Entra ID P2, the Risky users and Risky sign-ins reports under Identity Protection often already flag the account. Then search the audit log, either in the Microsoft Purview portal (Audit solution) or in PowerShell, starting just before the first suspicious sign-in and without filtering activities at first:
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-14) -EndDate (Get-Date) -UserIds adele@contoso.com `
-SessionCommand ReturnLargeSet -ResultSize 5000 | Export-Csv .\adele-audit.csv -NoTypeInformation
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-30) -EndDate (Get-Date) `
-Operations New-InboxRule,Set-InboxRule,Remove-InboxRule,Set-Mailbox -ResultSize 5000 -SessionCommand ReturnLargeSetThe AuditData column holds the client IP and the rule parameters, so you can match a rule to the sign-in that created it. Audit records normally appear 60 to 90 minutes after the event; Audit (Standard) keeps 180 days, and E5 licences (Audit Premium) keep a year and add MailItemsAccessed, which tells you what the attacker read. Finish with a message trace from the Defender portal and a look at Sent Items and Deleted Items to see what went out, and, if you have Defender for Office 365 Plan 2, use Threat Explorer to find the phishing message that started it and any copies in other mailboxes.
4. Recover and communicate#
When the investigation is complete, reset the password again, re-enable the account and register MFA with the user present. If the mailbox was used for spam it will be on the Restricted entities page in the Defender portal (Email & collaboration › Review); remove it there to restore sending. Brief the user by phone or Teams, not by email, tell them what you removed, and warn the people who received mail from the account during the window so they treat it as phishing.
Verify#
- Re-run the forwarding and inbox rule queries for the mailbox and the tenant; they should return nothing unexpected.
- Sign-in logs show only the user's known devices and locations after the reset, and no further risk detections.
- A fresh audit search for
New-InboxRuleandSet-Mailboxreturns only your own remediation entries. - A test message from the mailbox to an external address is delivered, confirming the sending block is lifted.
Tips & gotchas#
- Require MFA for everyone and phishing-resistant MFA for admins; the rules and forms persistence techniques only work after credentials are stolen.
- Set Automatic forwarding rules to Off in the default outbound spam policy and grant exceptions by group, so a forwarding rule fails instead of leaking.
- Turn on the default alert policies that matter here under Email & collaboration › Policies & rules › Alert policy: Creation of forwarding/redirect rule, Suspicious email forwarding activity and User restricted from sending email, and send them to a monitored mailbox.
- Confirm auditing is on before you need it:
Get-AdminAuditLogConfig | Format-List UnifiedAuditLogIngestionEnabledin Exchange Online PowerShell should return True. - Keep Outlook current; up-to-date clients block the "start application" rule action by default.
- Export every query result as you go. The CSVs are your evidence if the incident becomes a legal or insurance matter.