Public/Get-MailboxForwardingRules.ps1
|
function Get-MailboxForwardingRules { <# .SYNOPSIS Audits all mailbox forwarding rules for potential data exfiltration. .DESCRIPTION Checks three types of mail forwarding: 1. ForwardingAddress (set on the mailbox directly) 2. ForwardingSMTPAddress (set on the mailbox directly) 3. Inbox rules that forward or redirect to external addresses External forwarding is a common attack vector after account compromise and is a finding in most security frameworks. .PARAMETER ExternalOnly Only report rules forwarding to external (non-tenant) domains. .EXAMPLE Get-MailboxForwardingRules Returns all forwarding rules across all mailboxes. .EXAMPLE Get-MailboxForwardingRules -ExternalOnly Returns only rules forwarding to external domains. .NOTES Requires: ExchangeOnlineManagement module, Exchange Admin role. #> [CmdletBinding()] param( [switch]$ExternalOnly ) $results = [System.Collections.Generic.List[PSCustomObject]]::new() # Get accepted domains for internal/external classification $acceptedDomains = (Get-AcceptedDomain).DomainName # Check 1 & 2: Mailbox-level forwarding Write-Verbose "Checking mailbox-level forwarding..." $mailboxes = Get-Mailbox -ResultSize Unlimited | Where-Object { $_.ForwardingAddress -or $_.ForwardingSMTPAddress } foreach ($mbx in $mailboxes) { $target = if ($mbx.ForwardingSMTPAddress) { $mbx.ForwardingSMTPAddress.ToString() } elseif ($mbx.ForwardingAddress) { $mbx.ForwardingAddress.ToString() } else { '' } $isExternal = $true foreach ($domain in $acceptedDomains) { if ($target -like "*@$domain*" -or $target -like "*$domain*") { $isExternal = $false break } } if (-not $ExternalOnly -or $isExternal) { $results.Add([PSCustomObject]@{ Mailbox = $mbx.UserPrincipalName DisplayName = $mbx.DisplayName RuleType = 'MailboxForwarding' ForwardTo = $target DeliverToMailbox = $mbx.DeliverToMailboxAndForward IsExternal = $isExternal RuleName = 'N/A (Mailbox Setting)' Enabled = $true }) } } # Check 3: Inbox rules with forwarding Write-Verbose "Checking inbox rules (this may take a while)..." $allMailboxes = Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox $total = $allMailboxes.Count $current = 0 foreach ($mbx in $allMailboxes) { $current++ if ($current % 50 -eq 0) { Write-Progress -Activity "Checking inbox rules" -Status "$($mbx.Alias) ($current/$total)" -PercentComplete (($current / $total) * 100) } try { $rules = Get-InboxRule -Mailbox $mbx.UserPrincipalName -ErrorAction SilentlyContinue | Where-Object { $_.ForwardTo -or $_.ForwardAsAttachmentTo -or $_.RedirectTo } foreach ($rule in $rules) { $targets = @() if ($rule.ForwardTo) { $targets += $rule.ForwardTo } if ($rule.ForwardAsAttachmentTo) { $targets += $rule.ForwardAsAttachmentTo } if ($rule.RedirectTo) { $targets += $rule.RedirectTo } foreach ($target in $targets) { $targetStr = $target.ToString() $isExternal = $true foreach ($domain in $acceptedDomains) { if ($targetStr -like "*@$domain*" -or $targetStr -like "*$domain*") { $isExternal = $false break } } if (-not $ExternalOnly -or $isExternal) { $results.Add([PSCustomObject]@{ Mailbox = $mbx.UserPrincipalName DisplayName = $mbx.DisplayName RuleType = 'InboxRule' ForwardTo = $targetStr DeliverToMailbox = $null IsExternal = $isExternal RuleName = $rule.Name Enabled = $rule.Enabled }) } } } } catch { Write-Verbose "Could not check rules for $($mbx.UserPrincipalName): $_" } } Write-Progress -Activity "Checking inbox rules" -Completed $results } |