๐ŸŽ New User? Get 20% off your first purchase with code NEWUSER20 ยท โšก Instant download ยท ๐Ÿ”’ Secure checkout Register Now โ†’
Menu

Categories

Mailbox Forwarding Audit with Microsoft Graph (2026)

Mailbox Forwarding Audit with Microsoft Graph (2026)
Microsoft Graph mailbox forwarding audit - Dargslan 2026

The first thing a mailbox-takeover actor does is set up forwarding to an outside address, then delete the rule trace. Microsoft enforces "external forwarding blocked" by default in newer tenants, but inbox rules with redirect / forward to an external recipient still slip through, and the audit pass is essential.

Why forwarding rules matter

BEC playbooks share three properties: silence (the mailbox owner does not see incoming finance email), persistence (the rule survives a password reset), and exfiltration (a copy goes to the attacker). All three are achieved with one well-crafted inbox rule.

Mailbox-level forwarding

This is the property set on the mailbox itself, not in a rule:

Connect-MgGraph -Scopes 'MailboxSettings.Read','User.Read.All'

Get-MgUser -All -Property Id,UserPrincipalName | ForEach-Object {
    $s = Get-MgUserMailboxSetting -UserId $_.Id -ErrorAction SilentlyContinue
    if ($s.AutomaticRepliesSetting -or $s.DelegateMeetingMessageDeliveryOptions) { }
    if ($s.UserPurpose) { }                               # placeholder for future
}

For the equivalent of ForwardingSmtpAddress, the Graph SDK still relies on Exchange Online cmdlets โ€” Get-Mailbox | Select ForwardingSmtpAddress, ForwardingAddress, DeliverToMailboxAndForward. Use both modules together until Graph parity lands.

Inbox rules

Get-MgUser -All -Property Id,UserPrincipalName | ForEach-Object {
    $u = $_
    Get-MgUserMessageRule -UserId $u.Id -ErrorAction SilentlyContinue |
        Where-Object { $_.Actions.ForwardTo -or $_.Actions.RedirectTo -or $_.Actions.ForwardAsAttachmentTo } |
        ForEach-Object {
            [pscustomobject]@{
                UPN     = $u.UserPrincipalName
                Rule    = $_.DisplayName
                Forward = ($_.Actions.ForwardTo + $_.Actions.RedirectTo + $_.Actions.ForwardAsAttachmentTo).EmailAddress.Address -join ','
                Enabled = $_.IsEnabled
            }
        }
}

Detect external recipients

$internalDomains = @('contoso.com','contoso.onmicrosoft.com')
$above | Where-Object {
    $hits = $_.Forward -split ','
    ($hits | Where-Object { $d = ($_ -split '@')[1]; $internalDomains -notcontains $d }).Count -gt 0
}

Any rule that forwards to an address outside your accepted domains is the actionable list.

Remediate safely

  1. Snapshot the rule (export to JSON for the IR record).
  2. Disable the rule (do not delete yet).
  3. Force a password reset and revoke refresh tokens.
  4. Audit sign-in logs for the same user โ€” country, device, IP.
  5. Open an IR ticket if anything looks off.

Continuous detection

The Microsoft Defender for Office 365 alert "Suspicious inbox forwarding rule" covers some cases. Pair with a weekly cron of the script above (logged to a SIEM) so nothing slips between alerts.

Audit checklist

  1. Tenant external forwarding policy = Disabled or Restricted (1 pt)
  2. Zero mailboxes with ForwardingSmtpAddress to external (1 pt)
  3. Zero inbox rules forwarding to external (1 pt)
  4. Defender alert "Suspicious inbox forwarding" enabled (1 pt)
  5. Weekly cron of audit script with SIEM ingest (1 pt)

Detection across all forwarding mechanisms

Attackers create forwards in three places, and a complete audit checks all three: mailbox-level ForwardingSmtpAddress, inbox rules, and transport rules. A script that only checks one is the security equivalent of locking the front door and leaving the patio open.

Mailbox-level forwarding

The classic exfiltration channel. ForwardingSmtpAddress sends a copy of every inbound message to an external address. Pull every mailbox and flag any non-empty value that points outside your accepted domains.

