From 69a74edef42f02084acd2a81b28b06eadecfe7fd Mon Sep 17 00:00:00 2001 From: David Paulson Date: Wed, 11 Sep 2024 15:39:27 -0500 Subject: [PATCH 1/7] Added Nov SU 2024 Build Numbers --- Shared/Get-ExchangeBuildVersionInformation.ps1 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Shared/Get-ExchangeBuildVersionInformation.ps1 b/Shared/Get-ExchangeBuildVersionInformation.ps1 index 999d007f47..68f544cb00 100644 --- a/Shared/Get-ExchangeBuildVersionInformation.ps1 +++ b/Shared/Get-ExchangeBuildVersionInformation.ps1 @@ -127,16 +127,14 @@ function Get-ExchangeBuildVersionInformation { $cuReleaseDate = "02/13/2024" $supportedBuildNumber = $true } - (GetBuildVersion $ex19 "CU14" -SU "Apr24HU") { $latestSUBuild = $true } - (GetBuildVersion $ex19 "CU14" -SU "Mar24SU") { $latestSUBuild = $true } + (GetBuildVersion $ex19 "CU14" -SU "Nov24SU") { $latestSUBuild = $true } { $_ -lt (GetBuildVersion $ex19 "CU14") } { $cuLevel = "CU13" $cuReleaseDate = "05/03/2023" $supportedBuildNumber = $true $orgValue = 16761 } - (GetBuildVersion $ex19 "CU13" -SU "Apr24HU") { $latestSUBuild = $true } - (GetBuildVersion $ex19 "CU13" -SU "Mar24SU") { $latestSUBuild = $true } + (GetBuildVersion $ex19 "CU13" -SU "Nov24SU") { $latestSUBuild = $true } { $_ -lt (GetBuildVersion $ex19 "CU13") } { $cuLevel = "CU12" $cuReleaseDate = "04/20/2022" @@ -228,8 +226,7 @@ function Get-ExchangeBuildVersionInformation { $cuReleaseDate = "04/20/2022" $supportedBuildNumber = $true } - (GetBuildVersion $ex16 "CU23" -SU "Apr24HU") { $latestSUBuild = $true } - (GetBuildVersion $ex16 "CU23" -SU "Mar24SU") { $latestSUBuild = $true } + (GetBuildVersion $ex16 "CU23" -SU "Nov24SU") { $latestSUBuild = $true } { $_ -lt (GetBuildVersion $ex16 "CU23") } { $cuLevel = "CU22" $cuReleaseDate = "09/28/2021" @@ -715,6 +712,7 @@ function GetExchangeBuildDictionary { "Nov23SU" = "15.1.2507.35" "Mar24SU" = "15.1.2507.37" "Apr24HU" = "15.1.2507.39" + "Nov24SU" = "15.1.2507.43" }) } "Exchange2019" = @{ @@ -814,10 +812,12 @@ function GetExchangeBuildDictionary { "Nov23SU" = "15.2.1258.28" "Mar24SU" = "15.2.1258.32" "Apr24HU" = "15.2.1258.34" + "Nov24SU" = "15.2.1258.38" }) "CU14" = (NewCUAndSUObject "15.2.1544.4" @{ "Mar24SU" = "15.2.1544.9" "Apr24HU" = "15.2.1544.11" + "Nov24SU" = "15.2.1544.13" }) } } From ec0eb6c0313665c44fbd1f966a0f3fb219e9d8a9 Mon Sep 17 00:00:00 2001 From: David Paulson Date: Fri, 27 Sep 2024 15:01:24 -0500 Subject: [PATCH 2/7] Add ECC Certificate Check for Nov24SU --- ...ke-AnalyzerFrequentConfigurationIssues.ps1 | 15 +++++++++++ .../Invoke-AnalyzerSecurityOverrides.ps1 | 26 +++++++++++++++++++ .../Get-ExchangeRegistryValues.ps1 | 7 +++++ .../Tests/HealthChecker.E16.Main.Tests.ps1 | 3 ++- .../Tests/HealthChecker.E19.Main.Tests.ps1 | 3 ++- .../Tests/HealthChecker.MockedCalls.Tests.ps1 | 2 +- ...thCheckerTest.CommonMocks.NotPublished.ps1 | 1 + 7 files changed, 54 insertions(+), 3 deletions(-) diff --git a/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerFrequentConfigurationIssues.ps1 b/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerFrequentConfigurationIssues.ps1 index 5dc05064ed..12cd4301b0 100644 --- a/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerFrequentConfigurationIssues.ps1 +++ b/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerFrequentConfigurationIssues.ps1 @@ -74,6 +74,21 @@ function Invoke-AnalyzerFrequentConfigurationIssues { } Add-AnalyzedResultInformation @params + $detailsValue = $exchangeInformation.RegistryValues.EnableEccCertificateSupport + $displayWriteType = "Grey" + + if (-not (Test-ExchangeBuildGreaterOrEqualThanSecurityPatch -CurrentExchangeBuild $exchangeInformation.BuildInformation.VersionInformation -SUName "Nov24SU") -and $detailsValue -eq "1") { + $detailsValue = "1 --- Warning: On a build that doesn't support this configuration yet.`r`n`t`tMore Information: https://aka.ms/HC-EccCertificateChange" + $displayWriteType = "Yellow" + } + + $params = $baseParams + @{ + Name = "EnableEccCertificateSupport Registry Value" + Details = $detailsValue + DisplayWriteType = $displayWriteType + } + Add-AnalyzedResultInformation @params + $displayValue = $exchangeInformation.RegistryValues.CtsProcessorAffinityPercentage $displayWriteType = "Green" diff --git a/Diagnostics/HealthChecker/Analyzer/Security/Invoke-AnalyzerSecurityOverrides.ps1 b/Diagnostics/HealthChecker/Analyzer/Security/Invoke-AnalyzerSecurityOverrides.ps1 index b8a75e272f..a7e606bbf8 100644 --- a/Diagnostics/HealthChecker/Analyzer/Security/Invoke-AnalyzerSecurityOverrides.ps1 +++ b/Diagnostics/HealthChecker/Analyzer/Security/Invoke-AnalyzerSecurityOverrides.ps1 @@ -89,4 +89,30 @@ function Invoke-AnalyzerSecurityOverrides { } Add-AnalyzedResultInformation @params } + + if ((Test-ExchangeBuildGreaterOrEqualThanSecurityPatch -CurrentExchangeBuild $exchangeInformation.BuildInformation.VersionInformation -SUName "Nov24SU")) { + Write-Verbose "ECCCertificateSupport Check for after changes" + $params = @{ + ExchangeSettingOverride = $exchangeInformation.SettingOverrides + GetSettingOverride = $HealthServerObject.OrganizationInformation.GetSettingOverride + FilterServer = $HealthServerObject.ServerName + FilterServerVersion = $exchangeBuild + FilterComponentName = "Global" + FilterSectionName = "ECCCertificateSupport" + FilterParameterName = "Enabled" + } + + [array]$eccCertificateSupportOverride = Get-FilteredSettingOverrideInformation @params + $overrideIsEnabled = ($null -ne ($eccCertificateSupportOverride | Where-Object { $_.ParameterValue -eq "true" })) + + if ($overrideIsEnabled -and $exchangeInformation.RegistryValues.EnableEccCertificateSupport -ne "1") { + $params = $baseParams + @{ + Name = "ECCCertificateSupport" + Details = "Error - No longer fully enabled`r`n`t`tMore Information: https://aka.ms/HC-EccCertificateChange" + DisplayWriteType = "Red" + DisplayTestingValue = $true + } + Add-AnalyzedResultInformation @params + } + } } diff --git a/Diagnostics/HealthChecker/DataCollection/ExchangeInformation/Get-ExchangeRegistryValues.ps1 b/Diagnostics/HealthChecker/DataCollection/ExchangeInformation/Get-ExchangeRegistryValues.ps1 index 1caf2912d2..238af8e227 100644 --- a/Diagnostics/HealthChecker/DataCollection/ExchangeInformation/Get-ExchangeRegistryValues.ps1 +++ b/Diagnostics/HealthChecker/DataCollection/ExchangeInformation/Get-ExchangeRegistryValues.ps1 @@ -62,6 +62,12 @@ function Get-ExchangeRegistryValues { ValueType = "String" } + $eccCertificateSupportParams = $baseParams + @{ + SubKey = "SOFTWARE\Microsoft\ExchangeServer\v15\Diagnostics" + GetValue = "EnableEccCertificateSupport" + ValueType = "String" + } + return [PSCustomObject]@{ DisableBaseTypeCheckForDeserialization = [int](Get-RemoteRegistryValue @baseTypeCheckForDeserializationParams) CtsProcessorAffinityPercentage = [int](Get-RemoteRegistryValue @ctsParams) @@ -72,5 +78,6 @@ function Get-ExchangeRegistryValues { MsiInstallPath = [string](Get-RemoteRegistryValue @installDirectoryParams) DisablePreservation = [string](Get-RemoteRegistryValue @disablePreservationParams) FipFsDatabasePath = [string](Get-RemoteRegistryValue @fipFsDatabasePathParams) + EnableEccCertificateSupport = [string](Get-RemoteRegistryValue @eccCertificateSupportParams) } } diff --git a/Diagnostics/HealthChecker/Tests/HealthChecker.E16.Main.Tests.ps1 b/Diagnostics/HealthChecker/Tests/HealthChecker.E16.Main.Tests.ps1 index 42ccbf76a3..cedad1097e 100644 --- a/Diagnostics/HealthChecker/Tests/HealthChecker.E16.Main.Tests.ps1 +++ b/Diagnostics/HealthChecker/Tests/HealthChecker.E16.Main.Tests.ps1 @@ -119,8 +119,9 @@ Describe "Testing Health Checker by Mock Data Imports - Exchange 2016" { TestObjectMatch "EdgeTransport.exe.config Present" "True" -WriteType "Green" TestObjectMatch "Open Relay Wild Card Domain" "Not Set" TestObjectMatch "EXO Connector Present" "False" + TestObjectMatch "EnableEccCertificateSupport Registry Value" $false - $Script:ActiveGrouping.Count | Should -Be 12 + $Script:ActiveGrouping.Count | Should -Be 13 } It "Display Results - Security Settings" { diff --git a/Diagnostics/HealthChecker/Tests/HealthChecker.E19.Main.Tests.ps1 b/Diagnostics/HealthChecker/Tests/HealthChecker.E19.Main.Tests.ps1 index 8976e04336..1ab3021e5b 100644 --- a/Diagnostics/HealthChecker/Tests/HealthChecker.E19.Main.Tests.ps1 +++ b/Diagnostics/HealthChecker/Tests/HealthChecker.E19.Main.Tests.ps1 @@ -125,8 +125,9 @@ Describe "Testing Health Checker by Mock Data Imports" { TestObjectMatch "Open Relay Wild Card Domain" "Not Set" TestObjectMatch "EXO Connector Present" "True" # Custom EXO Connector with no TlsDomain TlsAuthLevel TestObjectMatch "UnifiedContent Auto Cleanup Configured" $true -WriteType "Green" + TestObjectMatch "EnableEccCertificateSupport Registry Value" $false - $Script:ActiveGrouping.Count | Should -Be 14 + $Script:ActiveGrouping.Count | Should -Be 15 } It "Display Results - Security Settings" { diff --git a/Diagnostics/HealthChecker/Tests/HealthChecker.MockedCalls.Tests.ps1 b/Diagnostics/HealthChecker/Tests/HealthChecker.MockedCalls.Tests.ps1 index 1e7e940c24..b9afcbc1ec 100644 --- a/Diagnostics/HealthChecker/Tests/HealthChecker.MockedCalls.Tests.ps1 +++ b/Diagnostics/HealthChecker/Tests/HealthChecker.MockedCalls.Tests.ps1 @@ -64,7 +64,7 @@ Describe "Testing Health Checker by Mock Data Imports" { Assert-MockCalled Get-WmiObjectHandler -Exactly 6 Assert-MockCalled Invoke-ScriptBlockHandler -Exactly 5 - Assert-MockCalled Get-RemoteRegistryValue -Exactly 25 + Assert-MockCalled Get-RemoteRegistryValue -Exactly 26 Assert-MockCalled Get-RemoteRegistrySubKey -Exactly 1 Assert-MockCalled Get-NETFrameworkVersion -Exactly 1 Assert-MockCalled Get-DotNetDllFileVersions -Exactly 1 diff --git a/Diagnostics/HealthChecker/Tests/HealthCheckerTest.CommonMocks.NotPublished.ps1 b/Diagnostics/HealthChecker/Tests/HealthCheckerTest.CommonMocks.NotPublished.ps1 index f1f48412b8..c0a1d6804d 100644 --- a/Diagnostics/HealthChecker/Tests/HealthCheckerTest.CommonMocks.NotPublished.ps1 +++ b/Diagnostics/HealthChecker/Tests/HealthCheckerTest.CommonMocks.NotPublished.ps1 @@ -138,6 +138,7 @@ Mock Get-RemoteRegistryValue { "DisablePreservation" { return 0 } "DatabasePath" { return "$Script:MockDataCollectionRoot\Exchange" } "SuppressExtendedProtection" { return 0 } + "EnableEccCertificateSupport" { return $null } default { throw "Failed to find GetValue: $GetValue" } } } From f21b41c19ff11ad2debf56b026b652618f99c484 Mon Sep 17 00:00:00 2001 From: David Paulson Date: Wed, 16 Oct 2024 10:30:52 -0500 Subject: [PATCH 3/7] Remove pester grouping counts on E15 --- .../Tests/HealthChecker.E15.Main.Tests.ps1 | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/Diagnostics/HealthChecker/Tests/HealthChecker.E15.Main.Tests.ps1 b/Diagnostics/HealthChecker/Tests/HealthChecker.E15.Main.Tests.ps1 index 50c6043ba8..f7423d170a 100644 --- a/Diagnostics/HealthChecker/Tests/HealthChecker.E15.Main.Tests.ps1 +++ b/Diagnostics/HealthChecker/Tests/HealthChecker.E15.Main.Tests.ps1 @@ -32,7 +32,6 @@ Describe "Testing Health Checker by Mock Data Imports - Exchange 2013" { TestObjectMatch "Internet Web Proxy" "Not Set" TestObjectMatch "Extended Protection Enabled (Any VDir)" $false TestObjectMatch "Setting Overrides Detected" $false - $Script:ActiveGrouping.Count | Should -Be 18 } It "Display Results - Organization Information" { @@ -42,8 +41,6 @@ Describe "Testing Health Checker by Mock Data Imports - Exchange 2013" { TestObjectMatch "Enable Download Domains" "Unknown" TestObjectMatch "AD Split Permissions" "False" TestObjectMatch "Dynamic Distribution Group Public Folder Mailboxes Count" 1 -WriteType "Green" - - $Script:ActiveGrouping.Count | Should -Be 6 } It "Display Results - Operating System Information" { @@ -69,8 +66,6 @@ Describe "Testing Health Checker by Mock Data Imports - Exchange 2013" { $pageFileAdditional = GetObject "PageFile Additional Information" $pageFileAdditional | Should -Be "Error: PageFile is not set to total system memory plus 10MB which should be 6154MB." - - $Script:ActiveGrouping.Count | Should -Be 14 } It "Display Results - Process/Hardware Information" { @@ -84,8 +79,6 @@ Describe "Testing Health Checker by Mock Data Imports - Exchange 2013" { TestObjectMatch "All Processor Cores Visible" "Passed" -WriteType "Green" TestObjectMatch "Max Processor Speed" 2200 TestObjectMatch "Physical Memory" 6 - - $Script:ActiveGrouping.Count | Should -Be 11 } It "Display Results - NIC Settings" { @@ -103,8 +96,6 @@ Describe "Testing Health Checker by Mock Data Imports - Exchange 2013" { TestObjectMatch "Address" "192.168.9.11/24 Gateway: 192.168.9.1" TestObjectMatch "Registered In DNS" "True" TestObjectMatch "Packets Received Discarded" 0 -WriteType "Green" - - $Script:ActiveGrouping.Count | Should -Be 16 } It "Display Results - Frequent Configuration Issues" { @@ -120,8 +111,6 @@ Describe "Testing Health Checker by Mock Data Imports - Exchange 2013" { TestObjectMatch "EXO Connector Present" "False" # For some reason by default Exchange 2013 doesn't have this setting. not going to look into it just going to make a not of it and move on. TestObjectMatch "UnifiedContent Auto Cleanup Configured" $false -WriteType "Red" - - $Script:ActiveGrouping.Count | Should -Be 13 } It "Display Results - Security Settings" { @@ -130,8 +119,6 @@ Describe "Testing Health Checker by Mock Data Imports - Exchange 2013" { TestObjectMatch "LmCompatibilityLevel Settings" 3 TestObjectMatch "SMB1 Installed" "True" -WriteType "Red" TestObjectMatch "SMB1 Blocked" "False" -WriteType "Red" - - $Script:ActiveGrouping.Count | Should -Be 88 } It "Display Results - Security Vulnerability" { @@ -139,7 +126,6 @@ Describe "Testing Health Checker by Mock Data Imports - Exchange 2013" { $cveTests = $Script:ActiveGrouping.TestingValue | Where-Object { ($_.GetType().Name -eq "String") -and ($_.StartsWith("CVE")) } $cveTests.Contains("CVE-2020-1147") | Should -Be $true - $cveTests.Count | Should -Be 58 } } } From 194438ac76ebfde8d46dea4f7ff28c98c2031479 Mon Sep 17 00:00:00 2001 From: David Paulson Date: Thu, 17 Oct 2024 13:08:15 -0500 Subject: [PATCH 4/7] Filter Setting Override to allow list of Parameters to find & Use -contains instead of .Contains() --- ...Get-FilteredSettingOverrideInformation.ps1 | 75 ++++++++++--------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/Diagnostics/HealthChecker/Analyzer/Get-FilteredSettingOverrideInformation.ps1 b/Diagnostics/HealthChecker/Analyzer/Get-FilteredSettingOverrideInformation.ps1 index 481b9275b0..c49b44b7b4 100644 --- a/Diagnostics/HealthChecker/Analyzer/Get-FilteredSettingOverrideInformation.ps1 +++ b/Diagnostics/HealthChecker/Analyzer/Get-FilteredSettingOverrideInformation.ps1 @@ -29,11 +29,11 @@ function Get-FilteredSettingOverrideInformation { [string]$FilterSectionName, [Parameter(Mandatory = $true)] - [string]$FilterParameterName + [string[]]$FilterParameterName ) begin { Write-Verbose "Calling: $($MyInvocation.MyCommand)" - Write-Verbose "Trying to filter down results for ComponentName: $FilterComponentName SectionName: $FilterSectionName ParameterName: $FilterParameterName" + Write-Verbose "Trying to filter down results for ComponentName: $FilterComponentName SectionName: $FilterSectionName ParameterName: $([string]::Join(", ", $FilterParameterName))" $results = New-Object "System.Collections.Generic.List[object]" $findFromOverride = $null $usedAdSettings = $false @@ -63,47 +63,50 @@ function Get-FilteredSettingOverrideInformation { Write-Verbose "Working on entry: $($entry.Name)" foreach ($p in [array]($entry.Parameters)) { Write-Verbose "Working on parameter: $p" - if ($p.Contains($FilterParameterName)) { - $value = $p.Substring($FilterParameterName.Length + 1) # Add plus 1 for '=' - # everything matched, however, only add it to the list for the following reasons - # - Status is Accepted and not from AD and a unique value in the list - # - Or From AD and current logic determines it applies + foreach ($currentFilterParameterName in $FilterParameterName) { + if ($p.Contains($currentFilterParameterName)) { + $value = $p.Substring($currentFilterParameterName.Length + 1) # Add plus 1 for '=' + # everything matched, however, only add it to the list for the following reasons + # - Status is Accepted and not from AD and a unique value in the list + # - Or From AD and current logic determines it applies - if ($usedAdSettings) { - # can have it apply by build and server parameter - if (($null -eq $entry.MinVersion -or - $FilterServerVersion -ge $entry.MinVersion) -and + if ($usedAdSettings) { + # can have it apply by build and server parameter + if (($null -eq $entry.MinVersion -or + $FilterServerVersion -ge $entry.MinVersion) -and (($null -eq $entry.MaxVersion -or - $FilterServerVersion -le $entry.MaxVersion)) -and + $FilterServerVersion -le $entry.MaxVersion)) -and (($null -eq $entry.Server -or - $entry.Server.ToLower().Contains($adjustedFilterServer)))) { - $status = $entry.Status + $entry.Server -contains $adjustedFilterServer))) { + $status = $entry.Status.ToString() + } else { + $status = "DoesNotApply" + } } else { - $status = "DoesNotApply" + $status = $entry.Status.ToString() } - } else { - $status = $entry.Status - } - if ($status -eq "Accepted" -and + # Add to the list if the status is Accepted, and we do not have that ParameterName yet in the list. + if ($status -eq "Accepted" -and ($results.Count -lt 1 -or - -not ($results.ParameterValue.ToLower().Contains($value.ToLower())))) { - $results.Add([PSCustomObject]@{ - Name = $entry.Name - Reason = $entry.Reason - ModifiedBy = $entry.ModifiedBy - ComponentName = $entry.ComponentName - SectionName = $entry.SectionName - ParameterName = $FilterParameterName - ParameterValue = $value - Status = $entry.Status - TrueStatus = $status - FromAdSettings = $usedAdSettings - }) - } elseif ($status -eq "Accepted") { - Write-Verbose "Already have 1 Accepted value added to list no need to add another one. Skip adding $($entry.Name)" - } else { - Write-Verbose "Already have parameter value added to the. Skip adding $($entry.Name)" + -not ($results.ParameterName -contains $currentFilterParameterName))) { + $results.Add([PSCustomObject]@{ + Name = $entry.Name + Reason = $entry.Reason + ModifiedBy = $entry.ModifiedBy + ComponentName = $entry.ComponentName + SectionName = $entry.SectionName + ParameterName = $currentFilterParameterName + ParameterValue = $value + Status = $entry.Status + TrueStatus = $status + FromAdSettings = $usedAdSettings + }) + } elseif ($status -eq "Accepted") { + Write-Verbose "Already have 1 Accepted value added to list no need to add another one. Skip adding $($entry.Name)" + } else { + Write-Verbose "Already have parameter value added to the list. Skip adding $($entry.Name)" + } } } } From eb7da05d49ff7c7be20228427c89062469f0d0f9 Mon Sep 17 00:00:00 2001 From: David Paulson Date: Mon, 21 Oct 2024 15:26:13 -0500 Subject: [PATCH 5/7] Fixed a bug found within pester --- .../Tests/DataCollection/E19/Exchange/GetSettingOverride.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Diagnostics/HealthChecker/Tests/DataCollection/E19/Exchange/GetSettingOverride.xml b/Diagnostics/HealthChecker/Tests/DataCollection/E19/Exchange/GetSettingOverride.xml index 7a4a2454d4..149fb28a99 100644 --- a/Diagnostics/HealthChecker/Tests/DataCollection/E19/Exchange/GetSettingOverride.xml +++ b/Diagnostics/HealthChecker/Tests/DataCollection/E19/Exchange/GetSettingOverride.xml @@ -28,7 +28,7 @@ System.Object - Enabled=False + Enabled=True @@ -41,7 +41,7 @@ Accepted 3 - <S CN="Cafe" SN="HttpRequestFiltering" MB="exlab.dom/Users/Administrator" R="Testing"><Ps><P>Enabled=False</P></Ps></S> + <S CN="Cafe" SN="HttpRequestFiltering" MB="exlab.dom/Users/Administrator" R="Testing"><Ps><P>Enabled=True</P></Ps></S> From 0db1bdcd98a71f9032d437260f2dbfe8e9cae651 Mon Sep 17 00:00:00 2001 From: David Paulson Date: Mon, 21 Oct 2024 14:49:09 -0500 Subject: [PATCH 6/7] AMSI Request Body Scanner Detection New analyzer to detect body scanner if it is enabled and possibly misconfigured. --- ...Invoke-AnalyzerSecurityAMSIConfigState.ps1 | 92 +++++++++++++++++-- .../Tests/HealthChecker.E16.Main.Tests.ps1 | 2 +- .../Tests/HealthChecker.E19.Main.Tests.ps1 | 2 +- .../HealthChecker/AMSIIntegration.md | 7 ++ 4 files changed, 93 insertions(+), 10 deletions(-) diff --git a/Diagnostics/HealthChecker/Analyzer/Security/Invoke-AnalyzerSecurityAMSIConfigState.ps1 b/Diagnostics/HealthChecker/Analyzer/Security/Invoke-AnalyzerSecurityAMSIConfigState.ps1 index 4402fb9d85..454dd6e348 100644 --- a/Diagnostics/HealthChecker/Analyzer/Security/Invoke-AnalyzerSecurityAMSIConfigState.ps1 +++ b/Diagnostics/HealthChecker/Analyzer/Security/Invoke-AnalyzerSecurityAMSIConfigState.ps1 @@ -33,7 +33,7 @@ function Invoke-AnalyzerSecurityAMSIConfigState { (Test-ExchangeBuildGreaterOrEqualThanBuild -CurrentExchangeBuild $exchangeInformation.BuildInformation.VersionInformation -Version "Exchange2019" -CU "CU10")) -and ($exchangeInformation.GetExchangeServer.IsEdgeServer -eq $false)) { - $params = @{ + $filterSettingOverrideParams = @{ ExchangeSettingOverride = $HealthServerObject.ExchangeInformation.SettingOverrides GetSettingOverride = $HealthServerObject.OrganizationInformation.GetSettingOverride FilterServer = $HealthServerObject.ServerName @@ -44,14 +44,15 @@ function Invoke-AnalyzerSecurityAMSIConfigState { } # Only thing that is returned is Accepted values and unique - [array]$amsiInformation = Get-FilteredSettingOverrideInformation @params - + [array]$amsiInformation = Get-FilteredSettingOverrideInformation @filterSettingOverrideParams $amsiWriteType = "Yellow" - $amsiConfigurationWarning = "`r`n`t`tThis may pose a security risk to your servers`r`n`t`tMore Information: https://aka.ms/HC-AMSIExchange" + $amsiConfigurationWarning = "`r`n`t`tThis may pose a security risk to your servers" + $amsiMoreInfo = "More Information: https://aka.ms/HC-AMSIExchange" + $amsiMoreInformationDisplay = $false $amsiConfigurationUnknown = "Exchange AMSI integration state is unknown" $additionalAMSIDisplayValue = $null - if ($null -eq $amsiInformation) { + if ($amsiInformation.Count -eq 0) { # No results returned, no matches therefore good. $amsiWriteType = "Green" $amsiState = "True" @@ -60,17 +61,17 @@ function Invoke-AnalyzerSecurityAMSIConfigState { } elseif ($amsiInformation.Count -eq 1) { $amsiState = $amsiInformation.ParameterValue if ($amsiInformation.ParameterValue -eq "False") { - $additionalAMSIDisplayValue = "Setting applies to the server" + $amsiConfigurationWarning + $additionalAMSIDisplayValue = "Setting applies to the server" + $amsiConfigurationWarning + "`r`n`t`t" + $amsiMoreInfo } elseif ($amsiInformation.ParameterValue -eq "True") { $amsiWriteType = "Green" } else { $additionalAMSIDisplayValue = $amsiConfigurationUnknown + " - Setting Override Name: $($amsiInformation.Name)" - $additionalAMSIDisplayValue += $amsiConfigurationWarning + $additionalAMSIDisplayValue += $amsiConfigurationWarning + "`r`n`t`t" + $amsiMoreInfo } } else { $amsiState = "Multiple overrides detected" $additionalAMSIDisplayValue = $amsiConfigurationUnknown + " - Multi Setting Overrides Applied: $([string]::Join(", ", $amsiInformation.Name))" - $additionalAMSIDisplayValue += $amsiConfigurationWarning + $additionalAMSIDisplayValue += $amsiConfigurationWarning + "`r`n`t`t" + $amsiMoreInfo } $params = $baseParams + @{ @@ -88,6 +89,81 @@ function Invoke-AnalyzerSecurityAMSIConfigState { } Add-AnalyzedResultInformation @params } + + <# + AMSI Needs to be enabled in order for Request Body Scanning to work. + - If request body scanning is enabled, but AMSI is disabled, call out this misconfiguration + - If request body max size is enabled, if the HTTP request body size is over 1MB regardless if AMSI is enabled, + it will be rejected. + - If request body scanning is enabled and AMSI is enabled, then just show enabled. + #> + + $amsiStateEnabled = "true" -eq $amsiState + $filterSettingOverrideParams.FilterSectionName = "AmsiRequestBodyScanning" + $filterSettingOverrideParams.FilterParameterName = @("EnabledAll", "EnabledApi", "EnabledAutoD", "EnabledEcp", + "EnabledEws", "EnabledMapi", "EnabledEas", "EnabledOab", "EnabledOwa", "EnabledPowerShell", "EnabledOthers") + [array]$amsiRequestBodyScanning = Get-FilteredSettingOverrideInformation @filterSettingOverrideParams + $filterSettingOverrideParams.FilterSectionName = "BlockRequestBodyGreaterThanMaxScanSize" + [array]$amsiBlockRequestBodyGreater = Get-FilteredSettingOverrideInformation @filterSettingOverrideParams + $amsiRequestBodyScanningEnabled = $amsiRequestBodyScanning.Count -gt 0 -and + ($null -ne ($amsiRequestBodyScanning | Where-Object { $_.ParameterValue -eq "True" })) + $amsiBlockRequestBodyEnabled = $amsiBlockRequestBodyGreater.Count -gt 0 -and + ($null -ne ($amsiBlockRequestBodyGreater | Where-Object { $_.ParameterValue -eq "True" })) + $requestBodyDisplayValue = $amsiStateEnabled -and $amsiRequestBodyScanningEnabled + $requestBodyDisplayType = $requestBodySizeBlockDisplayType = "Grey" + $requestBodySizeBlockDisplayValue = $false + + if ($amsiBlockRequestBodyEnabled) { + $requestBodySizeBlockDisplayValue = "$true - WARNING: Requests over 1MB will be blocked." + $requestBodySizeBlockDisplayType = "Yellow" + $amsiMoreInformationDisplay = $true + } + + if ($amsiStateEnabled -eq $false) { + if ($amsiRequestBodyScanningEnabled) { + $requestBodyDisplayValue = "$true - WARNING: AMSI not enabled" + $requestBodyDisplayType = "Yellow" + $amsiMoreInformationDisplay = $true + } + if ($amsiBlockRequestBodyEnabled) { + $requestBodySizeBlockDisplayValue += " AMSI not enabled and this will still be triggered." + $amsiMoreInformationDisplay = $true + } + } + + $params = $baseParams + @{ + Name = "AMSI Request Body Scanning" + Details = $requestBodyDisplayValue + DisplayWriteType = $requestBodyDisplayType + } + Add-AnalyzedResultInformation @params + + $params = $baseParams + @{ + Name = "AMSI Request Body Size Block" + Details = $requestBodySizeBlockDisplayValue + DisplayWriteType = $requestBodySizeBlockDisplayType + } + Add-AnalyzedResultInformation @params + + if (($amsiRequestBodyScanningEnabled -or + $amsiBlockRequestBodyEnabled) -and + -not (Test-ExchangeBuildGreaterOrEqualThanSecurityPatch -CurrentExchangeBuild $exchangeInformation.BuildInformation.VersionInformation -SUName "Nov24SU")) { + $params = $baseParams + @{ + Details = "AMSI Body Scanning Option(s) enabled, but not applicable due to the version of Exchange. Must be on Nov24SU or greater to have this feature enabled." + DisplayCustomTabNumber = 2 + DisplayWriteType = "Yellow" + } + Add-AnalyzedResultInformation @params + } + + if ($amsiMoreInformationDisplay) { + $params = $baseParams + @{ + Details = $amsiMoreInfo + DisplayCustomTabNumber = 2 + DisplayWriteType = "Yellow" + } + Add-AnalyzedResultInformation @params + } } else { Write-Verbose "AMSI integration is not available because we are on: $($exchangeInformation.BuildInformation.MajorVersion) $exchangeCU" } diff --git a/Diagnostics/HealthChecker/Tests/HealthChecker.E16.Main.Tests.ps1 b/Diagnostics/HealthChecker/Tests/HealthChecker.E16.Main.Tests.ps1 index cedad1097e..a80a8798f9 100644 --- a/Diagnostics/HealthChecker/Tests/HealthChecker.E16.Main.Tests.ps1 +++ b/Diagnostics/HealthChecker/Tests/HealthChecker.E16.Main.Tests.ps1 @@ -134,7 +134,7 @@ Describe "Testing Health Checker by Mock Data Imports - Exchange 2016" { TestObjectMatch "Pattern service" "Unreachable`r`n`t`tMore information: https://aka.ms/HelpConnectivityEEMS" -WriteType "Yellow" TestObjectMatch "Telemetry enabled" "False" - $Script:ActiveGrouping.Count | Should -Be 102 + $Script:ActiveGrouping.Count | Should -Be 104 } It "Display Results - Security Vulnerability" { diff --git a/Diagnostics/HealthChecker/Tests/HealthChecker.E19.Main.Tests.ps1 b/Diagnostics/HealthChecker/Tests/HealthChecker.E19.Main.Tests.ps1 index 1ab3021e5b..2cf80475e7 100644 --- a/Diagnostics/HealthChecker/Tests/HealthChecker.E19.Main.Tests.ps1 +++ b/Diagnostics/HealthChecker/Tests/HealthChecker.E19.Main.Tests.ps1 @@ -146,7 +146,7 @@ Describe "Testing Health Checker by Mock Data Imports" { TestObjectMatch "AES256-CBC Protected Content Support" "Not Supported Build" -WriteType "Red" TestObjectMatch "SerializedDataSigning Enabled" "Unsupported Version" -WriteType "Red" - $Script:ActiveGrouping.Count | Should -Be 85 + $Script:ActiveGrouping.Count | Should -Be 87 } It "Display Results - Security Vulnerability" { diff --git a/docs/Diagnostics/HealthChecker/AMSIIntegration.md b/docs/Diagnostics/HealthChecker/AMSIIntegration.md index ce17a7f7be..69e994dea2 100644 --- a/docs/Diagnostics/HealthChecker/AMSIIntegration.md +++ b/docs/Diagnostics/HealthChecker/AMSIIntegration.md @@ -17,6 +17,12 @@ This check verifies if an override exists which disables the AMSI integration wi `Get-SettingOverride | Where-Object { ($_.ComponentName -eq "Cafe") -and ($_.SectionName -eq "HttpRequestFiltering") }` +AMSI Body Scanning Feature, was introduced in Exchange Server November 2024 Security Update. This is disabled by default and can be enabled with a New-SettingOverride cmdlet. In order to properly function, it does require that AMSI is enabled as well. There will be a configuration issue/warning for the following scenarios: +- Body Scanning is enabled, but AMSI is disabled +- Block Request Greater than Max scan size is configured +- Body Scanning is enabled, but not on the correct version to have the setting applicable + + **Included in HTML Report?** Yes @@ -27,3 +33,4 @@ Yes [More about AMSI integration with Exchange Server](https://techcommunity.microsoft.com/t5/exchange-team-blog/more-about-amsi-integration-with-exchange-server/ba-p/2572371) +[Exchange Server AMSI Integration](https://learn.microsoft.com/Exchange/antispam-and-antimalware/amsi-integration-with-exchange) From 9f116afd5b6053eb40036b295780a3b239c853cc Mon Sep 17 00:00:00 2001 From: David Paulson Date: Mon, 28 Oct 2024 12:55:53 -0500 Subject: [PATCH 7/7] Add CVE-2024-49040 check --- .../Invoke-AnalyzerSecurityCve-2024-49040.ps1 | 55 +++++++++++++++++++ .../Invoke-AnalyzerSecurityCveCheck.ps1 | 2 + .../Tests/HealthChecker.E16.Main.Tests.ps1 | 4 +- .../Tests/HealthChecker.E19.Main.Tests.ps1 | 2 +- 4 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 Diagnostics/HealthChecker/Analyzer/Security/Invoke-AnalyzerSecurityCve-2024-49040.ps1 diff --git a/Diagnostics/HealthChecker/Analyzer/Security/Invoke-AnalyzerSecurityCve-2024-49040.ps1 b/Diagnostics/HealthChecker/Analyzer/Security/Invoke-AnalyzerSecurityCve-2024-49040.ps1 new file mode 100644 index 0000000000..56d45e2065 --- /dev/null +++ b/Diagnostics/HealthChecker/Analyzer/Security/Invoke-AnalyzerSecurityCve-2024-49040.ps1 @@ -0,0 +1,55 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +. $PSScriptRoot\..\Add-AnalyzedResultInformation.ps1 +function Invoke-AnalyzerSecurityCve-2024-49040 { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [ref]$AnalyzeResults, + + [Parameter(Mandatory = $true)] + [object]$SecurityObject, + + [Parameter(Mandatory = $true)] + [object]$DisplayGroupingKey + ) + begin { + Write-Verbose "Calling: $($MyInvocation.MyCommand)" + $exchangeInformation = $SecurityObject.ExchangeInformation + $organizationInformation = $SecurityObject.OrgInformation + $exchangeBuild = $exchangeInformation.BuildInformation.VersionInformation.BuildVersion + # cSpell:disable + # Need to disable cSpell because this is the name of the override + $filterParameterName = "AddDisclaimerforRegexMatch" + # cSpell:enable + } + process { + $params = @{ + ExchangeSettingOverride = $exchangeInformation.SettingOverrides + GetSettingOverride = $organizationInformation.GetSettingOverride + FilterServer = $exchangeInformation.GetExchangeServer.Name + FilterServerVersion = $exchangeBuild + FilterComponentName = "Transport" + FilterSectionName = "NonCompliantSenderSettings" + FilterParameterName = $filterParameterName + } + [array]$nonCompliantSenderSettings = Get-FilteredSettingOverrideInformation @params + + $overrideDisabled = $nonCompliantSenderSettings.Count -gt 0 -and + ($null -ne ($nonCompliantSenderSettings | Where-Object { $_.ParameterValue -eq "false" })) + $isSuApplied = (Test-ExchangeBuildGreaterOrEqualThanSecurityPatch -CurrentExchangeBuild $SecurityObject.BuildInformation -SUName "Nov24SU") + + if (-not $isSuApplied -or $overrideDisabled) { + $params = @{ + AnalyzedInformation = $AnalyzeResults + DisplayGroupingKey = $DisplayGroupingKey + Name = "Security Vulnerability" + Details = ("{0} - Override Is Set: $overrideDisabled`r`n`t`tSee: https://portal.msrc.microsoft.com/security-guidance/advisory/{0} for more information." -f "CVE-2024-49040") + DisplayWriteType = "Red" + DisplayTestingValue = "CVE-2024-49040" + } + Add-AnalyzedResultInformation @params + } + } +} diff --git a/Diagnostics/HealthChecker/Analyzer/Security/Invoke-AnalyzerSecurityCveCheck.ps1 b/Diagnostics/HealthChecker/Analyzer/Security/Invoke-AnalyzerSecurityCveCheck.ps1 index c4bb99ed89..ea60b6a001 100644 --- a/Diagnostics/HealthChecker/Analyzer/Security/Invoke-AnalyzerSecurityCveCheck.ps1 +++ b/Diagnostics/HealthChecker/Analyzer/Security/Invoke-AnalyzerSecurityCveCheck.ps1 @@ -9,6 +9,7 @@ . $PSScriptRoot\Invoke-AnalyzerSecurityCve-2022-21978.ps1 . $PSScriptRoot\Invoke-AnalyzerSecurityCve-2023-36434.ps1 . $PSScriptRoot\Invoke-AnalyzerSecurityCveAddressedBySerializedDataSigning.ps1 +. $PSScriptRoot\Invoke-AnalyzerSecurityCve-2024-49040.ps1 . $PSScriptRoot\Invoke-AnalyzerSecurityCve-MarchSuSpecial.ps1 . $PSScriptRoot\Invoke-AnalyzerSecurityExtendedProtectionConfigState.ps1 . $PSScriptRoot\Invoke-AnalyzerSecurityIISModules.ps1 @@ -211,6 +212,7 @@ function Invoke-AnalyzerSecurityCveCheck { Invoke-AnalyzerSecurityCveAddressedBySerializedDataSigning -AnalyzeResults $AnalyzeResults -SecurityObject $securityObject -DisplayGroupingKey $DisplayGroupingKey Invoke-AnalyzerSecurityCve-MarchSuSpecial -AnalyzeResults $AnalyzeResults -SecurityObject $securityObject -DisplayGroupingKey $DisplayGroupingKey Invoke-AnalyzerSecurityADV24199947 -AnalyzeResults $AnalyzeResults -SecurityObject $securityObject -DisplayGroupingKey $DisplayGroupingKey + Invoke-AnalyzerSecurityCve-2024-49040 -AnalyzeResults $AnalyzeResults -SecurityObject $securityObject -DisplayGroupingKey $DisplayGroupingKey # Make sure that these stay as the last one to keep the output more readable Invoke-AnalyzerSecurityExtendedProtectionConfigState -AnalyzeResults $AnalyzeResults -SecurityObject $securityObject -DisplayGroupingKey $DisplayGroupingKey } diff --git a/Diagnostics/HealthChecker/Tests/HealthChecker.E16.Main.Tests.ps1 b/Diagnostics/HealthChecker/Tests/HealthChecker.E16.Main.Tests.ps1 index a80a8798f9..5115bc7cd3 100644 --- a/Diagnostics/HealthChecker/Tests/HealthChecker.E16.Main.Tests.ps1 +++ b/Diagnostics/HealthChecker/Tests/HealthChecker.E16.Main.Tests.ps1 @@ -144,11 +144,11 @@ Describe "Testing Health Checker by Mock Data Imports - Exchange 2016" { $cveTests.Contains("CVE-2020-1147") | Should -Be $true $cveTests.Contains("CVE-2023-36039") | Should -Be $true $cveTests.Contains("ADV24199947") | Should -Be $true - $cveTests.Count | Should -Be 51 + $cveTests.Count | Should -Be 52 $downloadDomains = GetObject "CVE-2021-1730" $downloadDomains.DownloadDomainsEnabled | Should -Be "false" - $Script:ActiveGrouping.Count | Should -Be 58 + $Script:ActiveGrouping.Count | Should -Be 59 } } diff --git a/Diagnostics/HealthChecker/Tests/HealthChecker.E19.Main.Tests.ps1 b/Diagnostics/HealthChecker/Tests/HealthChecker.E19.Main.Tests.ps1 index 2cf80475e7..723a8253e8 100644 --- a/Diagnostics/HealthChecker/Tests/HealthChecker.E19.Main.Tests.ps1 +++ b/Diagnostics/HealthChecker/Tests/HealthChecker.E19.Main.Tests.ps1 @@ -157,7 +157,7 @@ Describe "Testing Health Checker by Mock Data Imports" { $cveTests.Contains("CVE-2023-36434") | Should -Be $true $cveTests.Contains("CVE-2023-36039") | Should -Be $true $cveTests.Contains("ADV24199947") | Should -Be $true - $cveTests.Count | Should -Be 51 + $cveTests.Count | Should -Be 52 $downloadDomains = GetObject "CVE-2021-1730" $downloadDomains.DownloadDomainsEnabled | Should -Be "False" TestObjectMatch "Extended Protection Vulnerable" "True" -WriteType "Red"