Public/Get-NSPM365InboxRuleReport.ps1
|
function Get-NSPM365InboxRuleReport { <# .SYNOPSIS Lists the inbox rules (hidden ones included) and mailbox forwarding of Exchange Online mailboxes, flagging the ones that look like attacker tradecraft. .DESCRIPTION Read-only. Needs an Exchange Online session (Connect-ExchangeOnline, from the ExchangeOnlineManagement module) with rights to read mailboxes and inbox rules. One row per rule (Kind 'InboxRule') and one per mailbox that forwards at the mailbox level (Kind 'MailboxForwarding'). The flags are Get-NSPM365InboxRuleAudit's: Forwards, ForwardsInternal, Deletes, MarksRead, MovesTo:<folder>, OddName, TargetsKeywords. Internal domains default to the tenant's accepted domains. Rules are read with -IncludeHidden. The built-in 'Junk E-mail Rule' is skipped. A rule hidden by editing its MAPI properties may still not show here: when a rule shows up in the audit log (Get-NSPM365InboxRuleAudit) but not here, look at the mailbox with MFCMAPI. To remove or disable what you've confirmed, pipe the rows to Remove-NSPM365InboxRule. .PARAMETER Mailbox Mailboxes to check. Defaults to every user and shared mailbox. .PARAMETER InternalDomain Domains that count as internal for forwarding. Defaults to Get-AcceptedDomain. .PARAMETER SuspiciousOnly Only the flagged rows. .EXAMPLE Connect-ExchangeOnline; Get-NSPM365InboxRuleReport -SuspiciousOnly | Format-Table Mailbox, RuleName, Flags, ForwardTo .EXAMPLE Get-NSPM365InboxRuleReport -Mailbox jdoe@contoso.com, ap@contoso.com | Export-Csv .\rules.csv -NoTypeInformation #> [CmdletBinding()] param( [string[]]$Mailbox, [string[]]$InternalDomain, [switch]$SuspiciousOnly ) if (-not (Get-Command Get-InboxRule -ErrorAction SilentlyContinue)) { throw 'Get-InboxRule is not available: run Connect-ExchangeOnline (ExchangeOnlineManagement module) first.' } if (-not $PSBoundParameters.ContainsKey('InternalDomain')) { $InternalDomain = @(Get-AcceptedDomain | ForEach-Object { [string]$_.DomainName }) } $mailboxes = if ($Mailbox) { @($Mailbox | ForEach-Object { Get-Mailbox -Identity $_ }) } else { @(Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox, SharedMailbox) } $emit = { param($Row) if (-not $SuspiciousOnly -or $Row.Suspicious) { $Row } } $index = 0 foreach ($mb in $mailboxes) { $index++ $address = [string]$mb.PrimarySmtpAddress Write-Progress -Activity 'Reading inbox rules' -Status $address -PercentComplete (100 * $index / [math]::Max(1, $mailboxes.Count)) $forwarding = @(ConvertTo-NSPRuleTarget @([string]$mb.ForwardingSmtpAddress, [string]$mb.ForwardingAddress)) if ($forwarding.Count) { $flags = @(Get-NSPInboxRuleFlag -ForwardTarget $forwarding -InternalDomain $InternalDomain) & $emit ([pscustomobject]@{ Mailbox = $address Kind = 'MailboxForwarding' RuleName = '' Enabled = $true MoveToFolder = '' ForwardTo = $forwarding -join '; ' DeleteMessage = -not [bool]$mb.DeliverToMailboxAndForward MarkAsRead = $false Conditions = '' Flags = $flags -join '; ' Suspicious = [bool](@($flags | Where-Object { $_ -ne 'ForwardsInternal' }).Count) Description = "DeliverToMailboxAndForward=$($mb.DeliverToMailboxAndForward)" RuleIdentity = '' }) } try { $rules = @(Get-InboxRule -Mailbox $address -IncludeHidden -ErrorAction Stop) } catch { Write-Warning "Could not read inbox rules of $address`: $($_.Exception.Message)" continue } foreach ($rule in $rules) { if ([string]$rule.Name -eq 'Junk E-mail Rule') { continue } $targets = @(ConvertTo-NSPRuleTarget @($rule.ForwardTo, $rule.RedirectTo, $rule.ForwardAsAttachmentTo)) $conditions = @(foreach ($n in 'From', 'FromAddressContainsWords', 'SubjectContainsWords', 'SubjectOrBodyContainsWords', 'BodyContainsWords', 'SentTo', 'HeaderContainsWords') { $v = @($rule.$n | Where-Object { $_ }) if ($v.Count) { "$n=$($v -join ',')" } }) -join '; ' $delete = [bool]$rule.DeleteMessage -or [bool]$rule.SoftDeleteMessage $flags = @(Get-NSPInboxRuleFlag -Name ([string]$rule.Name) -MoveToFolder ([string]$rule.MoveToFolder) -ForwardTarget $targets -Delete $delete -MarkAsRead ([bool]$rule.MarkAsRead) -Conditions $conditions -InternalDomain $InternalDomain) & $emit ([pscustomobject]@{ Mailbox = $address Kind = 'InboxRule' RuleName = [string]$rule.Name Enabled = [bool]$rule.Enabled MoveToFolder = [string]$rule.MoveToFolder ForwardTo = $targets -join '; ' DeleteMessage = $delete MarkAsRead = [bool]$rule.MarkAsRead Conditions = $conditions Flags = $flags -join '; ' Suspicious = [bool](@($flags | Where-Object { $_ -ne 'ForwardsInternal' }).Count) Description = ([string]$rule.Description).Trim() RuleIdentity = [string]$rule.Identity }) } } Write-Progress -Activity 'Reading inbox rules' -Completed } |