Skip to content

Commit

Permalink
Merge pull request #1879 from microsoft/main
Browse files Browse the repository at this point in the history
Nov SU Release
  • Loading branch information
bill-long authored Nov 14, 2023
2 parents 238eacb + f9d9d57 commit 34ee62c
Show file tree
Hide file tree
Showing 8 changed files with 330 additions and 98 deletions.
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
}
}
}
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"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
. $PSScriptRoot\Invoke-AnalyzerSecurityCve-2021-34470.ps1
. $PSScriptRoot\Invoke-AnalyzerSecurityCve-2022-21978.ps1
. $PSScriptRoot\Invoke-AnalyzerSecurityCve-2023-36434.ps1
. $PSScriptRoot\Invoke-AnalyzerSecurityCveAddressedBySerializedDataSigning.ps1
. $PSScriptRoot\Invoke-AnalyzerSecurityCve-MarchSuSpecial.ps1
. $PSScriptRoot\Invoke-AnalyzerSecurityExtendedProtectionConfigState.ps1
. $PSScriptRoot\Invoke-AnalyzerSecurityIISModules.ps1
Expand Down Expand Up @@ -130,6 +131,7 @@ function Invoke-AnalyzerSecurityCveCheck {
"Jun23SU" = (NewCveEntry @("CVE-2023-28310", "CVE-2023-32031") @($ex2016, $ex2019))
"Aug23SU" = (NewCveEntry @("CVE-2023-38181", "CVE-2023-38182", "CVE-2023-38185", "CVE-2023-35368", "CVE-2023-35388", "CVE-2023-36777", "CVE-2023-36757", "CVE-2023-36756", "CVE-2023-36745", "CVE-2023-36744") @($ex2016, $ex2019))
"Oct23SU" = (NewCveEntry @("CVE-2023-36778") @($ex2016, $ex2019))
"Nov23SU" = (NewCveEntry @("CVE-2023-36050", "CVE-2023-36039", "CVE-2023-36035", "CVE-2023-36439") @($ex2016, $ex2019))
}

# Need to organize the list so oldest CVEs come out first.
Expand Down Expand Up @@ -204,6 +206,7 @@ function Invoke-AnalyzerSecurityCveCheck {
Invoke-AnalyzerSecurityCve-2021-34470 -AnalyzeResults $AnalyzeResults -SecurityObject $securityObject -DisplayGroupingKey $DisplayGroupingKey
Invoke-AnalyzerSecurityCve-2022-21978 -AnalyzeResults $AnalyzeResults -SecurityObject $securityObject -DisplayGroupingKey $DisplayGroupingKey
Invoke-AnalyzerSecurityCve-2023-36434 -AnalyzeResults $AnalyzeResults -SecurityObject $securityObject -DisplayGroupingKey $DisplayGroupingKey
Invoke-AnalyzerSecurityCveAddressedBySerializedDataSigning -AnalyzeResults $AnalyzeResults -SecurityObject $securityObject -DisplayGroupingKey $DisplayGroupingKey
Invoke-AnalyzerSecurityCve-MarchSuSpecial -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
Expand Down
Loading

0 comments on commit 34ee62c

Please sign in to comment.