Skip to content

Commit

Permalink
Merge pull request #1819 from microsoft/dpaul-TokenCacheModuleUpdate
Browse files Browse the repository at this point in the history
Token Cache Module Update
  • Loading branch information
dpaulson45 authored Aug 29, 2023
2 parents 1a61792 + d2c18cf commit 0c1572b
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 264 deletions.
124 changes: 85 additions & 39 deletions Security/src/CVE-2023-21709/CVE-2023-21709.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
.SYNOPSIS
This script removes the TokenCacheModule from IIS to protect Exchange Server against CVE-2023-21709.
.DESCRIPTION
The script removes the TokenCacheModule from IIS to protect Exchange Server against CVE-2023-21709.
The script removes the TokenCacheModule from IIS to protect Exchange Server against CVE-2023-21709.
It comes with a parameter that allows you to explicitly specify a subset of Exchange servers on which the TokenCacheModule
should be removed or restored (ExchangeServerNames).
It's also possible to exclude a subset of Exchange servers from the operation performed by the script (SkipExchangeServerNames).
Expand All @@ -14,7 +14,11 @@
.PARAMETER SkipExchangeServerNames
Use this parameter to explicitly exclude Exchange servers from removing or restoring the TokenCacheModule.
.PARAMETER Rollback
Use this parameter rollback the CVE-2023-21709 solution and add the TokenCacheModule back to IIS.
Use this parameter rollback the CVE-2023-21709 configuration and add the TokenCacheModule back to IIS.
.PARAMETER ScriptUpdateOnly
This optional parameter allows you to only update the script without performing any other actions.
.PARAMETER SkipVersionCheck
This optional parameter allows you to skip the automatic version check and script update.
.EXAMPLE
PS C:\> .\CVE-2023-21709.ps1
It will remove the TokenCacheModule from all of the Exchange servers in the organization.
Expand All @@ -28,67 +32,101 @@
PS C:\> .\CVE-2023-21709.ps1 -Rollback
It will restore the TokenCacheModule on all Exchange servers within the organization.
#>
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]

[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $true, ConfirmImpact = 'High')]
param(
[Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = "Default")]
[Alias("Name", "Fqdn")]
[string[]]$ExchangeServerNames = $null,

[Parameter(Mandatory = $false, ParameterSetName = "Default")]
[string[]]$SkipExchangeServerNames = $null,

[switch]$Rollback
[Parameter(Mandatory = $false, ParameterSetName = "Default")]
[switch]$Rollback,

[Parameter(Mandatory = $false, ParameterSetName = "ScriptUpdateOnly")]
[switch]$ScriptUpdateOnly,

[Parameter(Mandatory = $false, ParameterSetName = "Default")]
[switch]$SkipVersionCheck
)

