diff --git a/Security/README.md b/Security/README.md index 830af59dae..c7d7dbfb97 100644 --- a/Security/README.md +++ b/Security/README.md @@ -1,12 +1,64 @@ Script|More Info|Download -|-|- -BackendCookieMitigation.ps1 | [More Info](https://github.com/microsoft/CSS-Exchange/tree/main/Security#backendcookiemitigationps1) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/BackendCookieMitigation.ps1) +EOMT | [More Info](https://github.com/microsoft/CSS-Exchange/tree/main/Security#exchange-on-premises-mitigation-tool-eomt) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/EOMT.ps1) CompareExchangeHashes.ps1 | [More Info](https://github.com/microsoft/CSS-Exchange/tree/main/Security#compareexchangehashesps1) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/CompareExchangeHashes.ps1) ExchangeMitigations.ps1 | [More Info](https://github.com/microsoft/CSS-Exchange/tree/main/Security#exchangemitigationsps1) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/ExchangeMitigations.ps1) http-vuln-cve2021-26855.nse | [More Info](https://github.com/microsoft/CSS-Exchange/tree/main/Security#http-vuln-cve2021-26855nse) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/http-vuln-cve2021-26855.nse) Test-ProxyLogon.ps1 | [More Info](https://github.com/microsoft/CSS-Exchange/tree/main/Security#test-proxylogonps1) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/Test-ProxyLogon.ps1) # Security scripts + +## [Exchange On-premises Mitigation Tool (EOMT)](https://github.com/microsoft/CSS-Exchange/releases/latest/download/EOMT.ps1) +This script contains mitigations to help address the following vulnerabilities. + +* CVE-2021-26855 + +This is the most effective way to help quickly protect and mitigate your Exchange Servers prior to patching. **We recommend this script over the previous ExchangeMitigations.ps1 script.** EOMT automatically downloads any dependencies and runs the Microsoft Safety Scanner. This a better approach for Exchange deployments with Internet access and for those who want an attempt at automated remediation. We have not observed any impact to Exchange Server functionality via these mitigation methods. EOMT.ps1 is completely automated and uses familiar mitigation methods previously documented. This script has three operations it performs: + +* Mitigation of CVE-2021-26855 via a URL Rewrite configuration. Note: This mitigates the known methods of this exploit. +* Malware scan of the Exchange Server via the Microsoft Safety Scanner (https://docs.microsoft.com/en-us/windows/security/threat-protection/intelligence/safety-scanner-download) +* Attempt to remediate compromises detected by the Microsoft Safety Scanner. + +This a better approach for Exchange deployments with Internet access and for those who want an attempt at automated remediation. We have not observed any impact to Exchange Server functionality via these mitigation methods nor do these mitigation methods make any direct changes that disable features of Exchange. + +### Requirements to run EOMT + +* External Internet Connection from your Exchange server (required to download the safety scanner and the IIS URL Rewrite Module). +* PowerShell script must be run as Administrator. + +### System Requirements +* PowerShell 3 or later +* IIS 7.5 and later +* Exchange 2013, 2016, or 2019 +* Windows Server 2008 R2, Server 2012, Server 2012 R2, Server 2016, Server 2019 + +### Who should run EOMT + +Situation | Guidance +-|- +If you have done nothing to date to patch or mitigate this issue… | Run EOMT.PS1 as soon as possible.This will both attempt to remediate as well as mitigate your servers against further attacks. Once complete, follow patching guidance to update your servers on http://aka.ms/exchangevulns +If you have mitigated using any/all of the mitigation guidance Microsoft has given (Exchangemitigations.Ps1, Blog post, etc..) | Run EOMT.PS1 as soon as possible. This will both attempt to remediate as well as mitigate your servers against further attacks. Once complete, follow patching guidance to update your servers on http://aka.ms/exchangevulns +If you have already patched your systems and are protected, but did NOT investigate for any adversary activity, indicators of compromise, etc…. | Run EOMT.PS1 as soon as possible. This will attempt to remediate any existing compromise that may not have been full remediated before patching. +If you have already patched and investigated your systems for any indicators of compromise, etc…. | No action is required + +### Important note regarding Microsoft Safety Scanner + EOMT runs the Microsoft Safety Scanner in a quick scan mode. If you suspect any compromise, we highly recommend you run it in the FULL SCAN mode. FULL SCAN mode can take a long time but if you are not running Mirosoft Defender AV as your default AV, FULL SCAN will be required to remediate threats. + +### EOMT Examples +The default recommended way of using of EOMT.ps1. This will determine if your server is vulnerable, mitigate if vulnerable, and run MSERT in quick scan mode. If the server is not vulnerable only MSERT quick scan will run. + +`.\EOMT.ps1` + +To run a Full MSERT Scan - We only recommend this option only if the initial quick scan discovered threats. The full scan may take hours or days to complete. + +`.\EOMT.ps1 -RunFullScan -DoNotRunMitigation` + +To roll back EOMT mitigations + +`.\EOMT.ps1 -Rollbackmitigation` + +Note: If ExchangeMitigations.ps1 was used previously to apply mitigations, Use ExchangeMitigations.ps1 for rollback. + ## [Test-ProxyLogon.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/Test-ProxyLogon.ps1) Formerly known as Test-Hafnium, this script automates all four of the commands found in the [Hafnium blog post](https://www.microsoft.com/security/blog/2021/03/02/hafnium-targeting-exchange-servers/). It also has a progress bar and some performance tweaks to make the CVE-2021-26855 test run much faster. @@ -138,44 +190,6 @@ Submitting files for analysis: * Please submit the output file for analysis in the malware analysis portal [here](https://www.microsoft.com/en-us/wdsi/filesubmission). Please add the text "ExchangeMarchCVE" in "Additional Information" field on the portal submission form. * Instructions on how to use the portal can be found [here](https://docs.microsoft.com/en-us/windows/security/threat-protection/intelligence/submission-guide). -## [BackendCookieMitigation.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/BackendCookieMitigation.ps1) - -This mitigation will filter https requests that contain malicious X-AnonResource-Backend and malformed X-BEResource cookies which were found to be used in CVE-2021-26855. - -This will help with defense against the known patterns observed but not the SSRF as a whole. For more information please visit https://aka.ms/exchangevulns. - -**For this script to work you must have the IIS URL Rewrite Module installed which can be done via this script using the -FullPathToMSI parameter.** - -For IIS 10 and higher URL Rewrite Module 2.1 must be installed, you can download version 2.1 here: - -* x86 & x64 -https://www.iis.net/downloads/microsoft/url-rewrite - -For IIS 8.5 and lower Rewrite Module 2.0 must be installed, you can download version 2.0 here: - -* x86 - https://www.microsoft.com/en-us/download/details.aspx?id=5747 - -* x64 - https://www.microsoft.com/en-us/download/details.aspx?id=7435 - -Installing URL Rewrite version 2.1 on IIS versions 8.5 and lower may cause IIS and Exchange to become unstable. If there is a mismatch between the URL Rewrite module and IIS version, BackendCookieMitigation.ps1 will not apply the mitigation for CVE-2021-26855. You must uninstall the URL Rewrite module and reinstall the correct version. - -**Script requires PowerShell 3.0 and later and must be executed from an elevated PowerShell Session.** - -Download the latest release here: - -[Download BackendCookieMitigation.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/BackendCookieMitigation.ps1) - -To apply with MSI install of the URL Rewrite module - Note: version may vary depending on system info - -`PS C:\> BackendCookieMitigation.ps1 -FullPathToMSI "C:\temp\rewrite_amd64_en-US.msi" -WebSiteNames "Default Web Site" -Verbose ` - -To apply without MSI install - -`PS C:\> BackendCookieMitigation.ps1 -WebSiteNames "Default Web Site" -Verbose` - -To rollback - Note: This does not remove the IIS Rewrite module, only the rules. - -`PS C:\> BackendCookieMitigation.ps1 -WebSiteNames "Default Web Site" -RollbackMitigation -Verbose` - ## [http-vuln-cve2021-26855.nse](https://github.com/microsoft/CSS-Exchange/releases/latest/download/http-vuln-cve2021-26855.nse) This file is for use with nmap. It detects whether the specified URL is vulnerable to the Exchange Server SSRF Vulnerability (CVE-2021-26855). diff --git a/Security/src/BackendCookieMitigation.ps1 b/Security/src/BackendCookieMitigation.ps1 deleted file mode 100644 index 7e67ac52bc..0000000000 --- a/Security/src/BackendCookieMitigation.ps1 +++ /dev/null @@ -1,320 +0,0 @@ -<# - .SYNOPSIS - This script contains a mitigation for CVE-2021-26855 - For more information please https://aka.ms/exchangevulns - - .DESCRIPTION - This mitigation will filter https requests that contain malicious X-AnonResource-Backend and malformed X-BEResource cookies which were found to be used in cve2021-26855. - This will help with defense against the known patterns observed but not the SSRF as a whole. - - For IIS 10 and higher URL Rewrite Module 2.1 must be installed, you can download version 2.1 (x86 and x64) here: - - * x86 & x64 -https://www.iis.net/downloads/microsoft/url-rewrite - - For IIS 8.5 and lower Rewrite Module 2.0 must be installed, you can download version 2.0 here: - - * x86 - https://www.microsoft.com/en-us/download/details.aspx?id=5747 - - * x64 - https://www.microsoft.com/en-us/download/details.aspx?id=7435 - - It is important to follow these version guidelines as it was found installing the newer version of the URL rewrite module on older versions of IIS (IIS 8.5 and lower) can cause IIS and Exchange to become unstable. - If you find yourself in a scenario where a newer version of the IIS URL rewrite module was installed on an older version of IIS, uninstalling the URL rewrite module and reinstalling the recommended version listed above should resolve any instability issues. - - Script requires PowerShell 3.0 and later and must be executed from an elevated PowerShell Session. - - .PARAMETER FullPathToMSI - This is string parameter is used to specify path of MSI file of URL Rewrite Module. - - .PARAMETER WebSiteNames - This is string array parameter is used to specify name of the Default Web Site in IIS. - - .PARAMETER RollbackMitigation - This is a switch parameter is used to roll back the Backend Cookie Mitigation - - .EXAMPLE - PS C:\> BackendCookieMitigation.ps1 -FullPathToMSI "C:\temp\rewrite_amd64_en-US.msi" -WebSiteNames "Default Web Site" -Verbose - - To apply with MSI install of the URL Rewrite module - Note: version may vary depending on system info - - .EXAMPLE - PS C:\> BackendCookieMitigation.ps1 -WebSiteNames "Default Web Site" -Verbose - - To apply without MSI install - - .EXAMPLE - PS C:\> BackendCookieMitigation.ps1 -WebSiteNames "Default Web Site" -RollbackMitigation -Verbose - - To rollback - Note: This does not uninstall the IIS Rewrite module, only the rules. - - .LINK - https://aka.ms/exchangevulns - https://www.iis.net/downloads/microsoft/url-rewrite - https://www.microsoft.com/en-us/download/details.aspx?id=5747 - https://www.microsoft.com/en-us/download/details.aspx?id=7435 - -#> - -[CmdLetBinding()] -param( - [System.IO.FileInfo]$FullPathToMSI, - [ValidateNotNullOrEmpty()] - [string[]]$WebSiteNames, - [switch]$RollbackMitigation -) -function GetMsiProductVersion { - param ( - [System.IO.FileInfo]$filename - ) - - try { - $windowsInstaller = New-Object -com WindowsInstaller.Installer - - $database = $windowsInstaller.GetType().InvokeMember( - "OpenDatabase", "InvokeMethod", $Null, - $windowsInstaller, @($filename.FullName, 0) - ) - - $q = "SELECT Value FROM Property WHERE Property = 'ProductVersion'" - $View = $database.GetType().InvokeMember( - "OpenView", "InvokeMethod", $Null, $database, ($q) - ) - - $View.GetType().InvokeMember("Execute", "InvokeMethod", $Null, $View, $Null) - - $record = $View.GetType().InvokeMember( - "Fetch", "InvokeMethod", $Null, $View, $Null - ) - - $productVersion = $record.GetType().InvokeMember( - "StringData", "GetProperty", $Null, $record, 1 - ) - - $View.GetType().InvokeMember("Close", "InvokeMethod", $Null, $View, $Null) - - return $productVersion - } catch { - throw "Failed to get MSI file version the error was: {0}." -f $_ - } -} -function Get-InstalledSoftware { - <# - .SYNOPSIS - Retrieves a list of all software installed on a Windows computer. - .EXAMPLE - PS> Get-InstalledSoftware - - This example retrieves all software installed on the local computer. - .PARAMETER ComputerName - If querying a remote computer, use the computer name here. - - .PARAMETER Name - The software title you'd like to limit the query to. - - .PARAMETER Guid - The software GUID you'e like to limit the query to - #> - [CmdletBinding()] - param ( - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$ComputerName = $env:COMPUTERNAME, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$Name, - - [Parameter()] - [guid]$Guid - ) - process { - try { - $scriptBlock = { - $args[0].GetEnumerator() | ForEach-Object { New-Variable -Name $_.Key -Value $_.Value } - - $UninstallKeys = @( - "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall", - "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" - ) - New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS | Out-Null - $UninstallKeys += Get-ChildItem HKU: | Where-Object { $_.Name -match 'S-\d-\d+-(\d+-){1,14}\d+$' } | ForEach-Object { - "HKU:\$($_.PSChildName)\Software\Microsoft\Windows\CurrentVersion\Uninstall" - } - if (-not $UninstallKeys) { - Write-Warning -Message 'No software registry keys found' - } else { - foreach ($UninstallKey in $UninstallKeys) { - $friendlyNames = @{ - 'DisplayName' = 'Name' - 'DisplayVersion' = 'Version' - } - Write-Verbose -Message "Checking uninstall key [$($UninstallKey)]" - if ($Name) { - $WhereBlock = { $_.GetValue('DisplayName') -like "$Name*" } - } elseif ($GUID) { - $WhereBlock = { $_.PsChildName -eq $Guid.Guid } - } else { - $WhereBlock = { $_.GetValue('DisplayName') } - } - $SwKeys = Get-ChildItem -Path $UninstallKey -ErrorAction SilentlyContinue | Where-Object $WhereBlock - if (-not $SwKeys) { - Write-Verbose -Message "No software keys in uninstall key $UninstallKey" - } else { - foreach ($SwKey in $SwKeys) { - $output = @{ } - foreach ($ValName in $SwKey.GetValueNames()) { - if ($ValName -ne 'Version') { - $output.InstallLocation = '' - if ($ValName -eq 'InstallLocation' -and - ($SwKey.GetValue($ValName)) -and - (@('C:', 'C:\Windows', 'C:\Windows\System32', 'C:\Windows\SysWOW64') -notcontains $SwKey.GetValue($ValName).TrimEnd('\'))) { - $output.InstallLocation = $SwKey.GetValue($ValName).TrimEnd('\') - } - [string]$ValData = $SwKey.GetValue($ValName) - if ($friendlyNames[$ValName]) { - $output[$friendlyNames[$ValName]] = $ValData.Trim() ## Some registry values have trailing spaces. - } else { - $output[$ValName] = $ValData.Trim() ## Some registry values trailing spaces - } - } - } - $output.GUID = '' - if ($SwKey.PSChildName -match '\b[A-F0-9]{8}(?:-[A-F0-9]{4}){3}-[A-F0-9]{12}\b') { - $output.GUID = $SwKey.PSChildName - } - New-Object -TypeName PSObject -Prop $output - } - } - } - } - } - - if ($ComputerName -eq $env:COMPUTERNAME) { - & $scriptBlock $PSBoundParameters - } else { - Invoke-Command -ComputerName $ComputerName -ScriptBlock $scriptBlock -ArgumentList $PSBoundParameters - } - } catch { - Write-Error -Message "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)" - } - } -} - -#Configure Rewrite Rule consts -$HttpCookieInput = '{HTTP_COOKIE}' -$root = 'system.webServer/rewrite/rules' -$inbound = '.*' -$name = 'X-AnonResource-Backend Abort - inbound' -$name2 = 'X-BEResource Abort - inbound' -$pattern = '(.*)X-AnonResource-Backend(.*)' -$pattern2 = '(.*)X-BEResource=(.+)/(.+)~(.+)' -$filter = "{0}/rule[@name='{1}']" -f $root, $name -$filter2 = "{0}/rule[@name='{1}']" -f $root, $name2 - -if (!$RollbackMitigation) { - Write-Verbose "[INFO] Starting mitigation process on $env:computername" - - #Check if IIS URL Rewrite Module 2 is installed - Write-Verbose "[INFO] Checking for IIS URL Rewrite Module 2 on $env:computername" - - #If IIS 10 check for URL rewrite 2.1 else URL rewrite 2.0 - $RewriteModule = Get-InstalledSoftware -Name *IIS* | Where-Object { $_.Name -like "*URL*" -and $_.Name -like "*2*" } - $IISVersion = Get-ItemProperty HKLM:\SOFTWARE\Microsoft\InetStp\ | Select-Object versionstring - - $RewriteModuleInstallLog = ($PSScriptRoot + '\' + 'RewriteModuleInstallLog.log') - - #Install module - if ($RewriteModule) { - - #Throwing an exception if incorrect rewrite module version is installed - if ($IISVersion.VersionString -like "*10.*" -and ($RewriteModule.Version -eq "7.2.2")) { - throw "Incorrect IIS URL Rewrite Module 2.0 Installed. You need to install IIS URL Rewrite Module 2.1 to avoid instability issues." - } - if ($IISVersion.VersionString -notlike "*10.*" -and ($RewriteModule.Version -eq "7.2.1993")) { - throw "Incorrect IIS URL Rewrite Module 2.1 Installed. You need to install IIS URL Rewrite Module 2.0 to avoid instability issues." - } - - Write-Verbose "[INFO] IIS URL Rewrite Module 2 already installed on $env:computername" -Verbose - } else { - - if ($FullPathToMSI) { - - $MSIProductVersion = GetMsiProductVersion -filename $FullPathToMSI - - #If IIS 10 assert URL rewrite 2.1 else URL rewrite 2.0 - if ($IISVersion.VersionString -like "*10.*" -and $MSIProductVersion -eq "7.2.2") { - throw "Incorrect MSI for IIS $($IISVersion.VersionString), please use URL rewrite 2.1" - } - if ($IISVersion.VersionString -notlike "*10.*" -and $MSIProductVersion -eq "7.2.1993") { - throw "Incorrect MSI for IIS $($IISVersion.VersionString), please use URL rewrite 2.0" - } - - Write-Verbose "[INFO] Installing IIS URL Rewrite Module 2" -Verbose - $arguments = " /i " + '"' + $FullPathToMSI.FullName + '"' + " /quiet /log " + '"' + $RewriteModuleInstallLog + '"' - $msiexecPath = $env:WINDIR + "\System32\msiexec.exe" - Start-Process -FilePath $msiexecPath -ArgumentList $arguments -Wait - Start-Sleep -Seconds 15 - $RewriteModule = Get-InstalledSoftware -Name *IIS* | Where-Object { $_.Name -like "*URL*" -and $_.Name -like "*2*" } - if ($RewriteModule) { - Write-Verbose "[OK] IIS URL Rewrite Module 2 installed on $env:computername" - } else { - throw "[ERROR] Issue installing IIS URL Rewrite Module 2, please review $($RewriteModuleInstallLog)" - } - } else { - throw "[ERROR] Unable to proceed on $env:computername, path to IIS URL Rewrite Module MSI not provided and module is not installed." - } - } - - foreach ($website in $WebSiteNames) { - Write-Verbose "[INFO] Applying rewrite rule configuration to $env:COMPUTERNAME :: $website" - - $site = "IIS:\Sites\$($website)" - - try { - if ((Get-WebConfiguration -Filter $filter -PSPath $site).name -eq $name) { - Clear-WebConfiguration -Filter $filter -PSPath $site - } - - if ((Get-WebConfiguration -Filter $filter2 -PSPath $site).name -eq $name2) { - Clear-WebConfiguration -Filter $filter2 -PSPath $site - } - - - Add-WebConfigurationProperty -PSPath $site -filter $root -name '.' -value @{name = $name; patterSyntax = 'Regular Expressions'; stopProcessing = 'False' } - Set-WebConfigurationProperty -PSPath $site -filter "$filter/match" -name 'url' -value $inbound - Set-WebConfigurationProperty -PSPath $site -filter "$filter/conditions" -name '.' -value @{input = $HttpCookieInput; matchType = '0'; pattern = $pattern; ignoreCase = 'True'; negate = 'False' } - Set-WebConfigurationProperty -PSPath $site -filter "$filter/action" -name 'type' -value 'AbortRequest' - - Add-WebConfigurationProperty -PSPath $site -filter $root -name '.' -value @{name = $name2; patternSyntax = 'Regular Expressions'; stopProcessing = 'True' } - Set-WebConfigurationProperty -PSPath $site -filter "$filter2/match" -name 'url' -value $inbound - Set-WebConfigurationProperty -PSPath $site -filter "$filter2/conditions" -name '.' -value @{input = $HttpCookieInput; matchType = '0'; pattern = $pattern2; ignoreCase = 'True'; negate = 'False' } - Set-WebConfigurationProperty -PSPath $site -filter "$filter2/action" -name 'type' -value 'AbortRequest' - - Write-Verbose "[OK] Rewrite rule configuration complete for $env:COMPUTERNAME :: $website" - Get-WebConfiguration -Filter $filter -PSPath $site - Get-WebConfiguration -Filter $filter2 -PSPath $site - } catch { - throw $_ - } - } -} else { - Write-Verbose "[INFO] Starting mitigation rollback process on $env:computername" - foreach ($website in $WebSiteNames) { - - $site = "IIS:\Sites\$($website)" - - $MitigationConfig = Get-WebConfiguration -Filter $filter -PSPath $site - if ($MitigationConfig) { - Clear-WebConfiguration -Filter $filter -PSPath $site - Clear-WebConfiguration -Filter $filter2 -PSPath $site - - $Rules = Get-WebConfiguration -Filter 'system.webServer/rewrite/rules/rule' -Recurse - if ($null -eq $Rules) { - Clear-WebConfiguration -PSPath $site -Filter 'system.webServer/rewrite/rules' - } - Write-Verbose "[OK] Rewrite rule mitigation removed for $env:COMPUTERNAME :: $website" - } else { - Write-Verbose "[INFO] Rewrite rule mitigation does not exist for $env:COMPUTERNAME :: $website" - } - } -} diff --git a/Security/src/EOMT.ps1 b/Security/src/EOMT.ps1 new file mode 100644 index 0000000000..64cfa5630e --- /dev/null +++ b/Security/src/EOMT.ps1 @@ -0,0 +1,795 @@ +<# + .SYNOPSIS + This script contains mitigations to help address the following vulnerabilities. + CVE-2021-26855 + + For more information on each mitigation please visit https://aka.ms/exchangevulns + + .DESCRIPTION + This script has three operations it performs: + Mitigation of CVE-2021-26855 via a URL Rewrite configuration. Note: this mitigates current known attacks. + Malware scan of the Exchange Server via the Microsoft Safety Scanner + Attempt to reverse any changes made by identified threats. + + .PARAMETER RunFullScan + If true will determine if the server is vulnerable and run MSERT in full scan mode. + + .PARAMETER RollbackMitigation + If true will only reverse the mitigations if present. + + .PARAMETER DoNotRunMSERT + If true will not run MSERT. + + .PARAMETER DoNotRunMitigation + If true will not apply mitigations. + + .EXAMPLE + PS C:\> EOMT.ps1 + + This will run the default mode which does the following: + 1. Checks if your server is vulnerable based on the presence of the SU patch or Exchange version + 2. Downloads and installs the IIS URL rewrite tool. + 3. Applies the URL rewrite mitigation (only if vulnerable). + 4. Runs the Microsoft Safety Scanner in "Quick Scan" mode. + + .EXAMPLE + PS C:\> EOMT.ps1 -RollbackMitigation + + This will only rollback the URL rewrite mitigation. + + .EXAMPLE + PS C:\> EOMT.ps1 -RunFullScan -DoNotRunMitigation + + This will only run the Microsoft Safety Scanner in "Full Scan" mode. We only recommend this option only if the initial quick scan discovered threats. The full scan may take hours or days to complete. + + .Link + https://aka.ms/exchangevulns + https://www.iis.net/downloads/microsoft/url-rewrite + https://docs.microsoft.com/en-us/windows/security/threat-protection/intelligence/safety-scanner-download +#> +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = 'Invalid rule result')] +[Cmdletbinding()] +param ( + [switch]$RunFullScan, + [switch]$RollbackMitigation, + [switch]$DoNotRunMSERT, + [switch]$DoNotRunMitigation +) + +$ProgressPreference = "SilentlyContinue" +$EOMTDir = Join-Path $env:TEMP "msert" +$EOMTLogFile = Join-Path $EOMTDir "EOMT.log" +$msertLogPath = "$env:SystemRoot\debug\msert.log" +$msertLogArchivePath = "$env:SystemRoot\debug\msert.old.log" +$detectionFollowUpURL = 'https://go.microsoft.com/fwlink/?linkid=2157359' +$SummaryFile = "$env:SystemDrive\EOMTSummary.txt" + +# Force TLS1.2 to make sure we can download from HTTPS +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + +function Run-Mitigate { + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseApprovedVerbs', '', Justification = 'Invalid rule result')] + param( + [string]$WebSiteName = "Default Web Site", + [string]$Stage = "MitigationProcess", + [switch]$RollbackMitigation + + ) + + function GetMsiProductVersion { + param ( + [string]$filename + ) + + try { + $windowsInstaller = New-Object -com WindowsInstaller.Installer + + $database = $windowsInstaller.GetType().InvokeMember( + "OpenDatabase", "InvokeMethod", $Null, + $windowsInstaller, @($filename, 0) + ) + + $q = "SELECT Value FROM Property WHERE Property = 'ProductVersion'" + + $View = $database.GetType().InvokeMember( + "OpenView", "InvokeMethod", $Null, $database, ($q) + ) + + try { + $View.GetType().InvokeMember("Execute", "InvokeMethod", $Null, $View, $Null) | Out-Null + + $record = $View.GetType().InvokeMember( + "Fetch", "InvokeMethod", $Null, $View, $Null + ) + + $productVersion = $record.GetType().InvokeMember( + "StringData", "GetProperty", $Null, $record, 1 + ) + + return $productVersion + } finally { + if ($View) { + $View.GetType().InvokeMember("Close", "InvokeMethod", $Null, $View, $Null) | Out-Null + } + } + } catch { + throw "Failed to get MSI file version the error was: {0}." -f $_ + } + } + + function Get-InstalledSoftwareVersion { + param ( + [ValidateNotNullOrEmpty()] + [string[]]$Name + ) + + try { + $UninstallKeys = @( + "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall", + "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" + ) + + New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS | Out-Null + + $UninstallKeys += Get-ChildItem HKU: | Where-Object { $_.Name -match 'S-\d-\d+-(\d+-){1,14}\d+$' } | ForEach-Object { + "HKU:\$($_.PSChildName)\Software\Microsoft\Windows\CurrentVersion\Uninstall" + } + + foreach ($UninstallKey in $UninstallKeys) { + $SwKeys = Get-ChildItem -Path $UninstallKey -ErrorAction SilentlyContinue + foreach ($n in $Name) { + $SwKeys = $SwKeys | Where-Object { $_.GetValue('DisplayName') -like "$n" } + } + if ($SwKeys) { + foreach ($SwKey in $SwKeys) { + if ($SwKey.GetValueNames().Contains("DisplayVersion")) { + return $SwKey.GetValue("DisplayVersion") + } + } + } + } + } catch { + Write-Error -Message "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)" + } + } + + function GetURLRewriteLink { + $DownloadLinks = @{ + "v2.1" = @{ + "x86" = @{ + "de-DE" = "https://download.microsoft.com/download/D/8/1/D81E5DD6-1ABB-46B0-9B4B-21894E18B77F/rewrite_x86_de-DE.msi" + "en-US" = "https://download.microsoft.com/download/D/8/1/D81E5DD6-1ABB-46B0-9B4B-21894E18B77F/rewrite_x86_en-US.msi" + "es-ES" = "https://download.microsoft.com/download/D/8/1/D81E5DD6-1ABB-46B0-9B4B-21894E18B77F/rewrite_x86_es-ES.msi" + "fr-FR" = "https://download.microsoft.com/download/D/8/1/D81E5DD6-1ABB-46B0-9B4B-21894E18B77F/rewrite_x86_fr-FR.msi" + "it-IT" = "https://download.microsoft.com/download/D/8/1/D81E5DD6-1ABB-46B0-9B4B-21894E18B77F/rewrite_x86_it-IT.msi" + "ja-JP" = "https://download.microsoft.com/download/D/8/1/D81E5DD6-1ABB-46B0-9B4B-21894E18B77F/rewrite_x86_ja-JP.msi" + "ko-KR" = "https://download.microsoft.com/download/D/8/1/D81E5DD6-1ABB-46B0-9B4B-21894E18B77F/rewrite_x86_ko-KR.msi" + "ru-RU" = "https://download.microsoft.com/download/D/8/1/D81E5DD6-1ABB-46B0-9B4B-21894E18B77F/rewrite_x86_ru-RU.msi" + "zh-CN" = "https://download.microsoft.com/download/D/8/1/D81E5DD6-1ABB-46B0-9B4B-21894E18B77F/rewrite_x86_zh-CN.msi" + "zh-TW" = "https://download.microsoft.com/download/D/8/1/D81E5DD6-1ABB-46B0-9B4B-21894E18B77F/rewrite_x86_zh-TW.msi" + } + + "x64" = @{ + "de-DE" = "https://download.microsoft.com/download/1/2/8/128E2E22-C1B9-44A4-BE2A-5859ED1D4592/rewrite_amd64_de-DE.msi" + "en-US" = "https://download.microsoft.com/download/1/2/8/128E2E22-C1B9-44A4-BE2A-5859ED1D4592/rewrite_amd64_en-US.msi" + "es-ES" = "https://download.microsoft.com/download/1/2/8/128E2E22-C1B9-44A4-BE2A-5859ED1D4592/rewrite_amd64_es-ES.msi" + "fr-FR" = "https://download.microsoft.com/download/1/2/8/128E2E22-C1B9-44A4-BE2A-5859ED1D4592/rewrite_amd64_fr-FR.msi" + "it-IT" = "https://download.microsoft.com/download/1/2/8/128E2E22-C1B9-44A4-BE2A-5859ED1D4592/rewrite_amd64_it-IT.msi" + "ja-JP" = "https://download.microsoft.com/download/1/2/8/128E2E22-C1B9-44A4-BE2A-5859ED1D4592/rewrite_amd64_ja-JP.msi" + "ko-KR" = "https://download.microsoft.com/download/1/2/8/128E2E22-C1B9-44A4-BE2A-5859ED1D4592/rewrite_amd64_ko-KR.msi" + "ru-RU" = "https://download.microsoft.com/download/1/2/8/128E2E22-C1B9-44A4-BE2A-5859ED1D4592/rewrite_amd64_ru-RU.msi" + "zh-CN" = "https://download.microsoft.com/download/1/2/8/128E2E22-C1B9-44A4-BE2A-5859ED1D4592/rewrite_amd64_zh-CN.msi" + "zh-TW" = "https://download.microsoft.com/download/1/2/8/128E2E22-C1B9-44A4-BE2A-5859ED1D4592/rewrite_amd64_zh-TW.msi" + } + } + "v2.0" = @{ + "x86" = @{ + "de-DE" = "https://download.microsoft.com/download/0/5/0/05045383-D280-4DC6-AE8C-81764118B0F9/rewrite_x86_de-DE.msi" + "en-US" = "https://download.microsoft.com/download/6/9/C/69C1195A-123E-4BE8-8EDF-371CDCA4EC6C/rewrite_2.0_rtw_x86.msi" + "es-ES" = "https://download.microsoft.com/download/1/D/9/1D9464B8-9F3B-4A86-97F2-AEC2AB48F481/rewrite_x86_es-ES.msi" + "fr-FR" = "https://download.microsoft.com/download/1/2/9/129A2686-9654-4B2A-82ED-FC7BCE2BCE93/rewrite_x86_fr-FR.msi" + "it-IT" = "https://download.microsoft.com/download/2/4/A/24AE553F-CA8F-43B3-ACF8-DAC526FC84F2/rewrite_x86_it-IT.msi" + "ja-JP" = "https://download.microsoft.com/download/A/6/9/A69D23A5-7CE3-4F80-B5AE-CF6478A5DE19/rewrite_x86_ja-JP.msi" + "ko-KR" = "https://download.microsoft.com/download/2/6/F/26FCA84A-48BC-4AEE-BD6A-B28ED595832E/rewrite_x86_ko-KR.msi" + "ru-RU" = "https://download.microsoft.com/download/B/1/F/B1FDE19F-B4F9-4EBF-9E50-5C9CDF0302D2/rewrite_x86_ru-RU.msi" + "zh-CN" = "https://download.microsoft.com/download/4/9/C/49CD28DB-4AA6-4A51-9437-AA001221F606/rewrite_x86_zh-CN.msi" + "zh-TW" = "https://download.microsoft.com/download/1/9/4/1947187A-8D73-4C3E-B62C-DC6C7E1B353C/rewrite_x86_zh-TW.msi" + } + "x64" = @{ + "de-DE" = "https://download.microsoft.com/download/3/1/C/31CE0BF6-31D7-415D-A70A-46A430DE731F/rewrite_x64_de-DE.msi" + "en-US" = "https://download.microsoft.com/download/6/7/D/67D80164-7DD0-48AF-86E3-DE7A182D6815/rewrite_2.0_rtw_x64.msi" + "es-ES" = "https://download.microsoft.com/download/9/5/5/955337F6-5A11-417E-A95A-E45EE8C7E7AC/rewrite_x64_es-ES.msi" + "fr-FR" = "https://download.microsoft.com/download/3/D/3/3D359CD6-147B-42E9-BD5B-407D3A1F0B97/rewrite_x64_fr-FR.msi" + "it-IT" = "https://download.microsoft.com/download/6/8/B/68B8EFA8-9404-45A3-A51B-53D940D5E742/rewrite_x64_it-IT.msi" + "ja-JP" = "https://download.microsoft.com/download/3/7/5/375C965C-9D98-438A-8F11-7F417D071DC9/rewrite_x64_ja-JP.msi" + "ko-KR" = "https://download.microsoft.com/download/2/A/7/2A746C73-467A-4BC6-B5CF-C4E88BB40406/rewrite_x64_ko-KR.msi" + "ru-RU" = "https://download.microsoft.com/download/7/4/E/74E569F7-44B9-4D3F-BCA7-87C5FE36BD62/rewrite_x64_ru-RU.msi" + "zh-CN" = "https://download.microsoft.com/download/4/E/7/4E7ECE9A-DF55-4F90-A354-B497072BDE0A/rewrite_x64_zh-CN.msi" + "zh-TW" = "https://download.microsoft.com/download/8/2/C/82CE350D-2068-4DAC-99D5-AEB2241DB545/rewrite_x64_zh-TW.msi" + } + } + } + + $IISVersion = Get-ItemProperty HKLM:\SOFTWARE\Microsoft\InetStp\ | Select-Object versionstring + + if ($IISVersion.VersionString -like "* 10.*") { + $Version = "v2.1" + } else { + $Version = "v2.0" + } + + if ([Environment]::Is64BitOperatingSystem) { + $Architecture = "x64" + } else { + $Architecture = "x86" + } + + if ((Get-Culture).Name -in @("de-DE", "en-US", "es-ES", "fr-FR", "it-IT", "ja-JP", "ko-KR", "ru-RU", "zn-CN", "zn-TW")) { + $Language = (Get-Culture).Name + } else { + $Language = "en-US" + } + + return $DownloadLinks[$Version][$Architecture][$Language] + } + + #Configure Rewrite Rule consts + $HttpCookieInput = '{HTTP_COOKIE}' + $root = 'system.webServer/rewrite/rules' + $inbound = '.*' + $name = 'X-AnonResource-Backend Abort - inbound' + $name2 = 'X-BEResource Abort - inbound' + $pattern = '(.*)X-AnonResource-Backend(.*)' + $pattern2 = '(.*)X-BEResource=(.+)/(.+)~(.+)' + $filter = "{0}/rule[@name='{1}']" -f $root, $name + $filter2 = "{0}/rule[@name='{1}']" -f $root, $name2 + + Import-Module WebAdministration + + if ($RollbackMitigation) { + $Message = "Starting rollback of mitigation on $env:computername" + $RegMessage = "Starting rollback of mitigation" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + + $site = "IIS:\Sites\$WebSiteName" + + $mitigationFound = $false + foreach ($f in @($filter, $filter2)) { + if (Get-WebConfiguration -Filter $f -PSPath $site) { + $mitigationFound = $true + Clear-WebConfiguration -Filter $f -PSPath $site + } + } + + if ($mitigationFound) { + $Rules = Get-WebConfiguration -Filter 'system.webServer/rewrite/rules/rule' -Recurse + if ($null -eq $Rules) { + Clear-WebConfiguration -PSPath $site -Filter 'system.webServer/rewrite/rules' + } + + $Message = "Rollback of mitigation complete on $env:computername" + $RegMessage = "Rollback of mitigation complete" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + } else { + $Message = "Mitigation not present on $env:computername" + $RegMessage = "Mitigation not present" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + } + } else { + $Message = "Starting mitigation process on $env:computername" + $RegMessage = "Starting mitigation process" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + + #If IIS 10 check for URL rewrite 2.1 else URL rewrite 2.0 + $RewriteModule = Get-InstalledSoftwareVersion -Name "*IIS*", "*URL*", "*2*" + $IISVersion = Get-ItemProperty HKLM:\SOFTWARE\Microsoft\InetStp\ | Select-Object versionstring + + #Install module + if ($RewriteModule) { + + #Throwing an exception if incorrect rewrite module version is installed + if ($IISVersion.VersionString -like "* 10.*" -and ($RewriteModule -eq "7.2.2")) { + $Message = "Incorrect IIS URL Rewrite Module previously installed on $env:computername" + $RegMessage = "Incorrect IIS URL Rewrite Module previously installed" + Set-LogActivity -Error -Stage $Stage -RegMessage $RegMessage -Message $Message + throw + } + if ($IISVersion.VersionString -notlike "* 10.*" -and ($RewriteModule -eq "7.2.1993")) { + $Message = "Incorrect IIS URL Rewrite Module previously installed on $env:computername" + $RegMessage = "Incorrect IIS URL Rewrite Module previously installed" + Set-LogActivity -Error -Stage $Stage -RegMessage $RegMessage -Message $Message + throw + } + + $Message = "IIS URL Rewrite Module is already installed on $env:computername" + $RegMessage = "IIS URL Rewrite Module already installed" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + } else { + $DownloadLink = GetURLRewriteLink + $DownloadPath = Join-Path $EOMTDir "\$($DownloadLink.Split("/")[-1])" + $RewriteModuleInstallLog = Join-Path $EOMTDir "\RewriteModuleInstall.log" + + $response = Invoke-WebRequest $DownloadLink -UseBasicParsing + [IO.File]::WriteAllBytes($DownloadPath, $response.Content) + + $MSIProductVersion = GetMsiProductVersion -filename $DownloadPath + + #If IIS 10 assert URL rewrite 2.1 else URL rewrite 2.0 + if ($IISVersion.VersionString -like "* 10.*" -and $MSIProductVersion -eq "7.2.2") { + $Message = "Incorrect IIS URL Rewrite Module downloaded on $env:computername" + $RegMessage = "Incorrect IIS URL Rewrite Module downloaded" + Set-LogActivity -Error -Stage $Stage -RegMessage $RegMessage -Message $Message + throw + } + + if ($IISVersion.VersionString -notlike "* 10.*" -and $MSIProductVersion -eq "7.2.1993") { + $Message = "Incorrect IIS URL Rewrite Module downloaded on $env:computername" + $RegMessage = "Incorrect IIS URL Rewrite Module downloaded" + Set-LogActivity -Error -Stage $Stage -RegMessage $RegMessage -Message $Message + throw + } + + $Message = "Installing the IIS URL Rewrite Module on $env:computername" + $RegMessage = "Installing IIS URL Rewrite Module" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + + $arguments = "/i `"$DownloadPath`" /quiet /log `"$RewriteModuleInstallLog`"" + $msiexecPath = $env:WINDIR + "\System32\msiexec.exe" + Start-Process -FilePath $msiexecPath -ArgumentList $arguments -Wait + Start-Sleep -Seconds 15 + $RewriteModule = Get-InstalledSoftwareVersion -Name "*IIS*", "*URL*", "*2*" + + if ($RewriteModule) { + $Message = "IIS URL Rewrite Module installed on $env:computername" + $RegMessage = "IIS URL Rewrite Module installed" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + } else { + $Message = "Issue installing IIS URL Rewrite Module $env:computername" + $RegMessage = "Issue installing IIS URL Rewrite Module" + Set-LogActivity -Error -Stage $Stage -RegMessage $RegMessage -Message $Message + throw + } + } + + $Message = "Applying URL Rewrite configuration to $env:COMPUTERNAME :: $WebSiteName" + $RegMessage = "Applying URL Rewrite configuration" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + + $site = "IIS:\Sites\$WebSiteName" + + try { + if ((Get-WebConfiguration -Filter $filter -PSPath $site).name -eq $name) { + Clear-WebConfiguration -Filter $filter -PSPath $site + } + + if ((Get-WebConfiguration -Filter $filter2 -PSPath $site).name -eq $name2) { + Clear-WebConfiguration -Filter $filter2 -PSPath $site + } + + Add-WebConfigurationProperty -PSPath $site -filter $root -name '.' -value @{name = $name; patternSyntax = 'Regular Expressions'; stopProcessing = 'False' } + Set-WebConfigurationProperty -PSPath $site -filter "$filter/match" -name 'url' -value $inbound + Set-WebConfigurationProperty -PSPath $site -filter "$filter/conditions" -name '.' -value @{input = $HttpCookieInput; matchType = '0'; pattern = $pattern; ignoreCase = 'True'; negate = 'False' } + Set-WebConfigurationProperty -PSPath $site -filter "$filter/action" -name 'type' -value 'AbortRequest' + + Add-WebConfigurationProperty -PSPath $site -filter $root -name '.' -value @{name = $name2; patternSyntax = 'Regular Expressions'; stopProcessing = 'True' } + Set-WebConfigurationProperty -PSPath $site -filter "$filter2/match" -name 'url' -value $inbound + Set-WebConfigurationProperty -PSPath $site -filter "$filter2/conditions" -name '.' -value @{input = $HttpCookieInput; matchType = '0'; pattern = $pattern2; ignoreCase = 'True'; negate = 'False' } + Set-WebConfigurationProperty -PSPath $site -filter "$filter2/action" -name 'type' -value 'AbortRequest' + + $Message = "Mitigation complete on $env:COMPUTERNAME :: $WebSiteName" + $RegMessage = "Mitigation complete" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + } catch { + $Message = "Mitigation failed on $env:COMPUTERNAME :: $WebSiteName" + $RegMessage = "Mitigation failed" + Set-LogActivity -Error -Stage $Stage -RegMessage $RegMessage -Message $Message + throw + } + } +} + +function Run-MSERT { + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseApprovedVerbs', '', Justification = 'Invalid rule result')] + param( + [switch] $RunFullScan + ) + $Stage = "MSERTProcess" + if ($DoNotRunMSERT) { + $Message = "Skipping mitigation -DoNotRunMSERT set on $env:computername" + $RegMessage = "Skipping mitigation -DoNotRunMSERT" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + return + } + + #Check for KB4474419 + $OS = [System.Environment]::OSVersion + if ($OS.Version.Major -eq 6 -and $OS.Version.Minor -eq 1) { + $Hotfix = Get-HotFix -Id KB4474419 -ErrorAction SilentlyContinue + + if (-not ($Hotfix)) { + $Message = "Unable to run MSERT: KB4474419 is missing on Server 2008 R2" + $RegMessage = "Unable to run MSERT KB4474419 missing" + + Set-LogActivity -Error -Stage $Stage -RegMessage $RegMessage -Message $Message + throw + } + } + + #Check for running MSERT or MRT process before download + $procsToWaitFor = @("mrt", "msert") + :checkForRunningCleaner while ($true) { + foreach ($procName in $procsToWaitFor) { + $proc = Get-Process -Name $procName -ErrorAction SilentlyContinue + if ($proc) { + $pids = [string]::Join(",", $proc.Id) + + $Message = "Found $procName already running ($pids). Waiting for it to exit." + $RegMessage = "msert already running waiting" + $Stage = "MSERTProcess" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + + Start-Sleep -Seconds 60 + continue checkForRunningCleaner + } + } + break + } + + if ([System.Environment]::Is64BitOperatingSystem) { + $MSERTUrl = "https://go.microsoft.com/fwlink/?LinkId=212732" + } else { + $MSERTUrl = "https://go.microsoft.com/fwlink/?LinkId=212733" + } + + $Message = "Starting MSERTProcess on $env:computername" + $RegMessage = "Starting MSERTProcess" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + + try { + $msertExe = Join-Path $EOMTDir "\msert.exe" + $response = Invoke-WebRequest $MSERTUrl -UseBasicParsing + [IO.File]::WriteAllBytes($msertExe, $response.Content) + + $Message = "MSERT download complete on $env:computername" + $RegMessage = "MSERT download complete" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + } catch { + $Message = "MSERT download failed on $env:computername" + $RegMessage = "MSERT download failed" + Set-LogActivity -Error -Stage $Stage -RegMessage $RegMessage -Message $Message + throw + } + + #Start MSERT + + function RunMsert { + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidAssignmentToAutomaticVariable', '', Justification = 'Invalid rule result')] + param( + [switch]$FullScan + ) + + $msertLogPath = "$env:SystemRoot\debug\msert.log" + $msertLogArchivePath = "$env:SystemRoot\debug\msert.old.log" + + if (Test-Path $msertLogPath) { + Get-Content $msertLogPath | Out-File $msertLogArchivePath -Append + Remove-Item $msertLogPath + } + + $msertArguments = "/Q" + if ($FullScan) { + $msertArguments = "/F /Q" + } + + Start-Process $msertExe -ArgumentList $msertArguments -Wait + + $detected = $false + + if (Test-Path $msertLogPath) { + $matches = Select-String -Path $msertLogPath -Pattern "Threat Detected" + if ($matches) { + $detected = $true + } + } else { + $Message = "Did not find expected scanner log file at $msertLogPath" + $RegMessage = "No scanner log" + Set-LogActivity -Error -Stage $Stage -RegMessage $RegMessage -Message $Message + throw + } + + return $detected + } + + if ($RunFullScan) { + Write-Warning -Message "Running a full scan can take hours or days to complete." + Write-Warning -Message "Would you like to continue with the Full MSERT Scan?" + + while ($true) { + $Confirm = Read-Host "(Y/N)" + if ($Confirm -like "N") { + return + } + if ($Confirm -like "Y") { + break + } + } + + $Message = "Running Microsoft Safety Scanner - Mode: Full Scan on $env:computername" + $RegMessage = "Running Microsoft Safety Scanner Full Scan" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + + $msertDetected = RunMsert -FullScan + } else { + Write-Verbose -Message "Quick scan will take several minutes to complete, please wait.." -Verbose + + $Message = "Running Microsoft Safety Scanner - Mode: Quick Scan on $env:computername" + $RegMessage = "Running Microsoft Safety Scanner Quick Scan" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + + $msertDetected = RunMsert + } + + if ($msertDetected) { + Write-Warning -Message "THREATS DETECTED on $env:computername!" + Get-Content $msertLogPath + $Message = "Threats detected! Please review `"$msertLogPath`" as soon as possible. " + if (!$RunFullScan) { + $Message += "We highly recommend re-running this script with -RunFullScan. " + } + $Message += "For additional guidance, see `"$SummaryFile`"." + $RegMessage = "Microsoft Safety Scanner is complete: THREATS DETECTED" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + } + + $Message = "Microsoft Safety Scanner is complete on $env:computername No known threats detected." + $RegMessage = "Microsoft Safety Scanner is complete" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message +} + +function Get-ServerVulnStatus { + param( + $Version = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\ExchangeServer\v15\Setup\') + ) + + $Version = $Version.OwaVersion + $FutureCUs = @{ + E19CU9 = "15.2.858.5" + E16CU20 = "15.1.2242.4" + } + + if ($version -like "15.2.*") { + $LatestCU = $FutureCUs.E19CU9 + } elseif ($version -like "15.1.*") { + $LatestCU = $FutureCUs.E16CU20 + } else { + $LatestCU = "15.2.000.0000" #version higher than 15.0 to trigger SecurityHotfix check for E15 + } + + if ([version]$LatestCU -ge [version]$Version) { + + $SecurityHotfix = Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\* ` + | Where-Object displayname -Like "*KB5000871*" ` + | Select-Object displayname -ErrorAction SilentlyContinue + + if (!$SecurityHotfix) { + return $true + } + } + return $false +} + +function Write-Log { + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidOverwritingBuiltInCmdlets', '', Justification = 'Invalid rule result')] + param + ( + [string]$Message, + [string]$Path = $EOMTLogFile, + [string]$Level = "Info" + ) + + $FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + + # Write log entry to $Path + "$FormattedDate $($Level): $Message" | Out-File -FilePath $Path -Append +} + +function Set-LogActivity { + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidAssignmentToAutomaticVariable', '', Justification = 'Invalid rule result')] + [CmdletBinding(SupportsShouldProcess)] + param ( + $Stage, + $RegMessage, + $Message, + [switch] $Error + ) + + if ($Error) { + $FullRegMessage = "0 $RegMessage" + $Level = "Error" + } else { + $FullRegMessage = "1 $RegMessage" + $Level = "Info" + } + If ($Level -eq "Info") { + Write-Verbose -Message $Message -Verbose + } else { + Write-Error -Message $Message + } + + Write-Log -Message $Message -Level $Level + Set-Registry -RegKey "HKLM:\Software\MSERTBootstrap\PatchState" -RegValue "Timestamp" -RegData (Get-Date).ToString("MM/dd/yyyy hh:mm:ss") -RegType String | Out-Null + Set-Registry -RegKey "HKLM:\Software\MSERTBootstrap\PatchState" -RegValue $Stage -RegData $FullRegMessage -RegType String | Out-Null +} + +function Set-Registry { + [CmdletBinding(SupportsShouldProcess)] + param ( + $RegKey, + $RegValue, + $RegData, + [ValidateSet('String', 'DWord', 'Binary', 'ExpandString', 'MultiString', 'None', 'QWord', 'Unknown')] + $RegType = 'String' + ) + + if (-not (Test-Path $RegKey)) { + Write-Verbose "The key $RegKey does not exist. Trying to create it..." + + try { + New-Item -Path $RegKey -Force + Write-Verbose "Creation of $RegKey was successful." + } catch { + Write-Error -Message $_ + return + } + } + + Set-ItemProperty -Path $RegKey -Name $RegValue -Value $RegData -Type $RegType -Force +} + +function Write-Summary { + param( + [switch]$Pass + ) + + if ($Pass) { + $header = @" +Microsoft Safety Scanner and CVE-2021-26855 mitigation summary +Message: Microsoft attempted to mitigate and protect your Exchange server from CVE-2021-26855 and clear malicious files. +For more information on these vulnerabilities please visit https://aka.ms/Exchangevulns. This attempt was successful. +Please review locations and files as soon as possible and take the recommended action. +"@ + } else { + $header = @" +Microsoft Safety Scanner and CVE-2021-26855 mitigation summary +Message: Microsoft attempted to mitigate and protect your Exchange server from CVE-2021-26855 and clear malicious files. +For more information on these vulnerabilities please visit https://aka.ms/Exchangevulns. This attempt was unsuccessful. +Please review locations and files as soon as possible and take the recommended action. +"@ + } + + $summary = @" +$header + +Microsoft saved several files to your system to "$EOMTDir". The only files that should be present in this directory are: + a - msert.exe + b - EOMT.log + c - RewriteModuleInstall.log + d - one of the following IIS URL rewrite MSIs: + rewrite_amd64_[de-DE,en-US,es-ES,fr-FR,it-IT,ja-JP,ko-KR,ru-RU,zh-CN,zh-TW].msi + rewrite_ x86_[de-DE,es-ES,fr-FR,it-IT,ja-JP,ko-KR,ru-RU,zh-CN,zh-TW].msi + rewrite_x64_[de-DE,es-ES,fr-FR,it-IT,ja-JP,ko-KR,ru-RU,zh-CN,zh-TW].msi + rewrite_2.0_rtw_x86.msi + rewrite_2.0_rtw_x64.msi + +1 - Confirm the IIS URL Rewrite Module is installed. This module is required for the mitigation of CVE-2021-26855, the module and the configuration (present or not) will not impact this system negatively. + a - If installed, Confirm the following entry exists in the "$env:SystemDrive\inetpub\wwwroot\web.config". If this configuration is not present, your server is not mitigated. This may have occurred if the module was not successfully installed with a supported version for your system. + + + + + + + + + + + + + + + + + + + + + +2 - Review the results of the Microsoft Safety Scanner + Microsoft Safety Scanner log can be found at "$msertLogPath" and "$msertLogArchivePath" If any threats were detected, please review the guidance here: $detectionFollowUpURL + +"@ + + if (Test-Path $SummaryFile) { + Remove-Item $SummaryFile -Force + } + + $summary = $summary.Replace("`r`n", "`n").Replace("`n", "`r`n") + $summary | Out-File -FilePath $SummaryFile -Encoding ascii -Force +} + +# Main +try { + $Stage = "EOMTStart" + + if (!(Test-Path $EOMTDir)) { + New-Item -ItemType Directory $EOMTDir | Out-Null + } + + $Message = "Starting EOMT.ps1 on $env:computername" + $RegMessage = "Starting EOMT.ps1" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + + #IsAdmin? + if (!([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { + $Message = "Unable to launch EOMT.ps1: please re-run as administrator." + $RegMessage = "Insufficient permissions" + Set-LogActivity -Error -Stage $Stage -RegMessage $RegMessage -Message $Message + throw + } + + #IsPS3 or later? + if ($PSVersionTable.PSVersion.Major -lt 3) { + $Message = "Unsupported Powershell on $env:computername" + $RegMessage = "Unsupported Powershell" + Set-LogActivity -Error -Stage $Stage -RegMessage $RegMessage -Message $Message + throw + } + + $Message = "EOMT precheck complete on $env:computername" + $RegMessage = "EOMT precheck complete" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + + #Execute Mitigation + if ($DoNotRunMitigation) { + $Stage = "DoNotRunMitigation" + $Message = "Skipping mitigation -DoNotRunMitigation set on $env:computername" + $RegMessage = "Skipping mitigation -DoNotRunMitigation" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + } + + if ($RollbackMitigation) { + Run-Mitigate -RollbackMitigation + } + + if (!$DoNotRunMitigation -and !$RollbackMitigation) { + #Normal run + $IsVulnerable = Get-ServerVulnStatus + if ($IsVulnerable) { + $Message = "$env:computername is vulnerable: applying mitigation" + $RegMessage = "Server is vulnerable" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + Run-Mitigate + } else { + $Message = "$env:computername is not vulnerable: mitigation not needed" + $RegMessage = "Server is not vulnerable" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + } + } + + #Execute Msert + if ($RunFullScan) { + Run-MSERT -RunFullScan + } elseif (!$RollbackMitigation) { + Run-MSERT + } + + $Message = "EOMT.ps1 complete on $env:computername, please review EOMT logs at $EOMTLogFile and the summary file at $SummaryFile" + $RegMessage = "EOMT.ps1 failed to complete" + Set-LogActivity -Stage $Stage -RegMessage $RegMessage -Message $Message + Write-Summary -Pass #Pass +} catch { + $Message = "EOMT.ps1 failed to complete on $env:computername, please review EOMT logs at $EOMTLogFile and the summary file at $SummaryFile - $_" + $RegMessage = "EOMT.ps1 failed to complete" + Set-LogActivity -Error -Stage $Stage -RegMessage $RegMessage -Message $Message + Write-Summary #Fail +}