$accepted = (Get-AcceptedDomain).DomainName  # via ExchangeOnlineManagement
$mbx = Get-EXOMailbox -ResultSize Unlimited `
            -Properties ForwardingSmtpAddress, DeliverToMailboxAndForward |
       Where-Object ForwardingSmtpAddress

$external = $mbx | Where-Object {
    $domain = ($_.ForwardingSmtpAddress -split '@')[-1].TrimEnd(';')
    $accepted -notcontains $domain
}

$external | Select DisplayName, PrimarySmtpAddress, ForwardingSmtpAddress |
            Export-Csv -Path 'fwd-mailbox-external.csv' -NoTypeInformation

Inbox rules per user via Graph

Inbox rules are user-owned and survive password resets. The Graph endpoint users/{id}/mailFolders/inbox/messageRules exposes them โ€” iterate every user, every rule, every action.

$users = Get-MgUser -All -Property Id, UserPrincipalName

$flagged = foreach ($u in $users) {
  try {
    $rules = Get-MgUserMailFolderMessageRule -UserId $u.Id -MailFolderId Inbox -All
  } catch { continue }  # mailbox not provisioned, skip

  foreach ($r in $rules | Where-Object IsEnabled) {
    $a = $r.Actions
    if ($a.ForwardTo -or $a.RedirectTo -or $a.ForwardAsAttachmentTo -or $a.MoveToFolder -eq 'deleteditems') {
      [pscustomobject]@{
        User       = $u.UserPrincipalName
        Rule       = $r.DisplayName
        Forward    = ($a.ForwardTo + $a.RedirectTo + $a.ForwardAsAttachmentTo |
                       ForEach-Object { $_.EmailAddress.Address }) -join '; '
        Actions    = ($r.Actions.PSObject.Properties | Where-Object Value).Name -join ','
      }
    }
  }
}

Transport rules: tenant-wide forwarding

A malicious admin can create a transport rule that forwards every message from a specific user, mailbox or pattern. Pull them with Get-TransportRule and verify every RedirectMessageTo and BlindCopyTo action.

Common pitfalls

  • Trusting RemoteDomain AutoForwardEnabled. Tenant-wide block helps but does not stop inbox rules using redirect. Check rules explicitly.
  • Forgetting shared mailboxes. Shared mailboxes have inbox rules too and are a frequent attacker target precisely because they are unmonitored.
  • Allowing partner domains as exceptions without review. Every exception ages โ€” review the partner-domain allow-list quarterly.
  • No alerting on rule creation. Audit-after-the-fact catches forwarders eventually. An alert on New-InboxRule in the Unified Audit Log catches them in minutes.
  • Removing rules without preserving evidence. Export the rule definition before deletion. The body of the rule is part of the incident record.

Incident-response playbook when an external forward is found

Finding an unauthorised external forwarding rule is a confirmed compromise indicator until proven otherwise. The 60-minute response below contains the blast radius, preserves evidence, and starts the formal investigation.

  1. Minute 0โ€“5 โ€” preserve. Export the rule body before doing anything else: Get-InboxRule -Mailbox $u -Identity $r | Export-Clixml ir-evidence-$(Get-Date -f yyyyMMddHHmm).xml. Without the original definition you lose the indicator.
  2. Minute 5โ€“15 โ€” contain. Disable the rule (do not delete yet). Block the destination domain at the transport layer. Reset the user's credential and revoke all existing sessions: Revoke-MgUserSignInSession -UserId $u.
  3. Minute 15โ€“30 โ€” scope. Pull the user's sign-in log for the last 30 days. Look for impossible-travel events, sign-ins from new IPs, and any successful authentication after the rule's CreatedDateTime. Any anomaly expands the investigation to that endpoint.
  4. Minute 30โ€“45 โ€” broaden the audit. Re-run the forwarding audit across the entire tenant filtered to rules created within ยฑ48 hours of the indicator timestamp. Attackers create rules in batches; the first one is rarely the only one.
  5. Minute 45โ€“60 โ€” communicate. Notify the user (without details if you suspect insider). Notify their manager. Open the formal IR ticket with the evidence file attached. Schedule the 24-hour follow-up review.

The 24-hour review checks: did the destination domain receive any messages before containment? Did the user create other rules elsewhere (Power Automate, Teams)? Are there sign-in events on adjacent accounts from the same source IP? Each yes expands the investigation; each no shrinks it.

Document the whole sequence in a runbook stored next to the audit script. The next analyst should be able to execute it without rediscovering the steps under pressure.

Continuous monitoring: catching the next forward in minutes, not weeks

A nightly audit catches the forwarding rule that an attacker created the previous day. A real-time monitoring pipeline catches it within minutes โ€” long before the attacker has read enough mail to do meaningful damage. Building the pipeline takes a single afternoon and pays for itself the first time it fires.

The foundation is the Unified Audit Log. Every inbox-rule creation, mailbox-forwarding configuration change, and transport-rule edit writes a structured event with the actor, target, parameters and timestamp. Stream the audit log into your SIEM and write three rules: alert on any new inbox rule with a forwarding action; alert on any change to mailbox ForwardingSmtpAddress; alert on any new transport rule with RedirectMessageTo or BlindCopyTo.

Tune for noise. Most legitimate inbox rules โ€” file an email, mark as read โ€” are not forwarding rules. The first week of monitoring will surface a handful of legitimate forwards (an out-of-office alternate, an executive's assistant). Whitelist them by user and rule name, and the alert volume drops to near-zero. From then on, every fire is worth investigating.

Pair the alerts with a weekly trend report: count of forwarding rules created, count by user, count of external destinations. Trends matter: a single rule is a finding; ten rules from ten users in one day is a campaign. Both deserve different responses, and the chart makes the difference visible.

For tenants serious about exfiltration prevention, layer in Defender for Office 365 alerting. The Suspicious Email Forwarding alert detects rules that match attacker patterns (forwarding to recently-created free-mail accounts, forwarding messages containing "invoice" or "wire") and fires before the rule has done meaningful damage. Combined with the audit-log monitoring, the two cover both novel and pattern-matched threats.

Document the response procedure once and route every alert through the same playbook. Consistency makes the response faster and the evidence collection cleaner; ad-hoc responses lose detail every time and slow the post-incident review.

FAQ

Why use Graph and not just Exchange Online cmdlets?

Graph is faster at scale and supports the same app-only auth pattern as the rest of your audit. Use EXO where Graph parity is missing โ€” but converge on Graph for the rest.

Does the audit catch hidden rules?

It catches every messageRule. Truly hidden rules require the EXO Get-InboxRule -IncludeHidden cmdlet โ€” combine both for full coverage.

What about Outlook desktop client rules?

Server-side rules (the kind we audit) sync to all clients. Client-only rules do not exfiltrate to external recipients โ€” they cannot run without the client open.

How do I prevent users from creating new external forwards?

Set the outbound spam filter to disable automatic forwarding. Combined with the audit, this is the standard belt-and-braces configuration.

Will this catch forwarding via Power Automate?

No โ€” Power Automate uses its own connectors and is invisible to mailbox-level audit. Audit Power Automate flows separately via the Power Platform admin centre.

What permission does the audit script need?

MailboxSettings.Read for the Graph calls and the Exchange role View-Only Recipients + View-Only Configuration for the EXO cmdlets. Application permissions, not delegated.

Can a user see that I ran this audit?

No. The audit is read-only and does not generate user-visible artifacts. The Unified Audit Log records that an admin ran it, which is the appropriate paper trail.

Related Dargslan resources

Share this article:
Dargslan Editorial Team (Dargslan)
About the Author

Dargslan Editorial Team (Dargslan)

Collective of Software Developers, System Administrators, DevOps Engineers, and IT Authors

Dargslan is an independent technology publishing collective formed by experienced software developers, system administrators, and IT specialists.

The Dargslan editorial team works collaboratively to create practical, hands-on technology books focused on real-world use cases. Each publication is developed, reviewed, and...

Programming Languages Linux Administration Web Development Cybersecurity Networking

Stay Updated

Subscribe to our newsletter for the latest tutorials, tips, and exclusive offers.