SHELL/2.4.2.ps1
|
$CheckId = "2.4.2" $Title = "Ensure Priority accounts have 'Strict protection' presets applied" $Level = "L1" $BenchmarkType = "Automated" $AuditCommands = [pscustomobject]@{ EOPStrictPreset = "Get-EOPProtectionPolicyRule | Where-Object { Name contains 'Strict Preset Security Policy' }" ATPStrictPreset = "Get-ATPProtectionPolicyRule | Where-Object { Name contains 'Strict Preset Security Policy' }" } function Get-AvailableCommandName { param( [string[]]$Names ) foreach ($Name in $Names) { if (Get-Command -Name $Name -ErrorAction SilentlyContinue) { return $Name } } return $null } function Convert-ToAuditText { param( [AllowNull()] [object]$InputObject ) if ($null -eq $InputObject) { return "" } $JsonText = "" try { $JsonText = $InputObject | ConvertTo-Json -Depth 12 -Compress } catch { $JsonText = "" } $RawText = "" try { $RawText = $InputObject | Out-String } catch { $RawText = "" } return "$JsonText $RawText".ToLowerInvariant().Replace("e-mail", "email") } function Get-ScopeValues { param( [Parameter(Mandatory = $true)] [object]$Rule ) $ScopeProperties = @( "SentTo", "SentToMemberOf", "Users", "UserTags", "RecipientTags", "Recipients", "RecipientDomainIs" ) $Values = @() foreach ($Name in $ScopeProperties) { if ($null -ne $Rule.PSObject.Properties[$Name]) { $Current = @($Rule.$Name | ForEach-Object { ([string]$_).Trim() } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) -and $_ -ne "{}" }) $Values += $Current } } return @($Values | Select-Object -Unique) } function Get-StrictPresetEvidence { param( [Parameter(Mandatory = $true)] [string]$CommandName, [Parameter(Mandatory = $true)] [string]$Category ) $AllRules = @(& $CommandName) $StrictRules = @($AllRules | Where-Object { $Text = Convert-ToAuditText -InputObject $_ $Text.Contains("strict preset security policy") }) $RuleDetails = foreach ($Rule in $StrictRules) { $ScopeValues = Get-ScopeValues -Rule $Rule [pscustomobject]@{ Identity = [string]$Rule.Identity Name = [string]$Rule.Name ScopeValues = @($ScopeValues) ScopeCount = @($ScopeValues).Count IncludesPriorityKeyword = (@($ScopeValues | Where-Object { ([string]$_).ToLowerInvariant().Contains("priority") })).Count -gt 0 } } [pscustomobject]@{ Category = $Category CommandUsed = $CommandName RuleCount = @($AllRules).Count StrictRuleCount = @($StrictRules).Count Rules = @($RuleDetails) } } try { $FailureReasons = [System.Collections.Generic.List[string]]::new() $ManualReasons = [System.Collections.Generic.List[string]]::new() $EopEvidence = [ordered]@{ Assessable = $false Result = $null Details = $null } $AtpEvidence = [ordered]@{ Assessable = $false Result = $null Details = $null } # CIS v6.0.1 2.4.2 audit is UI-based; use cmdlets as best-effort verification. $EopCmd = Get-AvailableCommandName -Names @("Get-EOPProtectionPolicyRule") if ($EopCmd) { $EopEvidence.Assessable = $true $EopDetails = Get-StrictPresetEvidence -CommandName $EopCmd -Category "Exchange Online Protection" $EopEvidence.Details = $EopDetails if ($EopDetails.StrictRuleCount -eq 0) { $EopEvidence.Result = $false $FailureReasons.Add("Strict Preset Security Policy rule was not found for Exchange Online Protection.") } else { $HasScope = @($EopDetails.Rules | Where-Object { $_.ScopeCount -gt 0 }).Count -gt 0 $EopEvidence.Result = $HasScope if (-not $HasScope) { $FailureReasons.Add("EOP strict preset policy exists but no specific recipients/groups were detected in scope.") } } } else { $ManualReasons.Add("Get-EOPProtectionPolicyRule cmdlet is unavailable in the current session.") } $AtpCmd = Get-AvailableCommandName -Names @("Get-ATPProtectionPolicyRule") if ($AtpCmd) { $AtpEvidence.Assessable = $true $AtpDetails = Get-StrictPresetEvidence -CommandName $AtpCmd -Category "Defender for Office 365" $AtpEvidence.Details = $AtpDetails if ($AtpDetails.StrictRuleCount -eq 0) { $AtpEvidence.Result = $false $FailureReasons.Add("Strict Preset Security Policy rule was not found for Defender for Office 365 protection.") } else { $HasScope = @($AtpDetails.Rules | Where-Object { $_.ScopeCount -gt 0 }).Count -gt 0 $AtpEvidence.Result = $HasScope if (-not $HasScope) { $FailureReasons.Add("Defender strict preset policy exists but no specific recipients/groups were detected in scope.") } } } else { $ManualReasons.Add("Get-ATPProtectionPolicyRule cmdlet is unavailable in the current session.") } # Mapping to org-defined priority accounts/groups may still require manual validation. if ($FailureReasons.Count -eq 0 -and $ManualReasons.Count -eq 0) { $AllScopeValues = @() if ($EopEvidence.Details) { $AllScopeValues += @($EopEvidence.Details.Rules | ForEach-Object { $_.ScopeValues }) } if ($AtpEvidence.Details) { $AllScopeValues += @($AtpEvidence.Details.Rules | ForEach-Object { $_.ScopeValues }) } $AllScopeValues = @($AllScopeValues | Select-Object -Unique) $HasPriorityNamedScope = @($AllScopeValues | Where-Object { ([string]$_).ToLowerInvariant().Contains("priority") }).Count -gt 0 if (-not $HasPriorityNamedScope) { $ManualReasons.Add("Strict preset policies were found, but automatic validation could not confirm they map to the organization's defined priority accounts/groups.") } } $Status = "PASS" $Pass = $true $ErrorMessage = $null if ($FailureReasons.Count -gt 0) { $Status = "FAIL" $Pass = $false $ErrorMessage = ($FailureReasons -join " ") } elseif ($ManualReasons.Count -gt 0) { $Status = "MANUAL_REVIEW" $Pass = $null $ErrorMessage = "Manual review required: $($ManualReasons -join " ")" } [pscustomobject]@{ CheckId = $CheckId Title = $Title Level = $Level BenchmarkType = $BenchmarkType Status = $Status Pass = $Pass Evidence = [pscustomobject]@{ AuditCommands = $AuditCommands AuditUiReference = @( "Microsoft 365 Defender > E-mail & collaboration > Policies & rules > Threat policies", "Verify Strict Preset Security Policy appears in Anti-phishing, Anti-spam, Anti-malware, Safe Attachments, and Safe Links", "Verify strict preset includes the organization's priority accounts/groups" ) EOPCheck = [pscustomobject]$EopEvidence DefenderCheck = [pscustomobject]$AtpEvidence ManualReviewReasons = @($ManualReasons) SourceDocument = "CIS_Microsoft_365_Foundations_Benchmark_v6.0.1" } Error = $ErrorMessage Timestamp = Get-Date } } catch { [pscustomobject]@{ CheckId = $CheckId Title = $Title Level = $Level BenchmarkType = $BenchmarkType Status = "ERROR" Pass = $null Evidence = [pscustomobject]@{ AuditCommands = $AuditCommands SourceDocument = "CIS_Microsoft_365_Foundations_Benchmark_v6.0.1" } Error = $_.Exception.Message Timestamp = Get-Date } } |