Exceedio.Exchange.psm1
function Connect-ExceedioExchangeOnline { <# .SYNOPSIS Connects to Exchange Online. .PARAMETER UserPrincipalName Username in username@contoso.com format. .PARAMETER DelegatedOrganization Domain name in contoso.com format. .EXAMPLE Connect-ExceedioExchangeOnline -UserPrincipalName alice@contoso.com -DelegatedOrganization fabrikam.com .NOTES This is a helper function for Connect-ExchangeOnline which is part of the Exchange Online PowerShell V2 module. See https://docs.microsoft.com/en-us/powershell/module/exchange/connect-exchangeonline?view=exchange-ps for details about that function. #> [CmdletBinding()] param ( [Parameter()] [String] $UserPrincipalName, [Parameter()] [String] $DelegatedOrganization ) $module = Get-InstalledModule -Name ExchangeOnlineManagement if ($null -eq $module -or $module.Version -lt '2.0.5') { Write-Warning "Module ExchangeOnlineManagement doesn't exist or is out of date; Install-Module ExchangeOnlineManagement -AllowClobber -Force" return } Connect-ExchangeOnline -UserPrincipalName $UserPrincipalName -DelegatedOrganization $DelegatedOrganization } function Connect-ExceedioIPPSSession { <# .SYNOPSIS Use the Connect-ExceedioIPPSSession cmdlet in the Exchange Online PowerShell V2 module to connect to Security & Compliance Center PowerShell or standalone Exchange Online Protection PowerShell using modern authentication. The cmdlet works for MFA or non-MFA enabled accounts. .PARAMETER UserPrincipalName Username in username@contoso.com format. .PARAMETER DelegatedOrganization Domain name in contoso.com format. .EXAMPLE Connect-ExceedioIPPSSession -UserPrincipalName alice@contoso.com -DelegatedOrganization fabrikam.com .NOTES This is a helper function for Connect-IPPSSession which is part of the Exchange Online PowerShell V2 module. See https://docs.microsoft.com/en-us/powershell/module/exchange/connect-ippssession?view=exchange-ps for details about that function. #> [CmdletBinding()] param ( [Parameter()] [String] $UserPrincipalName, [Parameter()] [String] $DelegatedOrganization ) $module = Get-InstalledModule -Name ExchangeOnlineManagement if ($null -eq $module -or $module.Version -lt '2.0.5') { Write-Warning "Module ExchangeOnlineManagement doesn't exist or is out of date; Install-Module ExchangeOnlineManagement -AllowClobber -Force" return } Connect-IPPSSession -UserPrincipalName $UserPrincipalName -DelegatedOrganization $DelegatedOrganization } function Get-ExceedioSafeLinksPolicy { <# .SYNOPSIS Retrieves and audits the default Safe Links policy and lists the recipient(s) and/or recipient domain(s) that the policy applies to. .EXAMPLE Get-ExceedioSafeLinksPolicy .NOTES Use Get-SafeLinksPolicy from the Exchange Online PowerShell V2 module to see all details for a specific Safe Links policy. #> $policy = Get-SafeLinksPolicy -Identity Default if (-not $policy) { Write-Warning "No Safe Links policy named 'Default' exists" return } $rule = $policy | Get-SafeLinksRule Write-Output ("[*] Name is 'Default' : {0}" -f ($policy.Name -eq 'Default' ? "OK" : "ERR")) Write-Output ("[*] Policy enabled : {0}" -f ($policy.IsEnabled ? "OK" : "ERR")) Write-Output ("[*] Email links are scanned : {0}" -f ($policy.ScanUrls ? "OK" : "ERR")) Write-Output ("[*] Email scanned before delivery : {0}" -f ($policy.DeliverMessageAfterScan ? "OK" : "ERR")) Write-Output ("[*] Teams links are scanned : {0}" -f ($policy.EnableSafeLinksForTeams ? "OK" : "ERR")) Write-Output ("[*] Click through is disabled : {0}" -f ($policy.DoNotAllowClickThrough ? "OK" : "ERR")) Write-Output ("[*] Clicks are tracked : {0}" -f (-not $policy.DoNotTrackUserClicks ? "OK" : "ERR")) Write-Output ("[*] Internal senders excluded : {0}" -f (-not $policy.EnableForInternalSenders ? "OK" : "ERR")) Write-Output ("[*] No special branding : {0}" -f (-not $policy.EnableOrganizationBranding ? "OK" : "ERR")) Write-Output ("[*] Recipient address(es) : {0}" -f $rule.SentTo) Write-Output ("[*] Recipient domain(s) : {0}" -f $rule.RecipientDomainIs) } function New-ExceedioSafeLinksPolicy { <# .SYNOPSIS Creates a standard Safe Links policy and applies it to recipient(s) or recipient domain(s). .PARAMETER Name The name of the policy. Defaults to 'Default' .PARAMETER Users Comma-separated list of recipients that the policy applies to. .PARAMETER Users Comma-separated list of domain names that the policy applies to. .EXAMPLE New-ExceedioSafeLinksPolicy -Users pilotuser1@fabrikam.com,pilotuser2@fabrikam.com .EXAMPLE New-ExceedioSafeLinksPolicy -Domains fabrikam.com .NOTES Running this after the first time will overwrite the existing policy with the same name. Normally you would run this first to set up a list of pilot users and then run it again at the end of the pilot to apply to the entire domain. #> [CmdletBinding()] param ( [Parameter()] [String] $Name = 'Default', [Parameter()] [String[]] $Users, [Parameter()] [String[]] $Domains ) if (-not $Users -and -not $Domains) { Write-Warning "You must specify either -Users or -Domains; both cannot be empty" return } if (Get-SafeLinksPolicy -Identity $Name -ErrorAction SilentlyContinue) { Write-Warning "A policy named '$Name' exists" Write-Output "Do you want to overwrite the existing policy '$Name' with a new policy?" Write-Host '[Y] Yes [N] No (Default is "N"): ' -NoNewline $answer = $Host.UI.RawUI.ReadKey() if ($answer.Character -ine 'Y') { return } else { Write-Output "" Write-Output "Removing existing policy..." Remove-SafeLinksRule -Identity "$Name" -Confirm:$false Remove-SafeLinksPolicy -Identity "$Name" -Confirm:$false } } $policy = New-SafeLinksPolicy ` -Name "$Name" ` -DeliverMessageAfterScan $true ` -AllowClickThrough $false ` -TrackClicks $true ` -EnableForInternalSenders $false ` -EnableOrganizationBranding $false ` -EnableSafeLinksForTeams $true ` -IsEnabled $true ` -ScanUrls $true if ($policy -and $Users) { New-SafeLinksRule ` -Name "$Name" ` -SafeLinksPolicy "$Name" ` -SentTo $Users ` | Out-Null } elseif ($policy -and $Domains) { New-SafeLinksRule ` -Name "$Name" ` -SafeLinksPolicy "$Name" ` -RecipientDomainIs $Domains ` | Out-Null } Write-Output "Policy '$Name' successfully created; Use Get-ExceedioSafeLinksPolicy to audit" } function Get-ExceedioSafeAttachmentPolicy { <# .SYNOPSIS Retrieves and audits the default Safe Attachment policy and lists the recipient(s) and/or recipient domain(s) that the policy applies to. .EXAMPLE Get-ExceedioSafeAttachmentPolicy .NOTES Use Get-SafeAttachmentPolicy from the Exchange Online PowerShell V2 module to see all details for a specific Safe Attachment policy. #> $policy = Get-SafeAttachmentPolicy -Identity Default if (-not $policy) { Write-Warning "No Safe Attachment policy named 'Default' exists" return } $rule = $policy | Get-SafeAttachmentRule Write-Output ("[*] Name is 'Default' : {0}" -f ($policy.Name -eq 'Default' ? "OK" : "ERR")) Write-Output ("[*] Policy enabled : {0}" -f ($policy.Enable ? "OK" : "ERR")) Write-Output ("[*] Action is dynamic delivery : {0}" -f ($policy.Action -eq 'DynamicDelivery' ? "OK" : "ERR")) Write-Output ("[*] Scan timeout is 30 : {0}" -f ($policy.ScanTimeout -eq 30 ? "OK" : "ERR")) Write-Output ("[*] Operation mode is Delay : {0}" -f ($policy.OperationMode -eq 'Delay' ? "OK" : "ERR")) Write-Output ("[*] Action on error is true : {0}" -f ($policy.ActionOnError ? "OK" : "ERR")) Write-Output ("[*] Redirect is false : {0}" -f (-not $policy.Redirect ? "OK" : "ERR")) Write-Output ("[*] Recipient address(es) : {0}" -f $rule.SentTo) Write-Output ("[*] Recipient domain(s) : {0}" -f $rule.RecipientDomainIs) } function New-ExceedioSafeAttachmentPolicy { <# .SYNOPSIS Creates a standard Safe Attachment policy and applies it to recipient(s) or recipient domain(s). .PARAMETER Name The name of the policy. Defaults to 'Default' .PARAMETER PrimaryDomain The primary domain name that the policy applies to (e.g. contoso.com). This is used when determining where to redirect mail that was found to contain malicious attachments and is required. .PARAMETER Users Comma-separated list of recipients that the policy applies to. .PARAMETER Domains Comma-separated list of domain names that the policy applies to. .EXAMPLE New-ExceedioSafeAttachmentPolicy -PrimaryDomain fabrikam.com -Users pilotuser1@fabrikam.com,pilotuser2@fabrikam.com .EXAMPLE New-ExceedioSafeAttachmentPolicy -PrimaryDomain fabrikam.com -Domains fabrikam.com .NOTES Running this after the first time will overwrite the existing policy with the same name. Normally you would run this first to set up a list of pilot users and then run it again at the end of the pilot to apply to the entire domain. #> [CmdletBinding()] param ( [Parameter()] [String] $Name = 'Default', [Parameter(Mandatory=$true)] [String] $PrimaryDomain, [Parameter()] [String[]] $Users, [Parameter()] [String[]] $Domains ) if (-not $Users -and -not $Domains) { Write-Warning "You must specify either -Users or -Domains; both cannot be empty" return } if (Get-SafeAttachmentPolicy -Identity $Name -ErrorAction SilentlyContinue) { Write-Warning "A policy named '$Name' exists" Write-Output "Do you want to overwrite the existing policy '$Name' with a new policy?" Write-Host '[Y] Yes [N] No (Default is "N"): ' -NoNewline $answer = $Host.UI.RawUI.ReadKey() if ($answer.Character -ine 'Y') { return } else { Write-Output "" Write-Output "Removing existing policy..." Remove-SafeAttachmentRule -Identity "$Name" -Confirm:$false Remove-SafeAttachmentPolicy -Identity "$Name" -Confirm:$false } } $redirectAddress = "blockedemail@$PrimaryDomain" $redirectMailbox = Get-EXOMailbox $redirectAddress -ErrorAction SilentlyContinue if (-not $redirectMailbox) { Write-Output "Creating shared mailbox $redirectAddress..." New-Mailbox -Name "Blocked Email" -DisplayName "Blocked Email" -Alias blockedemail -Shared } else { Write-Output "Mailbox $redirectAddress already exists; skipping creation of shared mailbox to hold blocked email" } $policy = New-SafeAttachmentPolicy ` -Name "$Name" ` -Enable $true ` -Action Block ` -Redirect $true ` -RedirectAddress $redirectAddress ` -ActionOnError $true if ($policy -and $Users) { New-SafeAttachmentRule ` -Name "$Name" ` -SafeAttachmentPolicy "$Name" ` -SentTo $Users ` | Out-Null } elseif ($policy -and $Domains) { New-SafeAttachmentRule ` -Name "$Name" ` -SafeAttachmentPolicy "$Name" ` -RecipientDomainIs $Domains ` | Out-Null } Write-Output "Policy '$Name' successfully created; Use Get-ExceedioSafeAttachmentPolicy to audit" } function New-ExceedioPhishSimOverridePolicy { <# .SYNOPSIS Creates a standard phish sim override policy to override phishing simulations. The parameter defaults are appropriate for allowing phishing simulations from KnowBe4. .PARAMETER PolicyName The name of the policy. Defaults to 'PhishSimOverridePolicy' and should not be changed. .PARAMETER RuleName The name of the policy. Defaults to 'PhishSimOverrideRule' and should not be changed. .PARAMETER SenderDomainIs Comma-separated list of domain names that the policy applies to. .PARAMETER SenderIpRanges Comma-separated list of IP addresses or IP address CIDR blocks that the policy applies to. .EXAMPLE New-ExceedioPhishSimOverridePolicy .EXAMPLE New-ExceedioPhishSimOverridePolicy -SenderDomainIs somedomain.com -SenderIpRanges 8.8.8.8,8.8.4.4 .NOTES The documentation for New-PhishSimOverridePolicy and New-PhishSimOverrideRule state that the Name parameter for both have no effect and the names will always be PhishSimOverridePolicy and PhishSimOverrideRule respectively. The parameters are included for completeness but should not be overridden. See https://docs.microsoft.com/en-us/microsoft-365/security/office-365-security/configure-advanced-delivery for more information. #> [CmdletBinding()] param ( [Parameter()] [String] $PolicyName = 'PhishSimOverridePolicy', [Parameter()] [String] $RuleName = 'PhishSimOverrideRule', [Parameter()] [String[]] $SenderDomainIs = 'psm.knowbe4.com', [Parameter()] [String[]] $SenderIpRanges = @('147.160.167.0/26','23.21.109.197','23.21.109.212') ) New-PhishSimOverridePolicy ` -Name $PolicyName ` -Enabled $true New-PhishSimOverrideRule ` -Name $RuleName ` -Policy $PolicyName ` -SenderDomainIs $SenderDomainIs ` -SenderIpRanges $SenderIpRanges } function Test-ExceedioMailboxesForForwarding { <# .SYNOPSIS Tests all mailboxes in an organization for both mailbox-level forwarding and Outlook Inbox rules that may be used for forwarding. .EXAMPLE Test-ExceedioMailboxesForForwarding .NOTES Scammers often set up forwarding rules if they are able to gain access to your email account and then watch for important emails in an effort to improve their chances at a Business Email Compromise attack. Use this cmdlet to investigate suspicious forwarding in an organization. #> $mailboxes = Get-Mailbox -ResultSize Unlimited foreach ($mailbox in $mailboxes) { Write-Host "[*] Checking mailbox for $($mailbox.DisplayName)" if ($mailbox.DeliverToMailboxAndForward) { Write-Host " [!] Mailbox forwarding is enabled for $($mailbox.DisplayName)" -ForegroundColor Red } else { Write-Host " [*] Mailbox forwarding is not enabled for $($mailbox.DisplayName)" -ForegroundColor Green } $rules = @(Get-InboxRule -Mailbox $mailbox.UserPrincipalName) if ($rules.Count -gt 0) { Write-Host " [*] Checking $($rules.Count) Outlook inbox rule(s) for $($mailbox.DisplayName)" foreach ($rule in $rules) { if (($rule.CopyToFolder -or $rule.MoveToFolder) -and ($rule.CopyToFolder -match 'RSS' -or $rule.MoveToFolder -match 'RSS')) { Write-Host " [!] Suspect inbox rule forwarding to RSS Feeds '$($rule.Name)' is $($rule.Enabled ? 'enabled' : 'disabled')" -ForegroundColor Red } if ($rule.ForwardTo -or $rule.RedirectTo -or $rule.DeleteMessage -or $rule.ForwardAsAttachmentTo -or $rule.SendTextMessageNotificationTo) { Write-Host " [!] Suspect inbox rule '$($rule.Name)' is $($rule.Enabled ? 'enabled' : 'disabled')" -ForegroundColor Yellow if ($rule.ForwardTo) { Write-Host " [>] Forwards to '$($rule.ForwardTo.Split(' '))'" } if ($rule.RedirectTo) { Write-Host " [>] Redirects to '$($rule.RedirectTo.Split(' '))'" } if ($rule.CopyToFolder) { Write-Host " [>] Copies to folder '$($rule.CopyToFolder)'" } if ($rule.DeleteMessage) { Write-Host " [>] Deletes message '$($rule.DeleteMessage)'" } if ($rule.ForwardAsAttachmentTo) { Write-Host " [>] Forwards message as attachment to '$($rule.ForwardAsAttachmentTo.Split(' '))'" } if ($rule.SendTextMessageNotificationTo) { Write-Host " [>] Sends SMS to '$($rule.SendTextMessageNotificationTo)'" } } } } else { Write-Host " [*] Mailbox $($mailbox.DisplayName) has no Outlook inbox rule" -ForegroundColor Green } } } |