-
Notifications
You must be signed in to change notification settings - Fork 346
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1879 from microsoft/main
Nov SU Release
- Loading branch information
Showing
8 changed files
with
330 additions
and
98 deletions.
There are no files selected for viewing
114 changes: 114 additions & 0 deletions
114
Diagnostics/HealthChecker/Analyzer/Security/Get-SerializedDataSigningState.ps1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# Copyright (c) Microsoft Corporation. | ||
# Licensed under the MIT License. | ||
|
||
. $PSScriptRoot\..\Get-FilteredSettingOverrideInformation.ps1 | ||
. $PSScriptRoot\..\..\Helpers\CompareExchangeBuildLevel.ps1 | ||
# Used to determine the state of the Serialized Data Signing on the server. | ||
function Get-SerializedDataSigningState { | ||
[CmdletBinding()] | ||
param( | ||
[Parameter(Mandatory = $true, ParameterSetName = "HealthServerObject")] | ||
[object]$HealthServerObject, | ||
|
||
[Parameter(Mandatory = $true, ParameterSetName = "SecurityObject")] | ||
[object]$SecurityObject | ||
) | ||
begin { | ||
<# | ||
SerializedDataSigning was introduced with the January 2023 Exchange Server Security Update | ||
In the first release of the feature, it was disabled by default. | ||
After November 2023 Exchange Server Security Update, it was enabled by default. | ||
Jan23SU thru Nov23SU | ||
- Exchange 2016/2019 > Feature must be enabled via New-SettingOverride | ||
- Exchange 2013 > Feature must be enabled via EnableSerializationDataSigning registry value | ||
Nov23SU + | ||
- Exchange 2016/2019 > Feature is enabled by default, but can be disabled by New-SettingOverride. | ||
Note: | ||
If the registry value is set on E16/E19, it will be ignored. | ||
Same goes for the SettingOverride set on E15 - it will be ignored and the feature remains off until the registry value is set. | ||
#> | ||
Write-Verbose "Calling: $($MyInvocation.MyCommand)" | ||
Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)" | ||
|
||
if ($PSCmdlet.ParameterSetName -eq "HealthServerObject") { | ||
$exchangeInformation = $HealthServerObject.ExchangeInformation | ||
$getSettingOverride = $HealthServerObject.OrganizationInformation.GetSettingOverride | ||
} else { | ||
$exchangeInformation = $SecurityObject.ExchangeInformation | ||
$getSettingOverride = $SecurityObject.OrgInformation.GetSettingOverride | ||
} | ||
|
||
$additionalInformation = [string]::Empty | ||
$serializedDataSigningEnabled = $false | ||
$supportedRole = $exchangeInformation.GetExchangeServer.IsEdgeServer -eq $false | ||
$supportedVersion = (Test-ExchangeBuildGreaterOrEqualThanSecurityPatch -CurrentExchangeBuild $exchangeInformation.BuildInformation.VersionInformation -SUName "Jan23SU") | ||
$enabledByDefaultVersion = (Test-ExchangeBuildGreaterOrEqualThanSecurityPatch -CurrentExchangeBuild $exchangeInformation.BuildInformation.VersionInformation -SUName "Nov23SU") | ||
$filterServer = $exchangeInformation.GetExchangeServer.Name | ||
$exchangeBuild = $exchangeInformation.BuildInformation.VersionInformation.BuildVersion | ||
Write-Verbose "Reviewing settings against build: $exchangeBuild" | ||
} process { | ||
|
||
if ($supportedVersion -and | ||
$supportedRole) { | ||
Write-Verbose "SerializedDataSigning is available on this Exchange role / version build combination" | ||
|
||
if ($exchangeBuild -ge "15.1.0.0") { | ||
Write-Verbose "Checking SettingOverride for SerializedDataSigning configuration state" | ||
$params = @{ | ||
ExchangeSettingOverride = $exchangeInformation.SettingOverrides | ||
GetSettingOverride = $getSettingOverride | ||
FilterServer = $filterServer | ||
FilterServerVersion = $exchangeBuild | ||
FilterComponentName = "Data" | ||
FilterSectionName = "EnableSerializationDataSigning" | ||
FilterParameterName = "Enabled" | ||
} | ||
|
||
[array]$serializedDataSigningSettingOverride = Get-FilteredSettingOverrideInformation @params | ||
|
||
if ($null -eq $serializedDataSigningSettingOverride) { | ||
Write-Verbose "No Setting Override Found" | ||
$serializedDataSigningEnabled = $enabledByDefaultVersion | ||
} elseif ($serializedDataSigningSettingOverride.Count -eq 1) { | ||
$stateValue = $serializedDataSigningSettingOverride.ParameterValue | ||
|
||
if ($stateValue -eq "False") { | ||
$additionalInformation = "SerializedDataSigning is explicitly disabled" | ||
Write-Verbose $additionalInformation | ||
} elseif ($stateValue -eq "True") { | ||
Write-Verbose "SerializedDataSigning is explicitly enabled" | ||
$serializedDataSigningEnabled = $true | ||
} else { | ||
Write-Verbose "Unknown value provided" | ||
$additionalInformation = "SerializedDataSigning is unknown" | ||
} | ||
} else { | ||
Write-Verbose "Multi overrides detected" | ||
$additionalInformation = "SerializedDataSigning is unknown - Multi Setting Overrides Applied: $([string]::Join(", ", $serializedDataSigningSettingOverride.Name))" | ||
} | ||
} else { | ||
Write-Verbose "Checking Registry Value for SerializedDataSigning configuration state" | ||
|
||
if ($exchangeInformation.RegistryValues.SerializedDataSigning -eq 1) { | ||
$serializedDataSigningEnabled = $true | ||
Write-Verbose "SerializedDataSigning enabled via Registry Value" | ||
} else { | ||
Write-Verbose "SerializedDataSigning not configured or explicitly disabled via Registry Value" | ||
} | ||
} | ||
} else { | ||
Write-Verbose "SerializedDataSigning isn't available because we are on role: $($exchangeInformation.BuildInformation.ServerRole) build: $exchangeBuild" | ||
} | ||
} end { | ||
return [PSCustomObject]@{ | ||
Enabled = $serializedDataSigningEnabled | ||
SupportedVersion = $supportedVersion | ||
SupportedRole = $supportedRole | ||
EnabledByDefaultVersion = $enabledByDefaultVersion | ||
AdditionalInformation = $additionalInformation | ||
} | ||
} | ||
} |
164 changes: 164 additions & 0 deletions
164
...hChecker/Analyzer/Security/Invoke-AnalyzerSecurityCveAddressedBySerializedDataSigning.ps1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
# Copyright (c) Microsoft Corporation. | ||
# Licensed under the MIT License. | ||
|
||
. $PSScriptRoot\Get-SerializedDataSigningState.ps1 | ||
. $PSScriptRoot\..\Add-AnalyzedResultInformation.ps1 | ||
function Invoke-AnalyzerSecurityCveAddressedBySerializedDataSigning { | ||
[CmdletBinding()] | ||
param( | ||
[Parameter(Mandatory = $true)] | ||
[ref]$AnalyzeResults, | ||
|
||
[Parameter(Mandatory = $true)] | ||
[object]$SecurityObject, | ||
|
||
[Parameter(Mandatory = $true)] | ||
[object]$DisplayGroupingKey | ||
) | ||
|
||
<# | ||
Description: Check for vulnerabilities that are addressed by turning serialized data signing for PowerShell payload on | ||
Affected Exchange versions: 2016, 2019 | ||
Fix: Enable Serialized Data Signing for PowerShell payload if disabled or install Exchange update if running an unsupported build | ||
#> | ||
|
||
begin { | ||
Write-Verbose "Calling: $($MyInvocation.MyCommand)" | ||
function NewCveFixedBySDSObject { | ||
param() | ||
|
||
begin { | ||
Write-Verbose "Calling: $($MyInvocation.MyCommand)" | ||
$cveList = New-Object 'System.Collections.Generic.List[object]' | ||
|
||
# Add all CVE that are addressed by turning Serialized Data Signing for PowerShell payload on | ||
# Add true or false as an indicator as some fixes needs to be done via code fix + SDS on | ||
$cveFixedBySDS = @( | ||
"CVE-2023-36050, $true", | ||
"CVE-2023-36039, $true", | ||
"CVE-2023-36035, $true", | ||
"CVE-2023-36439, $true") | ||
} process { | ||
foreach ($cve in $cveFixedBySDS) { | ||
$entry = $($cve.Split(",")[0]).Trim() | ||
$fixIndicator = $($cve.Split(",")[1]).Trim() | ||
$cveList.Add([PSCustomObject]@{ | ||
CVE = $entry | ||
CodeFixRequired = $fixIndicator | ||
}) | ||
} | ||
} end { | ||
return $cveList | ||
} | ||
} | ||
|
||
function FindCveEntryInAnalyzeResults { | ||
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Value is used')] | ||
param ( | ||
[Parameter(Mandatory = $true)] | ||
[ref]$AnalyzeResults, | ||
|
||
[Parameter(Mandatory = $true)] | ||
[string]$CVE, | ||
|
||
[Parameter(Mandatory = $false)] | ||
[switch]$RemoveWhenFound | ||
) | ||
|
||
begin { | ||
Write-Verbose "Calling: $($MyInvocation.MyCommand)" | ||
|
||
$key = $null | ||
$cveFound = $false | ||
} process { | ||
($AnalyzeResults.Value.DisplayResults.Values | Where-Object { | ||
# Find the 'Security Vulnerability' section | ||
($_.Name -eq "Security Vulnerability") | ||
}) | ForEach-Object { | ||
if ($_.CustomValue -match $CVE) { | ||
# Loop through each entry and check if the value is equal the CVE that we're looking for | ||
Write-Verbose ("$CVE was found in the CVE list!") | ||
$key = $_ | ||
} | ||
} | ||
|
||
$cveFound = ($null -ne $key) | ||
|
||
if ($RemoveWhenFound -and | ||
$cveFound) { | ||
# Remove the entry if found and if RemovedWhenFound parameter was used | ||
Write-Verbose ("Removing $CVE from the list") | ||
$AnalyzeResults.Value.DisplayResults.Values.Remove($key) | ||
} | ||
} end { | ||
Write-Verbose ("Was $CVE found in the list? $cveFound") | ||
return $cveFound | ||
} | ||
} | ||
|
||
$params = @{ | ||
AnalyzedInformation = $AnalyzeResults | ||
DisplayGroupingKey = $DisplayGroupingKey | ||
Name = "Security Vulnerability" | ||
DisplayWriteType = "Red" | ||
} | ||
|
||
$detailsString = "{0}`r`n`t`tSee: https://portal.msrc.microsoft.com/security-guidance/advisory/{0} for more information." | ||
|
||
$getSerializedDataSigningState = Get-SerializedDataSigningState -SecurityObject $SecurityObject | ||
$cveFixedBySerializedDataSigning = NewCveFixedBySDSObject | ||
} | ||
process { | ||
if ($getSerializedDataSigningState.SupportedRole -ne $false) { | ||
if ($cveFixedBySerializedDataSigning.Count -ge 1) { | ||
Write-Verbose ("Testing CVEs: {0}" -f [string]::Join(", ", $cveFixedBySerializedDataSigning.CVE)) | ||
|
||
if (($getSerializedDataSigningState.SupportedVersion) -and | ||
($getSerializedDataSigningState.Enabled)) { | ||
Write-Verbose ("Serialized Data Signing is supported and enabled - removing any CVE that is mitigated by this feature") | ||
|
||
foreach ($entry in $cveFixedBySerializedDataSigning) { | ||
$buildIsVulnerable = $null | ||
# If we find it on the AnalyzedResults list, it means that the build is outdated and as a result vulnerable | ||
$buildIsVulnerable = FindCveEntryInAnalyzeResults -AnalyzeResults $AnalyzeResults -CVE $($entry.CVE) | ||
if ($entry.CodeFixRequired -and | ||
$buildIsVulnerable) { | ||
# SDS is configured but there is a code change required that comes as part of a newer Exchange build. | ||
# We consider this version as vulnerable since it's running an outdated build. | ||
Write-Verbose ("To be fully protected against this vulnerability, a fixed Exchange build is required") | ||
} elseif (($entry.CodeFixRequired -eq $false) -and | ||
($buildIsVulnerable)) { | ||
# SDS is configured as expected and there is no code change required. | ||
# We consider this combination as secure since the Exchange build was vulnerable but SDS mitigates. | ||
Write-Verbose ("CVE was on this list but was removed since SDS mitigates the vulnerability") | ||
FindCveEntryInAnalyzeResults -AnalyzeResults $AnalyzeResults -CVE $($entry.CVE) -RemoveWhenFound | ||
} else { | ||
# We end up here if build is not vulnerable | ||
Write-Verbose ("CVE wasn't on the list - system seems not to be vulnerable") | ||
} | ||
} | ||
} elseif (($getSerializedDataSigningState.SupportedVersion -eq $false) -or | ||
($getSerializedDataSigningState.Enabled -eq $false)) { | ||
|
||
foreach ($entry in $cveFixedBySerializedDataSigning) { | ||
Write-Verbose ("System is vulnerable to: $($entry.CVE)") | ||
|
||
if ((FindCveEntryInAnalyzeResults -AnalyzeResults $AnalyzeResults -CVE $($entry.CVE)) -eq $false) { | ||
Write-Verbose ("CVE wasn't found in the results list and will be added now as it requires SDS to be mitigated") | ||
$params.Details = $detailsString -f $($entry.CVE) | ||
$params.DisplayTestingValue = $($entry.CVE) | ||
Add-AnalyzedResultInformation @params | ||
} else { | ||
# We end up here in case the CVE is already on the list | ||
Write-Verbose ("CVE is already on the results list") | ||
} | ||
} | ||
} | ||
} else { | ||
Write-Verbose "There are no vulnerabilities that have been addressed by enabling serialized data signing" | ||
} | ||
} else { | ||
Write-Verbose "Exchange server role is not affected by these vulnerabilities" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.