begin {
$BuildVersion = ""

. $PSScriptRoot\WriteFunctions.ps1
. $PSScriptRoot\ConfigurationAction\Invoke-ConfigureMitigation.ps1
. $PSScriptRoot\ConfigurationAction\Invoke-Rollback.ps1
. $PSScriptRoot\ConfigurationAction\Invoke-TokenCacheModuleAction.ps1
. $PSScriptRoot\..\..\..\Shared\OutputOverrides\Write-Host.ps1
. $PSScriptRoot\..\..\..\Shared\OutputOverrides\Write-Progress.ps1
. $PSScriptRoot\..\..\..\Shared\OutputOverrides\Write-Verbose.ps1
. $PSScriptRoot\..\..\..\Shared\OutputOverrides\Write-Warning.ps1
. $PSScriptRoot\..\..\..\Shared\ScriptUpdateFunctions\Test-ScriptVersion.ps1
. $PSScriptRoot\..\..\..\Shared\Confirm-Administrator.ps1
. $PSScriptRoot\..\..\..\Shared\Confirm-ExchangeShell.ps1
. $PSScriptRoot\..\..\..\Shared\LoggerFunctions.ps1
. $PSScriptRoot\..\..\..\Shared\Out-Columns.ps1
. $PSScriptRoot\..\..\..\Shared\Show-Disclaimer.ps1

$Script:Logger = Get-NewLoggerInstance -LogName "CVE-2023-21709-$((Get-Date).ToString("yyyyMMddhhmmss"))-Debug" `
-AppendDateTimeToFileName $false `
-ErrorAction SilentlyContinue
function Write-VerboseLog ($Message) {
$Script:Logger = $Script:Logger | Write-LoggerInstance $Message
}

function Write-HostLog ($Message) {
$Script:Logger = $Script:Logger | Write-LoggerInstance $Message
}

$loggerInstanceParams = @{
LogName = "CVE-2023-21709-$((Get-Date).ToString("yyyyMMddhhmmss"))-Debug"
AppendDateTimeToFileName = $false
ErrorAction = "SilentlyContinue"
}

$Script:Logger = Get-NewLoggerInstance @loggerInstanceParams

SetWriteHostAction ${Function:Write-HostLog}
SetWriteVerboseAction ${Function:Write-VerboseLog}
SetWriteWarningAction ${Function:Write-HostLog}
SetWriteProgressAction ${Function:Write-HostLog}

$exchangeServersToProcess = New-Object "System.Collections.Generic.List[string]"
} process {
if ($null -ne $ExchangeServerNames) {
Write-Verbose ("Adding server(s): $([string]::Join(", ", $ExchangeServerNames)) to the list of servers to be processed...")
$exchangeServersToProcess.AddRange($ExchangeServerNames)
} else {
Write-Verbose ("No server was passed via the ExchangeServerNames parameter")
}
} end {
if (-not (Confirm-Administrator)) {
Write-Warning "The script needs to be executed in elevated mode. Start the shell as an Administrator."
if (-not(Confirm-Administrator)) {
Write-Host "The script needs to be executed in elevated mode. Start the PowerShell as an administrator." -ForegroundColor Yellow
exit
}

Write-Host ("CVE-2023-21709 script version $($BuildVersion)") -ForegroundColor Green
$versionsUrl = "https://aka.ms/CVE-2023-21709-VersionsUrl"
if (Test-ScriptVersion -AutoUpdate -VersionsUrl $versionsUrl -Confirm:$false) {
Write-Host ("CVE-2023-21709 script version $($BuildVersion)") -ForegroundColor Green

if ($ScriptUpdateOnly) {
switch (Test-ScriptVersion -AutoUpdate -VersionsUrl $versionsUrl -Confirm:$false) {
($true) { Write-Host ("Script was successfully updated") -ForegroundColor Green }
($false) { Write-Host ("No update of the script performed") -ForegroundColor Yellow }
default { Write-Host ("Unable to perform ScriptUpdateOnly operation") -ForegroundColor Red }
}
return
}

if ((-not($SkipVersionCheck)) -and
(Test-ScriptVersion -AutoUpdate -VersionsUrl $versionsUrl -Confirm:$false)) {
Write-Host ("Script was updated. Please re-run the command") -ForegroundColor Yellow
return
}

$exchangeShell = Confirm-ExchangeShell
if (-not($exchangeShell.ShellLoaded)) {
Write-Warning "Failed to load the Exchange Management Shell. Start the script using the Exchange Management Shell."
exit
} elseif (-not($exchangeShell.EMS)) {
Write-Warning "This script requires to be run inside of Exchange Management Shell. Please run on an Exchange Management Server or an Exchange Server with Exchange Management Shell."
Write-Host "Failed to load the Exchange Management Shell. Start the script using the Exchange Management Shell." -ForegroundColor Yellow
exit
}

try {
$iisAppPoolWording = "Note that each Exchange server's IIS Application Pool will be restarted after either applying the setting change or restore action."
$vulnerabilityMoreInformationWording = "More information about the vulnerability can be found here: https://portal.msrc.microsoft.com/security-guidance/advisory/CVE-2023-21709."
if (-not $Rollback) {
if (-not($Rollback)) {
$params = @{
Message = "Display Warning about TokenCacheModule removal operation"
Target = "Removal of TokenCacheModule from IIS is recommended for security reasons. " +
Expand All @@ -110,37 +148,45 @@ begin {
}
Show-Disclaimer @params

Write-Verbose ("Running Get-ExchangeServer to get list of all exchange servers")
Write-Verbose ("Running Get-ExchangeServer to get list of all Exchange servers")
Set-ADServerSettings -ViewEntireForest $true
$ExchangeServers = Get-ExchangeServer | Where-Object { $_.AdminDisplayVersion -like "Version 15*" -and $_.ServerRole -ne "Edge" }

if ($null -ne $ExchangeServerNames -and $ExchangeServerNames.Count -gt 0) {
Write-Verbose "Running only on servers: $([string]::Join(", " ,$ExchangeServerNames))"
$ExchangeServers = $ExchangeServers | Where-Object { ($_.Name -in $ExchangeServerNames) -or ($_.FQDN -in $ExchangeServerNames) }
$ExchangeServers = Get-ExchangeServer | Where-Object {
(($_.AdminDisplayVersion -like "Version 15*") -and
($_.ServerRole -ne "Edge"))
}

if ($null -ne $SkipExchangeServerNames -and $SkipExchangeServerNames.Count -gt 0) {
Write-Verbose "Skipping servers: $([string]::Join(", ", $SkipExchangeServerNames))"
if (($null -ne $exchangeServersToProcess) -and
($exchangeServersToProcess.Count -gt 0)) {
Write-Host "Running only on Exchange servers: $([string]::Join(", " ,$exchangeServersToProcess))"
$ExchangeServers = $ExchangeServers | Where-Object {
(($_.Name -in $exchangeServersToProcess) -or
($_.FQDN -in $exchangeServersToProcess))
}
}

if (($null -ne $SkipExchangeServerNames) -and
($SkipExchangeServerNames.Count -gt 0)) {
Write-Host "Skipping Exchange servers: $([string]::Join(", ", $SkipExchangeServerNames))"
# Remove all the servers present in the SkipExchangeServerNames list
$ExchangeServers = $ExchangeServers | Where-Object { ($_.Name -notin $SkipExchangeServerNames) -and ($_.FQDN -notin $SkipExchangeServerNames) }
$ExchangeServers = $ExchangeServers | Where-Object {
(($_.Name -notin $SkipExchangeServerNames) -and
($_.FQDN -notin $SkipExchangeServerNames))
}
}

if ($null -eq $ExchangeServers) {
Write-Host "No exchange servers to process. Please specify server filters correctly"
Write-Host "No Exchange servers to process. Please specify server filters correctly" -ForegroundColor Red
exit
}

if ($Rollback) {
Invoke-Rollback -ExchangeServers $ExchangeServers
return
$tokenCacheActionParams = @{
ExchangeServers = $ExchangeServers
Action = if (-not($Rollback)) { "Protect" } else { "Rollback" }
}

Invoke-ConfigureMitigation -ExchangeServers $ExchangeServers
return
Invoke-TokenCacheModuleAction @tokenCacheActionParams
} finally {
Write-Host ""
Write-Host "Script Completed successfully!"
Write-Host "Do you have feedback regarding the script? Please email [email protected]."
Write-Host "Do you have feedback regarding the script? Please let us know: [email protected]."
}
}

This file was deleted.

Loading

0 comments on commit 0c1572b

Please sign in to comment.