Skip to content

Instantly share code, notes, and snippets.

@jhochwald
Created April 5, 2021 19:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jhochwald/1178b28a80b4d766531acb934d8abced to your computer and use it in GitHub Desktop.
Save jhochwald/1178b28a80b4d766531acb934d8abced to your computer and use it in GitHub Desktop.
Deploying Exchange Online security and spoofing warnings rules
#requires -Version 3.0 -Modules ExchangeOnlineManagement, MSOnline
<#
.SYNOPSIS
Deploying Exchange Online security and spoofing warnings rules
.DESCRIPTION
Deploying Exchange Online security and spoofing warnings rules
.EXAMPLE
PS C:\> .\invoke-ExchangeSafetyRules.ps1
.LINK
https://www.cyberdrain.com/automating-with-powershell-deploying-spoofing-warnings/
.NOTES
Based on the great work of CYBERDRAIN.COM (Kelvin Tegelaar)
#>
[CmdletBinding(ConfirmImpact = 'None')]
param ()
# Connect to the online services
<#
Look at the solution of CYBERDRAIN: https://www.cyberdrain.com/automating-with-powershell-deploying-spoofing-warnings/
You can use an Azure App to connect.
#>
Connect-ExchangeOnline
Connect-MsolService
# Get the InitialDomain
$InitialDomain = (Get-MsolCompanyInformation | Select-Object -ExpandProperty InitialDomain)
# Get the TenantID! Could be done with the AzureAD Module or by a complex filetr on the Domains as well
$paramInvokeWebRequest = @{
UseBasicParsing = $true
Method = 'Get'
Uri = ('https://login.windows.net/' + $InitialDomain + '/.well-known/openid-configuration')
}
$TargetTenantID = ((Invoke-WebRequest @paramInvokeWebRequest | ConvertFrom-Json).token_endpoint.Split('/')[3])
#region Rule setup
<#
Please take a closer look here
#>
$Rules = [PSCustomObject]@{
RuleName = 'Displayname spoofing'
RuleWarning = 'CAUTION: This email was sent by an external user with the same display name. This email might be fraudulent'
},
[PSCustomObject]@{
RuleName = 'Typosquatted Domains'
RuleWarning = 'CAUTION: This e-mail was sent from a domain that looks like yours, but is not. This email might be fraudulent'
},
[PSCustomObject]@{
RuleName = 'SPF fail'
RuleWarning = 'CAUTION: This e-mail is not validated to come from the sender.'
}
#endregion Rule setup
#region HelperFunction
function Get-TypoSquatDomain
{
<#
.SYNOPSIS
Get a fuzy naming variation for a given domain
.DESCRIPTION
Get a fuzy naming variation for a given domain
.PARAMETER DomainName
Top Level Domain
e.g. contoso.com or contoso.net
.EXAMPLE
PS C:\> Get-TypoSquatDomain -DomainName 'contoso.com'
.LINK
https://www.cyberdrain.com/automating-with-powershell-deploying-spoofing-warnings/
.NOTES
Internal Helper
Based on the great work of CYBERDRAIN.COM (Kelvin Tegelaar)
I renamed it
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([string])]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
HelpMessage = 'Top Level Domain')]
[ValidateNotNullOrEmpty()]
[ValidateNotNull()]
[Alias('Domain')]
[string]
$DomainName
)
begin
{
$ReplacementGylph = [pscustomobject]@{
0 = 'b', 'd'
1 = 'b', 'lb'
2 = 'c', 'e'
3 = 'd', 'b'
4 = 'd', 'cl'
5 = 'd', 'dl'
6 = 'e', 'c'
7 = 'g', 'q'
8 = 'h', 'lh'
9 = 'i', '1'
10 = 'i', 'l'
11 = 'k', 'lk'
12 = 'k', 'ik'
13 = 'k', 'lc'
14 = 'l', '1'
15 = 'l', 'i'
16 = 'm', 'n'
17 = 'm', 'nn'
18 = 'm', 'rn'
19 = 'm', 'rr'
20 = 'n', 'r'
21 = 'n', 'm'
22 = 'o', '0'
23 = 'o', 'q'
24 = 'q', 'g'
25 = 'u', 'v'
26 = 'v', 'u'
27 = 'w', 'vv'
28 = 'w', 'uu'
29 = 'z', 's'
30 = 'n', 'r'
31 = 'r', 'n'
}
$i = 0
}
process
{
$TLD = ($DomainName -split '\.' | Select-Object -Last 1)
$DomainName = ($DomainName -split '\.' | Select-Object -First 1)
$HomoGlyph = do
{
$NewDomain = $DomainName -replace $ReplacementGylph.$i
$NewDomain
$NewDomain + 's'
$NewDomain + 'a'
$NewDomain + 't'
$NewDomain + 'en'
$i++
}
while ($i -lt 29)
$i = 0
$BitSquatAndOmission = do
{
$($DomainName[0 .. ($i)] -join '') + $($DomainName[($i + 2) .. $DomainName.Length] -join '')
$($DomainName[0 .. $i] -join '') + $DomainName[$i + 2] + $DomainName[$i + 1] + $($DomainName[($i + 3) .. $DomainName.Length] -join '')
$i++
}
while ($i -lt $DomainName.Length)
$Plurals = $DomainName + 's'
$DomainName + 'a'
$DomainName + 'en'
$DomainName + 't'
$CombinedDomains = $HomoGlyph + $BitSquatAndOmission + $Plurals | ForEach-Object -Process {
"$($_).$($TLD)"
}
}
end
{
return ($CombinedDomains | Sort-Object -Unique | Where-Object -FilterScript {
$_ -ne $DomainName
})
}
}
#endregion HelperFunction
foreach ($Rule in $Rules)
{
$Reason = $Rule.RuleWarning
# Also something you might want to tweak to fit your CI or security requirements
$WarningHTML = @"
<table border=0 cellspacing=0 cellpadding=0 align=left width="100%" style='width:100.0%><tr style='mso-yfti-irow:0;mso-yfti-firstrow:yes;mso-yfti-lastrow:yes'><td style='background:#910A19;padding:5.25pt 1.5pt 5.25pt 1.5pt'></td><td width="100%" style='width:100.0%;background:#FDF2F4;padding:5.25pt 3.75pt 5.25pt 11.25pt; word-wrap:break-word' cellpadding="7px 5px 7px 15px" color="#212121"><div><p mso-element-wrap:around;mso-element-anchor-vertical:paragraph;mso-element-anchor-horizontal: column;mso-height-rule:exactly'><span style='font-size:10.0pt;color:#212121'>$Reason<o:p></o:p></span></p></div></td></tr></table>
"@
# Try to figure out if the rule exists
$ExistingRule = (Get-TransportRule | Where-Object -FilterScript {
$_.Identity -contains $Rule.RuleName
})
if ($ExistingRule)
{
# Modify the existing rule(s)!
switch ($Rule.rulename)
{
'SPF Fail'
{
$paramSetTransportRule = @{
Identity = $Rule.RuleName
Priority = 0
FromScope = 'NotInOrganization'
ApplyHtmlDisclaimerLocation = 'Prepend'
HeaderMatchesMessageHeader = 'Authentication-Results'
HeaderMatchesPatterns = 'fail'
SenderAddressLocation = 'HeaderOrEnvelope'
ApplyHtmlDisclaimerText = $WarningHTML
}
$null = (Set-TransportRule @paramSetTransportRule)
}
'Displayname Spoofing'
{
$AllNames = ((Get-Mailbox -ResultSize Unlimited).DisplayName)
$paramSetTransportRule = @{
Identity = $Rule.RuleName
Priority = 0
FromScope = 'NotInOrganization'
ApplyHtmlDisclaimerLocation = 'Prepend'
HeaderMatchesMessageHeader = 'From'
HeaderMatchesPatterns = $AllNames
SenderAddressLocation = 'HeaderOrEnvelope'
ApplyHtmlDisclaimerText = $WarningHTML
}
$null = (Set-TransportRule @paramSetTransportRule)
}
'Typosquatted Domains'
{
#TODO: This need some care! The list might get to big and creates an error.
<#
A plit based on each domain with a dedicated rule will come soon!
#>
$OwnedDomains = (Get-MsolDomain -TenantId $TargetTenantID)
$TypoSquattedDomains = foreach ($Domain in $OwnedDomains)
{
Get-TypoSquatDomain -DomainName $Domain.name
}
$paramSetTransportRule = @{
Identity = $Rule.RuleName
Priority = 0
FromScope = 'NotInOrganization'
ApplyHtmlDisclaimerLocation = 'Prepend'
SenderDomainIs = $TypoSquattedDomains
SenderAddressLocation = 'HeaderOrEnvelope'
ApplyHtmlDisclaimerText = $WarningHTML
}
$null = (Set-TransportRule @paramSetTransportRule)
}
}
}
else
{
# Create the rule(s)!
switch ($Rule.rulename)
{
'SPF Fail'
{
$paramNewTransportRule = @{
Name = $Rule.RuleName
Priority = 0
FromScope = 'NotInOrganization'
ApplyHtmlDisclaimerLocation = 'Prepend'
HeaderMatchesMessageHeader = 'Authentication-Results'
HeaderMatchesPatterns = 'fail'
SenderAddressLocation = 'HeaderOrEnvelope'
ApplyHtmlDisclaimerText = $WarningHTML
}
$null = (New-TransportRule @paramNewTransportRule)
}
'Displayname Spoofing'
{
$AllNames = (Get-Mailbox -ResultSize Unlimited).DisplayName
$paramNewTransportRule = @{
Name = $Rule.RuleName
Priority = 0
FromScope = 'NotInOrganization'
ApplyHtmlDisclaimerLocation = 'Prepend'
HeaderMatchesMessageHeader = 'From'
HeaderMatchesPatterns = $AllNames
SenderAddressLocation = 'HeaderOrEnvelope'
ApplyHtmlDisclaimerText = $WarningHTML
}
$null = (New-TransportRule @paramNewTransportRule)
}
'Typosquatted Domains'
{
#TODO: This need some care! The list might get to big and creates an error.
<#
A plit based on each domain with a dedicated rule will come soon!
#>
$OwnedDomains = (Get-AcceptedDomain)
$TypoSquattedDomains = foreach ($Domain in $OwnedDomains)
{
Get-TypoSquatDomain -DomainName $Domain.Domainname
}
$paramNewTransportRule = @{
Name = $Rule.RuleName
Priority = 0
FromScope = 'NotInOrganization'
ApplyHtmlDisclaimerLocation = 'Prepend'
SenderDomainIs = $TypoSquattedDomains
SenderAddressLocation = 'HeaderOrEnvelope'
ApplyHtmlDisclaimerText = $WarningHTML
}
$null = (New-TransportRule @paramNewTransportRule)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment