Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions powershell/Maester.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,13 @@ FunctionsToExport = 'Add-MtTestResultDetail', 'Clear-MtGraphCache', 'Connect-Mae
'Test-MtCisGlobalAdminCount',
'Test-MtCis365PublicGroup',
'Test-MtCisCalendarSharing',
'Test-MtCisDkim',
'Test-MtCisSharedMailboxSignIn',
'Test-MtCisPasswordExpiry',
'Test-MtCisCustomerLockBox',
'Test-MtCisSafeLink',
'Test-MtCisAttachmentFilter',
'Test-MtCisAttachmentFilterComprehensive',
'Test-MtCisInternalMalwareNotification',
'Test-MtCisSafeAttachment',
'Test-MtCisSafeAttachmentsAtpPolicy',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
2.1.14 (L2) Ensure comprehensive attachment filtering is applied

**Rationale:**
Blocking known malicious file types can help prevent malware-infested files from infecting a host or performing other malicious attacks such as phishing and data extraction. Defining a comprehensive list of attachments can help protect against additional unknown and known threats.

#### Remediation action:

To implement a new policy containing a comprehensive list of extensions:
1. Connect to Exchange Online using Connect-ExchangeOnline.
2. Run the following script:
```
# Create an attachment policy and associated rule. The rule is
# intentionally disabled allowing the org to enable it when ready
$Policy = @{
Name = "CIS L2 Attachment Policy"
EnableFileFilter = $true
ZapEnabled = $true
EnableInternalSenderAdminNotifications = $true
InternalSenderAdminAddress = 'admin@contoso.com' # Change this.
}

$L2Extensions = @(
"7z", "a3x", "ace", "ade", "adp", "ani", "app", "appinstaller",
"applescript", "application", "appref-ms", "appx", "appxbundle", "arj",
"asd", "asx", "bas", "bat", "bgi", "bz2", "cab", "chm", "cmd", "com",
"cpl", "crt", "cs", "csh", "daa", "dbf", "dcr", "deb",
"desktopthemepackfile", "dex", "diagcab", "dif", "dir", "dll", "dmg",
"doc", "docm", "dot", "dotm", "elf", "eml", "exe", "fxp", "gadget", "gz",
"hlp", "hta", "htc", "htm", "htm", "html", "html", "hwpx", "ics", "img",
"inf", "ins", "iqy", "iso", "isp", "jar", "jnlp", "js", "jse", "kext",
"ksh", "lha", "lib", "library-ms", "lnk", "lzh", "macho", "mam", "mda",
"mdb", "mde", "mdt", "mdw", "mdz", "mht", "mhtml", "mof", "msc", "msi",
"msix", "msp", "msrcincident", "mst", "ocx", "odt", "ops", "oxps", "pcd",
"pif", "plg", "pot", "potm", "ppa", "ppam", "ppkg", "pps", "ppsm", "ppt",
"pptm", "prf", "prg", "ps1", "ps11", "ps11xml", "ps1xml", "ps2",
"ps2xml", "psc1", "psc2", "pub", "py", "pyc", "pyo", "pyw", "pyz",
"pyzw", "rar", "reg", "rev", "rtf", "scf", "scpt", "scr", "sct",
"searchConnector-ms", "service", "settingcontent-ms", "sh", "shb", "shs",
"shtm", "shtml", "sldm", "slk", "so", "spl", "stm", "svg", "swf", "sys",
"tar", "theme", "themepack", "timer", "uif", "url", "uue", "vb", "vbe",
"vbs", "vhd", "vhdx", "vxd", "wbk", "website", "wim", "wiz", "ws", "wsc",
"wsf", "wsh", "xla", "xlam", "xlc", "xll", "xlm", "xls", "xlsb", "xlsm",
"xlt", "xltm", "xlw", "xml", "xnk", "xps", "xsl", "xz", "z"
)

# Create the policy
New-MalwareFilterPolicy @Policy -FileTypes $L2Extensions

# Create the rule for all accepted domains
$Rule = @{
Name = $Policy.Name
Enabled = $false
MalwareFilterPolicy = $Policy.Name
RecipientDomainIs = (Get-AcceptedDomain).Name
Priority = 0
}

New-MalwareFilterRule @Rule
```
3. When prepared enable the rule either through the UI or PowerShell.

#### Related links

* [Microsoft 365 Defender](https://security.microsoft.com)
* [CIS Microsoft 365 Foundations Benchmark v3.1.0 - Page 102](https://www.cisecurity.org/benchmark/microsoft_365)

<!--- Results --->
%TestResult%
110 changes: 110 additions & 0 deletions powershell/public/cis/Test-MtCisAttachmentFilterComprehensive.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<#
.SYNOPSIS
Checks if the common attachment types filter is comprehensive

.DESCRIPTION
The common attachment types filter should be comprehensive

.EXAMPLE
Test-MtCisAttachmentFilterComprehensive

Returns true if the attachment types match the comprehensive list supplied by CIS

.LINK
https://maester.dev/docs/commands/Test-MtCisAttachmentFilterComprehensive
#>
function Test-MtCisAttachmentFilterComprehensive {
[CmdletBinding()]
[OutputType([bool])]
param()

if (!(Test-MtConnection ExchangeOnline)) {
Add-MtTestResultDetail -SkippedBecause NotConnectedExchange
return $null
}
elseif (!(Test-MtConnection SecurityCompliance)) {
Add-MtTestResultDetail -SkippedBecause NotConnectedSecurityCompliance
return $null
}

Write-Verbose "Executing checks"

# Set CIS supplied comprehensive extension list
$L2Extensions = @(
"7z", "a3x", "ace", "ade", "adp", "ani", "app", "appinstaller",
"applescript", "application", "appref-ms", "appx", "appxbundle", "arj",
"asd", "asx", "bas", "bat", "bgi", "bz2", "cab", "chm", "cmd", "com",
"cpl", "crt", "cs", "csh", "daa", "dbf", "dcr", "deb",
"desktopthemepackfile", "dex", "diagcab", "dif", "dir", "dll", "dmg",
"doc", "docm", "dot", "dotm", "elf", "eml", "exe", "fxp", "gadget", "gz",
"hlp", "hta", "htc", "htm", "htm", "html", "html", "hwpx", "ics", "img",
"inf", "ins", "iqy", "iso", "isp", "jar", "jnlp", "js", "jse", "kext",
"ksh", "lha", "lib", "library-ms", "lnk", "lzh", "macho", "mam", "mda",
"mdb", "mde", "mdt", "mdw", "mdz", "mht", "mhtml", "mof", "msc", "msi",
"msix", "msp", "msrcincident", "mst", "ocx", "odt", "ops", "oxps", "pcd",
"pif", "plg", "pot", "potm", "ppa", "ppam", "ppkg", "pps", "ppsm", "ppt",
"pptm", "prf", "prg", "ps1", "ps11", "ps11xml", "ps1xml", "ps2",
"ps2xml", "psc1", "psc2", "pub", "py", "pyc", "pyo", "pyw", "pyz",
"pyzw", "rar", "reg", "rev", "rtf", "scf", "scpt", "scr", "sct",
"searchConnector-ms", "service", "settingcontent-ms", "sh", "shb", "shs",
"shtm", "shtml", "sldm", "slk", "so", "spl", "stm", "svg", "swf", "sys",
"tar", "theme", "themepack", "timer", "uif", "url", "uue", "vb", "vbe",
"vbs", "vhd", "vhdx", "vxd", "wbk", "website", "wim", "wiz", "ws", "wsc",
"wsf", "wsh", "xla", "xlam", "xlc", "xll", "xlm", "xls", "xlsb", "xlsm",
"xlt", "xltm", "xlw", "xml", "xnk", "xps", "xsl", "xz", "z"
)

# Duplicate the array, so we are left with a list of extensions missing at the end
$missingExtensionList = $L2Extensions

Write-Verbose "Getting Attachment Types Filter..."
$policies = Get-MtExo -Request MalwareFilterPolicy

# For each policy, run checks
foreach ($policyId in $policies.Id) {

# We grab the policy we are checking
$policy = $policies | Where-Object { $_.Id -eq $policyId }

if ($policy.EnableFileFilter -ne "True") {
# If the policy isn't enabled, skip
break
}

foreach ($extension in $L2Extensions) {

$checkResult = $policy | Where-Object { $_.FileTypes -contains $extension }

if ($checkResult) {

#If the check finds extension, remove it from the list as it is covered
$missingExtensionList = $missingExtensionList | Where-Object { $_ –ne $extension }

}

}

}

$testResult = ($missingExtensionList | Measure-Object).Count -eq 0

if ($testResult) {
$testResultMarkdown = "Well done. Your tenant covers all CIS recommended file attachment extensions:`n`n%TestResult%"
}
else {
$testResultMarkdown = "Your tenant does not cover all CIS recommended file attachment extensions:`n`n%TestResult%"
}

$resultMd = "| Extension Name | Result |`n"
$resultMd += "| --- | --- |`n"
foreach ($item in $missingExtensionList) {
$itemResult = "❌ Fail"
$resultMd += "| $($item) | $($itemResult) |`n"
}

$testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $resultMd

Add-MtTestResultDetail -Result $testResultMarkdown

return $testResult
}
22 changes: 22 additions & 0 deletions powershell/public/cis/Test-MtCisDkim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
2.1.9 (L1) Ensure that DKIM is enabled for all Exchange Online Domains

Description: DKIM lets an organization add a digital signature to outbound email messages in the message header.

#### Remediation action:

To enable Safe Attachments for SharePoint, OneDrive, and Microsoft Teams:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This intro line mentions enabling Safe Attachments instead of DKIM.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in PR #735


1. Navigate to Microsoft 365 Defender [https://security.microsoft.com](https://security.microsoft.com)
2. Under **Email & collaboration** select **Policies & rules** then **Threat policies**
3. Under the **Rules** section click **Email authentication settings**
4. Select **DKIM**
5. Click on each domain and confirm that **Sign messages for this domain with DKIM signatures** is **Enabled**
6. A status of **Not signing DKIM signatures for this domain** is an audit fail.

#### Related links

* [Microsoft 365 Defender](https://security.microsoft.com)
* [CIS Microsoft 365 Foundations Benchmark v3.1.0 - Page 84](https://www.cisecurity.org/benchmark/microsoft_365)

<!--- Results --->
%TestResult%
115 changes: 115 additions & 0 deletions powershell/public/cis/Test-MtCisDkim.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<#
.SYNOPSIS
Checks state of DKIM for all EXO domains

.DESCRIPTION
DKIM SHOULD be enabled for all domains.

.EXAMPLE
Test-MtCisDkim

Returns true if DKIM record exists and EXO shows DKIM enabled

.LINK
https://maester.dev/docs/commands/Test-MtCisDkim
#>
function Test-MtCisDkim {
[CmdletBinding()]
[OutputType([bool])]
param(
# Selector-name for the DKIM record to test..
[string]$Selector = "selector1"
)

if(!(Test-MtConnection ExchangeOnline)){
Add-MtTestResultDetail -SkippedBecause NotConnectedExchange
return $null
}

$signingConfig = Get-MtExo -Request DkimSigningConfig
$acceptedDomains = Get-MtExo -Request AcceptedDomain
<# DKIM record without key for parked domains
$sendingDomains = $acceptedDomains | Where-Object {`
-not $_.SendingFromDomainDisabled
}
#>

$dkimRecords = @()
foreach($domain in $acceptedDomains){
$config = $signingConfig | Where-Object {`
$_.domain -eq $domain.domainname
}
if((Get-Date) -gt $config.RotateOnDate){
if($Selector -ne $config.SelectorAfterRotateOnDate){
Write-Verbose "Using DKIM $($config.SelectorAfterRotateOnDate) based on EXO config"
}
$Selector = $config.SelectorAfterRotateOnDate
}else{
if($Selector -ne $config.SelectorBeforeRotateOnDate){
Write-Verbose "Using DKIM $($config.SelectorBeforeRotateOnDate) based on EXO config"
}
$selector = $config.SelectorBeforeRotateOnDate
}

$dkimRecord = Get-MailAuthenticationRecord -DomainName $domain.DomainName -DkimSelector $Selector -Records DKIM
$dkimRecord | Add-Member -MemberType NoteProperty -Name "pass" -Value "Failed"
$dkimRecord | Add-Member -MemberType NoteProperty -Name "reason" -Value ""

if($dkimRecord.dkimRecord.GetType().Name -eq "DKIMRecord"){
if($config.enabled){
if(-not $dkimRecord.dkimRecord.validBase64){
$dkimRecord.reason = "Malformed public key"
}else{
$dkimRecord.pass = "Passed"
}
}else{
$dkimRecord.pass = "Skipped"
$dkimRecord.reason = "Parked domain"
}
}elseif($dkimRecord.dkimRecord -like "*not available"){
$dkimRecord.pass = "Skipped"
$dkimRecord.reason = $dkimRecord.dkimRecord
}else{
$dkimRecord.reason = $dkimRecord.dkimRecord
}

$dkimRecords += $dkimRecord
}

if("Failed" -in $dkimRecords.pass){
$testResult = $false
}elseif("Failed" -notin $dkimRecords.pass -and "Passed" -notin $dkimRecords.pass){
Add-MtTestResultDetail -SkippedBecause NotSupported
return $null
}else{
$testResult = $true
}

$portalLink = "https://security.microsoft.com/authentication?viewid=DKIM"

if($testResult){
$testResultMarkdown = "Well done. Your tenant's domains have DKIM configured and valid records exist.`n`n%TestResult%"
}else{
$testResultMarkdown = "Your tenant's domains do not have DKIM fully deployed. Review [EXO configuration]($portalLink) and DNS records.`n`n%TestResult%"
}

$passResult = "✅ Pass"
$failResult = "❌ Fail"
$skipResult = "🗄️ Skip"
$result = "| Domain | Result | Reason |`n"
$result += "| --- | --- | --- |`n"
foreach ($item in $dkimRecords | Sort-Object -Property domain) {
switch($item.pass){
"Passed" {$itemResult = $passResult}
"Skipped" {$itemResult = $skipResult}
"Failed" {$itemResult = $failResult}
}
$result += "| $($item.domain) | $($itemResult) | $($item.reason) |`n"
}

$testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $result

Add-MtTestResultDetail -Result $testResultMarkdown

return $testResult
}
2 changes: 1 addition & 1 deletion tests/cis/Test-MtCisAttachmentFilter.Tests.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Describe "CIS" -Tag "CIS 2.1.2", "L1", "CIS E3 Level 1", "CIS E3", "CIS", "Security", "All", "CIS M365 v3.1.0" {
Describe "CIS" -Tag "CIS 2.1.2", "L1", "CIS E3 Level 1", "CIS E3", "CIS", "Security", "All", "CIS M365 v3.1.0" {
It "CIS 2.1.2 (L1) Ensure the Common Attachment Types Filter is enabled (Only Checks Default Policy)" {

$result = Test-MtCisAttachmentFilter
Expand Down
10 changes: 10 additions & 0 deletions tests/cis/Test-MtCisAttachmentFilterComprehensive.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Describe "CIS" -Tag "CIS 2.1.14", "L2", "CIS E3 Level 2", "CIS E3", "CIS", "Security", "All", "CIS M365 v3.1.0" {
It "CIS 2.1.14 (L2) Ensure comprehensive attachment filtering is applied" {

$result = Test-MtCisAttachmentFilterComprehensive

if ($null -ne $result) {
$result | Should -Be $true -Because "the default malware filter policy did not have comprehensive attachment filtering applied."
}
}
}
10 changes: 10 additions & 0 deletions tests/cis/Test-MtCisDkim.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Describe "CIS" -Tag "CIS 2.1.9", "L1", "CIS E3 Level 1", "CIS E3", "CIS", "Security", "All", "CIS M365 v3.1.0" {
It "CIS 2.1.9 (L1) Ensure that DKIM is enabled for all Exchange Online Domains" {

$result = Test-MtCisDkim

if ($null -ne $result) {
$result | Should -Be $true -Because "DKIM record should exist and be configured."
}
}
}
7 changes: 7 additions & 0 deletions website/docs/tests/cis/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,10 @@ TBD in this case refers to CIS "manual" checks. It might be possible to automate
| [Test-MtCisSafeAttachmentsAtpPolicy](/docs/commands/) | 2.1.5 (L2) Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled |
| [Test-MtCisOutboundSpamFilterPolicy](/docs/commands/) | 2.1.6 (L1) Ensure Exchange Online Spam Policies are set to notify administrators |
| [Test-MtCisSafeAntiPhishingPolicy](/docs/commands/) | 2.1.7 (L1) Ensure that an anti-phishing policy has been created |
| TBD | 2.1.8 (L1) Ensure that SPF records are published for all Exchange Domains |
| [Test-MtCisDkim](/docs/commands/) | 2.1.9 (L1) Ensure that DKIM is enabled for all Exchange Online Domains |
| TBD | 2.1.10 (L1) Ensure DMARC Records for all Exchange Online domains are published |
| N/A | 2.1.11 (L1) Ensure the spoofed domains report is reviewed weekly |
| N/A | 2.1.12 (L1) Ensure the 'Restricted entities' report is reviewed weekly |
| N/A | 2.1.13 (L1) Ensure malware trends are reviewed at least weekly |
| [Test-MtCisAttachmentFilterComprehensive](/docs/commands/) | 2.1.14 (L2) Ensure comprehensive attachment filtering is applied |
Loading