Skip to content

Commit 31ccf06

Browse files
authored
Merge pull request #708 from NZLostboy/cis-updates
Added CIS Checks 2.1.9 & 2.1.14
2 parents 7dc2719 + 83b2c5d commit 31ccf06

9 files changed

+345
-1
lines changed

powershell/Maester.psd1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,13 @@ FunctionsToExport = 'Add-MtTestResultDetail', 'Clear-MtGraphCache', 'Connect-Mae
136136
'Test-MtCisGlobalAdminCount',
137137
'Test-MtCis365PublicGroup',
138138
'Test-MtCisCalendarSharing',
139+
'Test-MtCisDkim',
139140
'Test-MtCisSharedMailboxSignIn',
140141
'Test-MtCisPasswordExpiry',
141142
'Test-MtCisCustomerLockBox',
142143
'Test-MtCisSafeLink',
143144
'Test-MtCisAttachmentFilter',
145+
'Test-MtCisAttachmentFilterComprehensive',
144146
'Test-MtCisInternalMalwareNotification',
145147
'Test-MtCisSafeAttachment',
146148
'Test-MtCisSafeAttachmentsAtpPolicy',
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
2.1.14 (L2) Ensure comprehensive attachment filtering is applied
2+
3+
**Rationale:**
4+
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.
5+
6+
#### Remediation action:
7+
8+
To implement a new policy containing a comprehensive list of extensions:
9+
1. Connect to Exchange Online using Connect-ExchangeOnline.
10+
2. Run the following script:
11+
```
12+
# Create an attachment policy and associated rule. The rule is
13+
# intentionally disabled allowing the org to enable it when ready
14+
$Policy = @{
15+
Name = "CIS L2 Attachment Policy"
16+
EnableFileFilter = $true
17+
ZapEnabled = $true
18+
EnableInternalSenderAdminNotifications = $true
19+
InternalSenderAdminAddress = 'admin@contoso.com' # Change this.
20+
}
21+
22+
$L2Extensions = @(
23+
"7z", "a3x", "ace", "ade", "adp", "ani", "app", "appinstaller",
24+
"applescript", "application", "appref-ms", "appx", "appxbundle", "arj",
25+
"asd", "asx", "bas", "bat", "bgi", "bz2", "cab", "chm", "cmd", "com",
26+
"cpl", "crt", "cs", "csh", "daa", "dbf", "dcr", "deb",
27+
"desktopthemepackfile", "dex", "diagcab", "dif", "dir", "dll", "dmg",
28+
"doc", "docm", "dot", "dotm", "elf", "eml", "exe", "fxp", "gadget", "gz",
29+
"hlp", "hta", "htc", "htm", "htm", "html", "html", "hwpx", "ics", "img",
30+
"inf", "ins", "iqy", "iso", "isp", "jar", "jnlp", "js", "jse", "kext",
31+
"ksh", "lha", "lib", "library-ms", "lnk", "lzh", "macho", "mam", "mda",
32+
"mdb", "mde", "mdt", "mdw", "mdz", "mht", "mhtml", "mof", "msc", "msi",
33+
"msix", "msp", "msrcincident", "mst", "ocx", "odt", "ops", "oxps", "pcd",
34+
"pif", "plg", "pot", "potm", "ppa", "ppam", "ppkg", "pps", "ppsm", "ppt",
35+
"pptm", "prf", "prg", "ps1", "ps11", "ps11xml", "ps1xml", "ps2",
36+
"ps2xml", "psc1", "psc2", "pub", "py", "pyc", "pyo", "pyw", "pyz",
37+
"pyzw", "rar", "reg", "rev", "rtf", "scf", "scpt", "scr", "sct",
38+
"searchConnector-ms", "service", "settingcontent-ms", "sh", "shb", "shs",
39+
"shtm", "shtml", "sldm", "slk", "so", "spl", "stm", "svg", "swf", "sys",
40+
"tar", "theme", "themepack", "timer", "uif", "url", "uue", "vb", "vbe",
41+
"vbs", "vhd", "vhdx", "vxd", "wbk", "website", "wim", "wiz", "ws", "wsc",
42+
"wsf", "wsh", "xla", "xlam", "xlc", "xll", "xlm", "xls", "xlsb", "xlsm",
43+
"xlt", "xltm", "xlw", "xml", "xnk", "xps", "xsl", "xz", "z"
44+
)
45+
46+
# Create the policy
47+
New-MalwareFilterPolicy @Policy -FileTypes $L2Extensions
48+
49+
# Create the rule for all accepted domains
50+
$Rule = @{
51+
Name = $Policy.Name
52+
Enabled = $false
53+
MalwareFilterPolicy = $Policy.Name
54+
RecipientDomainIs = (Get-AcceptedDomain).Name
55+
Priority = 0
56+
}
57+
58+
New-MalwareFilterRule @Rule
59+
```
60+
3. When prepared enable the rule either through the UI or PowerShell.
61+
62+
#### Related links
63+
64+
* [Microsoft 365 Defender](https://security.microsoft.com)
65+
* [CIS Microsoft 365 Foundations Benchmark v3.1.0 - Page 102](https://www.cisecurity.org/benchmark/microsoft_365)
66+
67+
<!--- Results --->
68+
%TestResult%
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<#
2+
.SYNOPSIS
3+
Checks if the common attachment types filter is comprehensive
4+
5+
.DESCRIPTION
6+
The common attachment types filter should be comprehensive
7+
8+
.EXAMPLE
9+
Test-MtCisAttachmentFilterComprehensive
10+
11+
Returns true if the attachment types match the comprehensive list supplied by CIS
12+
13+
.LINK
14+
https://maester.dev/docs/commands/Test-MtCisAttachmentFilterComprehensive
15+
#>
16+
function Test-MtCisAttachmentFilterComprehensive {
17+
[CmdletBinding()]
18+
[OutputType([bool])]
19+
param()
20+
21+
if (!(Test-MtConnection ExchangeOnline)) {
22+
Add-MtTestResultDetail -SkippedBecause NotConnectedExchange
23+
return $null
24+
}
25+
elseif (!(Test-MtConnection SecurityCompliance)) {
26+
Add-MtTestResultDetail -SkippedBecause NotConnectedSecurityCompliance
27+
return $null
28+
}
29+
30+
Write-Verbose "Executing checks"
31+
32+
# Set CIS supplied comprehensive extension list
33+
$L2Extensions = @(
34+
"7z", "a3x", "ace", "ade", "adp", "ani", "app", "appinstaller",
35+
"applescript", "application", "appref-ms", "appx", "appxbundle", "arj",
36+
"asd", "asx", "bas", "bat", "bgi", "bz2", "cab", "chm", "cmd", "com",
37+
"cpl", "crt", "cs", "csh", "daa", "dbf", "dcr", "deb",
38+
"desktopthemepackfile", "dex", "diagcab", "dif", "dir", "dll", "dmg",
39+
"doc", "docm", "dot", "dotm", "elf", "eml", "exe", "fxp", "gadget", "gz",
40+
"hlp", "hta", "htc", "htm", "htm", "html", "html", "hwpx", "ics", "img",
41+
"inf", "ins", "iqy", "iso", "isp", "jar", "jnlp", "js", "jse", "kext",
42+
"ksh", "lha", "lib", "library-ms", "lnk", "lzh", "macho", "mam", "mda",
43+
"mdb", "mde", "mdt", "mdw", "mdz", "mht", "mhtml", "mof", "msc", "msi",
44+
"msix", "msp", "msrcincident", "mst", "ocx", "odt", "ops", "oxps", "pcd",
45+
"pif", "plg", "pot", "potm", "ppa", "ppam", "ppkg", "pps", "ppsm", "ppt",
46+
"pptm", "prf", "prg", "ps1", "ps11", "ps11xml", "ps1xml", "ps2",
47+
"ps2xml", "psc1", "psc2", "pub", "py", "pyc", "pyo", "pyw", "pyz",
48+
"pyzw", "rar", "reg", "rev", "rtf", "scf", "scpt", "scr", "sct",
49+
"searchConnector-ms", "service", "settingcontent-ms", "sh", "shb", "shs",
50+
"shtm", "shtml", "sldm", "slk", "so", "spl", "stm", "svg", "swf", "sys",
51+
"tar", "theme", "themepack", "timer", "uif", "url", "uue", "vb", "vbe",
52+
"vbs", "vhd", "vhdx", "vxd", "wbk", "website", "wim", "wiz", "ws", "wsc",
53+
"wsf", "wsh", "xla", "xlam", "xlc", "xll", "xlm", "xls", "xlsb", "xlsm",
54+
"xlt", "xltm", "xlw", "xml", "xnk", "xps", "xsl", "xz", "z"
55+
)
56+
57+
# Duplicate the array, so we are left with a list of extensions missing at the end
58+
$missingExtensionList = $L2Extensions
59+
60+
Write-Verbose "Getting Attachment Types Filter..."
61+
$policies = Get-MtExo -Request MalwareFilterPolicy
62+
63+
# For each policy, run checks
64+
foreach ($policyId in $policies.Id) {
65+
66+
# We grab the policy we are checking
67+
$policy = $policies | Where-Object { $_.Id -eq $policyId }
68+
69+
if ($policy.EnableFileFilter -ne "True") {
70+
# If the policy isn't enabled, skip
71+
break
72+
}
73+
74+
foreach ($extension in $L2Extensions) {
75+
76+
$checkResult = $policy | Where-Object { $_.FileTypes -contains $extension }
77+
78+
if ($checkResult) {
79+
80+
#If the check finds extension, remove it from the list as it is covered
81+
$missingExtensionList = $missingExtensionList | Where-Object { $_ –ne $extension }
82+
83+
}
84+
85+
}
86+
87+
}
88+
89+
$testResult = ($missingExtensionList | Measure-Object).Count -eq 0
90+
91+
if ($testResult) {
92+
$testResultMarkdown = "Well done. Your tenant covers all CIS recommended file attachment extensions:`n`n%TestResult%"
93+
}
94+
else {
95+
$testResultMarkdown = "Your tenant does not cover all CIS recommended file attachment extensions:`n`n%TestResult%"
96+
}
97+
98+
$resultMd = "| Extension Name | Result |`n"
99+
$resultMd += "| --- | --- |`n"
100+
foreach ($item in $missingExtensionList) {
101+
$itemResult = "❌ Fail"
102+
$resultMd += "| $($item) | $($itemResult) |`n"
103+
}
104+
105+
$testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $resultMd
106+
107+
Add-MtTestResultDetail -Result $testResultMarkdown
108+
109+
return $testResult
110+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2.1.9 (L1) Ensure that DKIM is enabled for all Exchange Online Domains
2+
3+
Description: DKIM lets an organization add a digital signature to outbound email messages in the message header.
4+
5+
#### Remediation action:
6+
7+
To enable Safe Attachments for SharePoint, OneDrive, and Microsoft Teams:
8+
9+
1. Navigate to Microsoft 365 Defender [https://security.microsoft.com](https://security.microsoft.com)
10+
2. Under **Email & collaboration** select **Policies & rules** then **Threat policies**
11+
3. Under the **Rules** section click **Email authentication settings**
12+
4. Select **DKIM**
13+
5. Click on each domain and confirm that **Sign messages for this domain with DKIM signatures** is **Enabled**
14+
6. A status of **Not signing DKIM signatures for this domain** is an audit fail.
15+
16+
#### Related links
17+
18+
* [Microsoft 365 Defender](https://security.microsoft.com)
19+
* [CIS Microsoft 365 Foundations Benchmark v3.1.0 - Page 84](https://www.cisecurity.org/benchmark/microsoft_365)
20+
21+
<!--- Results --->
22+
%TestResult%
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<#
2+
.SYNOPSIS
3+
Checks state of DKIM for all EXO domains
4+
5+
.DESCRIPTION
6+
DKIM SHOULD be enabled for all domains.
7+
8+
.EXAMPLE
9+
Test-MtCisDkim
10+
11+
Returns true if DKIM record exists and EXO shows DKIM enabled
12+
13+
.LINK
14+
https://maester.dev/docs/commands/Test-MtCisDkim
15+
#>
16+
function Test-MtCisDkim {
17+
[CmdletBinding()]
18+
[OutputType([bool])]
19+
param(
20+
# Selector-name for the DKIM record to test..
21+
[string]$Selector = "selector1"
22+
)
23+
24+
if(!(Test-MtConnection ExchangeOnline)){
25+
Add-MtTestResultDetail -SkippedBecause NotConnectedExchange
26+
return $null
27+
}
28+
29+
$signingConfig = Get-MtExo -Request DkimSigningConfig
30+
$acceptedDomains = Get-MtExo -Request AcceptedDomain
31+
<# DKIM record without key for parked domains
32+
$sendingDomains = $acceptedDomains | Where-Object {`
33+
-not $_.SendingFromDomainDisabled
34+
}
35+
#>
36+
37+
$dkimRecords = @()
38+
foreach($domain in $acceptedDomains){
39+
$config = $signingConfig | Where-Object {`
40+
$_.domain -eq $domain.domainname
41+
}
42+
if((Get-Date) -gt $config.RotateOnDate){
43+
if($Selector -ne $config.SelectorAfterRotateOnDate){
44+
Write-Verbose "Using DKIM $($config.SelectorAfterRotateOnDate) based on EXO config"
45+
}
46+
$Selector = $config.SelectorAfterRotateOnDate
47+
}else{
48+
if($Selector -ne $config.SelectorBeforeRotateOnDate){
49+
Write-Verbose "Using DKIM $($config.SelectorBeforeRotateOnDate) based on EXO config"
50+
}
51+
$selector = $config.SelectorBeforeRotateOnDate
52+
}
53+
54+
$dkimRecord = Get-MailAuthenticationRecord -DomainName $domain.DomainName -DkimSelector $Selector -Records DKIM
55+
$dkimRecord | Add-Member -MemberType NoteProperty -Name "pass" -Value "Failed"
56+
$dkimRecord | Add-Member -MemberType NoteProperty -Name "reason" -Value ""
57+
58+
if($dkimRecord.dkimRecord.GetType().Name -eq "DKIMRecord"){
59+
if($config.enabled){
60+
if(-not $dkimRecord.dkimRecord.validBase64){
61+
$dkimRecord.reason = "Malformed public key"
62+
}else{
63+
$dkimRecord.pass = "Passed"
64+
}
65+
}else{
66+
$dkimRecord.pass = "Skipped"
67+
$dkimRecord.reason = "Parked domain"
68+
}
69+
}elseif($dkimRecord.dkimRecord -like "*not available"){
70+
$dkimRecord.pass = "Skipped"
71+
$dkimRecord.reason = $dkimRecord.dkimRecord
72+
}else{
73+
$dkimRecord.reason = $dkimRecord.dkimRecord
74+
}
75+
76+
$dkimRecords += $dkimRecord
77+
}
78+
79+
if("Failed" -in $dkimRecords.pass){
80+
$testResult = $false
81+
}elseif("Failed" -notin $dkimRecords.pass -and "Passed" -notin $dkimRecords.pass){
82+
Add-MtTestResultDetail -SkippedBecause NotSupported
83+
return $null
84+
}else{
85+
$testResult = $true
86+
}
87+
88+
$portalLink = "https://security.microsoft.com/authentication?viewid=DKIM"
89+
90+
if($testResult){
91+
$testResultMarkdown = "Well done. Your tenant's domains have DKIM configured and valid records exist.`n`n%TestResult%"
92+
}else{
93+
$testResultMarkdown = "Your tenant's domains do not have DKIM fully deployed. Review [EXO configuration]($portalLink) and DNS records.`n`n%TestResult%"
94+
}
95+
96+
$passResult = "✅ Pass"
97+
$failResult = "❌ Fail"
98+
$skipResult = "🗄️ Skip"
99+
$result = "| Domain | Result | Reason |`n"
100+
$result += "| --- | --- | --- |`n"
101+
foreach ($item in $dkimRecords | Sort-Object -Property domain) {
102+
switch($item.pass){
103+
"Passed" {$itemResult = $passResult}
104+
"Skipped" {$itemResult = $skipResult}
105+
"Failed" {$itemResult = $failResult}
106+
}
107+
$result += "| $($item.domain) | $($itemResult) | $($item.reason) |`n"
108+
}
109+
110+
$testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $result
111+
112+
Add-MtTestResultDetail -Result $testResultMarkdown
113+
114+
return $testResult
115+
}

tests/cis/Test-MtCisAttachmentFilter.Tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Describe "CIS" -Tag "CIS 2.1.2", "L1", "CIS E3 Level 1", "CIS E3", "CIS", "Security", "All", "CIS M365 v3.1.0" {
1+
Describe "CIS" -Tag "CIS 2.1.2", "L1", "CIS E3 Level 1", "CIS E3", "CIS", "Security", "All", "CIS M365 v3.1.0" {
22
It "CIS 2.1.2 (L1) Ensure the Common Attachment Types Filter is enabled (Only Checks Default Policy)" {
33

44
$result = Test-MtCisAttachmentFilter
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Describe "CIS" -Tag "CIS 2.1.14", "L2", "CIS E3 Level 2", "CIS E3", "CIS", "Security", "All", "CIS M365 v3.1.0" {
2+
It "CIS 2.1.14 (L2) Ensure comprehensive attachment filtering is applied" {
3+
4+
$result = Test-MtCisAttachmentFilterComprehensive
5+
6+
if ($null -ne $result) {
7+
$result | Should -Be $true -Because "the default malware filter policy did not have comprehensive attachment filtering applied."
8+
}
9+
}
10+
}

tests/cis/Test-MtCisDkim.Tests.ps1

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Describe "CIS" -Tag "CIS 2.1.9", "L1", "CIS E3 Level 1", "CIS E3", "CIS", "Security", "All", "CIS M365 v3.1.0" {
2+
It "CIS 2.1.9 (L1) Ensure that DKIM is enabled for all Exchange Online Domains" {
3+
4+
$result = Test-MtCisDkim
5+
6+
if ($null -ne $result) {
7+
$result | Should -Be $true -Because "DKIM record should exist and be configured."
8+
}
9+
}
10+
}

website/docs/tests/cis/readme.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,10 @@ TBD in this case refers to CIS "manual" checks. It might be possible to automate
5050
| [Test-MtCisSafeAttachmentsAtpPolicy](/docs/commands/) | 2.1.5 (L2) Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled |
5151
| [Test-MtCisOutboundSpamFilterPolicy](/docs/commands/) | 2.1.6 (L1) Ensure Exchange Online Spam Policies are set to notify administrators |
5252
| [Test-MtCisSafeAntiPhishingPolicy](/docs/commands/) | 2.1.7 (L1) Ensure that an anti-phishing policy has been created |
53+
| TBD | 2.1.8 (L1) Ensure that SPF records are published for all Exchange Domains |
54+
| [Test-MtCisDkim](/docs/commands/) | 2.1.9 (L1) Ensure that DKIM is enabled for all Exchange Online Domains |
55+
| TBD | 2.1.10 (L1) Ensure DMARC Records for all Exchange Online domains are published |
56+
| N/A | 2.1.11 (L1) Ensure the spoofed domains report is reviewed weekly |
57+
| N/A | 2.1.12 (L1) Ensure the 'Restricted entities' report is reviewed weekly |
58+
| N/A | 2.1.13 (L1) Ensure malware trends are reviewed at least weekly |
59+
| [Test-MtCisAttachmentFilterComprehensive](/docs/commands/) | 2.1.14 (L2) Ensure comprehensive attachment filtering is applied |

0 commit comments

Comments
 (0)