From 35c4eccbde082cabcd83e622f8f7b785ee38fdb0 Mon Sep 17 00:00:00 2001 From: iserrano76 Date: Wed, 17 May 2023 18:38:50 +0200 Subject: [PATCH 01/19] Replacement of #1546 --- .build/cspell-words.txt | 2 + Admin/Test-AMSI.ps1 | 813 +++++++++++++++++++++++++++++++++++----- docs/Admin/Test-AMSI.md | 43 ++- 3 files changed, 751 insertions(+), 107 deletions(-) diff --git a/.build/cspell-words.txt b/.build/cspell-words.txt index 65789868b3..b9db29bb36 100644 --- a/.build/cspell-words.txt +++ b/.build/cspell-words.txt @@ -62,6 +62,7 @@ httperr ietf imap Infoworker +Inproc INSUFF isnot Journaling @@ -139,6 +140,7 @@ Sids Snapin SPDLT STMP +syncall tcpip TDSDSA Truncater diff --git a/Admin/Test-AMSI.ps1 b/Admin/Test-AMSI.ps1 index 731442bced..dc99a071ce 100644 --- a/Admin/Test-AMSI.ps1 +++ b/Admin/Test-AMSI.ps1 @@ -1,144 +1,757 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -[CmdletBinding(DefaultParameterSetName = "TestAMSI", HelpUri = "https://aka.ms/css-exchange")] +<# + +.SYNOPSIS + The Windows AntiMalware Scan Interface (AMSI) is a versatile standard that allows applications and services to integrate with any AntiMalware product present on a machine. Seeing that Exchange administrators might not be familiar with AMSI, we wanted to provide a script that would make life a bit easier to test, enable, disable, or Check your AMSI Providers. +.DESCRIPTION + The Windows AntiMalware Scan Interface (AMSI) is a versatile standard that allows applications and services to integrate with any AntiMalware product present on a machine. Seeing that Exchange administrators might not be familiar with AMSI, we wanted to provide a script that would make life a bit easier to test, enable, disable, or Check your AMSI Providers. +.PARAMETER TestAMSI + If you want to test to see if AMSI integration is working. You can use a server, server list or FQDN of load balanced array of Client Access servers. +.PARAMETER IgnoreSSL + If you need to test and ignoring the certificate check. +.PARAMETER CheckAMSIConfig + If you want to see what AMSI Providers are installed. You can combine with ServerList, AllServers or Sites. +.PARAMETER EnableAMSI + If you want to enable AMSI. Without any additional parameter it will apply at Organization Level. If combine with ServerList, AllServers or Sites it will apply at server level. +.PARAMETER DisableAMSI + If you want to disable AMSI. Without any additional parameter it will apply at Organization Level. If combine with ServerList, AllServers or Sites it will apply at server level. +.PARAMETER RestartIIS + If you want to restart the Internet Information Services (IIS). You can combine with ServerList, AllServers or Sites. +.PARAMETER Force + If you want to restart the Internet Information Services (IIS) without confirmation. +.PARAMETER ServerList + If you want to apply to some specific servers. +.PARAMETER AllServers + If you want to apply to all server. +.PARAMETER Sites + If you want to apply to all server on a sites or list of sites. + + +.EXAMPLE + .\Test-AMSI.ps1 mail.contoso.com + If you want to test to see if AMSI integration is working in a LB Array + +.EXAMPLE + .\Test-AMSI.ps1 -ServerList server1, server2 + If you want to test to see if AMSI integration is working in list of servers. + +.EXAMPLE + .\Test-AMSI.ps1 -AllServers + If you want to test to see if AMSI integration is working in all server. + +.EXAMPLE + .\Test-AMSI.ps1 -AllServers -Sites Site1, Site2 + If you want to test to see if AMSI integration is working in all server in a list of sites. + +.EXAMPLE + .\Test-AMSI.ps1 -IgnoreSSL + If you need to test and ignoring the certificate check. + +.EXAMPLE + .\Test-AMSI.ps1 -CheckAMSIProviders + If you want to see what AMSI Providers are installed on the local machine. + +.EXAMPLE + .\Test-AMSI.ps1 -EnableAMSI + If you want to enable AMSI at organization level. + +.EXAMPLE + .\Test-AMSI.ps1 -EnableAMSI -ServerList Exch1, Exch2 + If you want to enable AMSI in an Exchange Server or Server List at server level. + +.EXAMPLE + .\Test-AMSI.ps1 -EnableAMSI -AllServers + If you want to enable AMSI in all Exchange Server at server level. + +.EXAMPLE + .\Test-AMSI.ps1 -EnableAMSI -AllServers -Sites Site1, Site2 + If you want to enable AMSI in all Exchange Server in a site or sites at server level. + +.EXAMPLE + .\Test-AMSI.ps1 -DisableAMSI + If you want to disable AMSI on the Exchange Server. + +.EXAMPLE + .\Test-AMSI.ps1 -DisableAMSI -ServerList Exch1, Exch2 + If you want to disable AMSI in an Exchange Server or Server List at server level. + +.EXAMPLE + .\Test-AMSI.ps1 -DisableAMSI -AllServers + If you want to disable AMSI in all Exchange Server at server level. + +.EXAMPLE + .\Test-AMSI.ps1 -DisableAMSI -AllServers -Sites Site1, Site2 + If you want to disable AMSI in all Exchange Server in a site or sites at server level. + +.EXAMPLE + .\Test-AMSI.ps1 -RestartIIS + If you want to restart the Internet Information Services (IIS). + +.EXAMPLE + .\Test-AMSI.ps1 -RestartIIS -Force + If you want to restart the Internet Information Services (IIS) without confirmation. + +#> + +[CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "TestAMSI", HelpUri = "https://microsoft.github.io/CSS-Exchange/Admin/Test-AMSI/")] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWMICmdlet', '', Justification = 'Used Get-WmiObject just in case Get-CimInstance does not get the Windows version as a fallback.')] param( - [Parameter(ParameterSetName = 'TestAMSI', Mandatory = $true, Position = 0)] - [ValidateNotNullOrEmpty()] - [string]$ExchangeServerFQDN, + [Parameter(ParameterSetName = 'TestAMSI', Mandatory = $false)] + [Parameter(ParameterSetName = 'TestAMSIAll', Mandatory = $false)] + [switch]$TestAMSI, + [Parameter(ParameterSetName = 'TestAMSI', Mandatory = $false)] + [Parameter(ParameterSetName = 'TestAMSIAll', Mandatory = $false)] [switch]$IgnoreSSL, - [Parameter(ParameterSetName = 'CheckAMSIProviders', Mandatory = $false)] - [switch]$CheckAMSIProviders, - [Parameter(ParameterSetName = 'EnableAMSI', Mandatory = $false)] + [Parameter(ParameterSetName = 'CheckAMSIConfig', Mandatory = $true)] + [Parameter(ParameterSetName = 'CheckAMSIConfigAll', Mandatory = $true)] + [switch]$CheckAMSIConfig, + [Parameter(ParameterSetName = 'EnableAMSI', Mandatory = $true)] + [Parameter(ParameterSetName = 'EnableAMSIAll', Mandatory = $true)] [switch]$EnableAMSI, - [Parameter(ParameterSetName = 'DisableAMSI', Mandatory = $false)] + [Parameter(ParameterSetName = 'DisableAMSI', Mandatory = $true)] + [Parameter(ParameterSetName = 'DisableAMSIAll', Mandatory = $true)] [switch]$DisableAMSI, + [Parameter(ParameterSetName = 'RestartIIS', Mandatory = $true)] + [Parameter(ParameterSetName = 'RestartIISAll', Mandatory = $true)] + [switch]$RestartIIS, + [Alias("ExchangeServerFQDN")] + [Parameter(ParameterSetName = 'TestAMSI', Mandatory = $false, ValueFromPipeline = $true)] + [Parameter(ParameterSetName = 'EnableAMSI', Mandatory = $false, ValueFromPipeline = $true)] + [Parameter(ParameterSetName = 'DisableAMSI', Mandatory = $false, ValueFromPipeline = $true)] + [Parameter(ParameterSetName = 'CheckAMSIConfig', Mandatory = $false, ValueFromPipeline = $true)] + [Parameter(ParameterSetName = 'RestartIIS', Mandatory = $false, ValueFromPipeline = $true)] + [string[]]$ServerList = $null, + [Parameter(ParameterSetName = 'TestAMSIAll', Mandatory = $true)] + [Parameter(ParameterSetName = 'CheckAMSIConfigAll', Mandatory = $true)] + [Parameter(ParameterSetName = 'EnableAMSIAll', Mandatory = $true)] + [Parameter(ParameterSetName = 'DisableAMSIAll', Mandatory = $true)] + [Parameter(ParameterSetName = 'RestartIISAll', Mandatory = $true)] + [switch]$AllServers, + [Parameter(ParameterSetName = 'TestAMSIAll', Mandatory = $false)] + [Parameter(ParameterSetName = 'CheckAMSIConfigAll', Mandatory = $false)] + [Parameter(ParameterSetName = 'EnableAMSIAll', Mandatory = $false)] + [Parameter(ParameterSetName = 'DisableAMSIAll', Mandatory = $false)] + [Parameter(ParameterSetName = 'RestartIISAll', Mandatory = $false)] + [string[]]$Sites = $null, [Parameter(ParameterSetName = 'RestartIIS', Mandatory = $false)] - [switch]$RestartIIS + [Parameter(ParameterSetName = 'RestartIISAll', Mandatory = $false)] + [switch]$Force ) -function Confirm-Administrator { - $currentPrincipal = New-Object Security.Principal.WindowsPrincipal( [Security.Principal.WindowsIdentity]::GetCurrent() ) - if ($currentPrincipal.IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator )) { - return $true - } else { - return $false - } -} +begin { -function SetCertificateValidationBehavior { - [CmdletBinding()] - param ( - [Parameter(ParameterSetName = "Ignore", Mandatory = $true)] - [switch] - $Ignore, - - [Parameter(ParameterSetName = "Default", Mandatory = $true)] - [switch] - $Default - ) - - if ($Ignore) { - Add-Type @" - using System; - using System.Net; - using System.Net.Security; - using System.Security.Cryptography.X509Certificates; - public class ServerCertificateValidationBehavior + Add-Type @" + using System; + using System.Net; + using System.Net.Security; + using System.Security.Cryptography.X509Certificates; + public class ServerCertificateValidationBehavior + { + public static void Ignore() { - public static void Ignore() - { - ServicePointManager.ServerCertificateValidationCallback += - delegate(Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) - { - return true; - }; - } + ServicePointManager.ServerCertificateValidationCallback += + delegate(Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) + { + return true; + }; } -"@ - [ServerCertificateValidationBehavior]::Ignore() - } else { - [Net.ServicePointManager]::ServerCertificateValidationCallback = $null } -} +"@ + . $PSScriptRoot\..\Shared\Confirm-Administrator.ps1 + . $PSScriptRoot\..\Shared\Confirm-ExchangeShell.ps1 -function Test-AMSI { - $msgNewLine = "`n" - $currentForegroundColor = $host.ui.RawUI.ForegroundColor - if (-not (Confirm-Administrator)) { - Write-Output $msgNewLine - Write-Warning "This script needs to be executed in elevated mode. Start the Exchange Management Shell as an Administrator and try again." - $Error.Clear() - Start-Sleep -Seconds 2 - exit - } - $dateTime = Get-Date - $installPath = (Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ExchangeServer\v15\Setup -ErrorAction SilentlyContinue).MsiInstallPath - if ($ExchangeServerFQDN) { - try { - if ($IgnoreSSL) { - SetCertificateValidationBehavior -Ignore - } + function CheckServerAMSI { + param( + [Parameter(Mandatory = $true)] + [string]$ExchangeServer, + [Parameter(Mandatory = $false)] + [switch]$isServer + ) + try { $CookieContainer = New-Object Microsoft.PowerShell.Commands.WebRequestSession - $Cookie = New-Object System.Net.Cookie("X-BEResource", "a]@$($ExchangeServerFQDN):444/ecp/proxyLogon.ecp#~1941997017", "/", "$ExchangeServerFQDN") + $Cookie = New-Object System.Net.Cookie("X-BEResource", "a]@$($ExchangeServer):444/ecp/proxyLogon.ecp#~1941997017", "/", "$ExchangeServer") $CookieContainer.Cookies.Add($Cookie) - Invoke-WebRequest https://$ExchangeServerFQDN/ecp/x.js -Method POST -Headers @{"Host" = "$ExchangeServerFQDN" } -WebSession $CookieContainer + $testTime = Get-Date + Write-Host "Starting test at $($testTime -f "yyyy-MM-dd HH:mm:ss")" + if ($IgnoreSSL -and ![System.Net.ServicePointManager]::ServerCertificateValidationCallback ) { + [ServerCertificateValidationBehavior]::Ignore() + } + Invoke-WebRequest https://$ExchangeServer/ecp/x.js -Method POST -Headers @{"Host" = "$ExchangeServer" } -WebSession $CookieContainer } catch [System.Net.WebException] { - if ($_.Exception.Message -notlike "*: (400)*") { - $Message = ($_.Exception.Message).ToString().Trim() - Write-Output $msgNewLine - Write-Error $Message + $Message = ($_.Exception.Message).ToString().Trim() + $currentForegroundColor = $host.ui.RawUI.ForegroundColor + if ( $_.Exception.Message -eq "The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.") { + $host.ui.RawUI.ForegroundColor = "Red" + Write-Output $Message $host.ui.RawUI.ForegroundColor = "Yellow" - Write-Output "If you are using Microsoft Defender then AMSI may be disabled or you are using a AntiVirus Product that may not be AMSI capable (Please Check with your AntiVirus Provider for Exchange AMSI Support)" + Write-Output "You could use the -IgnoreSSL parameter" $host.ui.RawUI.ForegroundColor = $currentForegroundColor - Write-Output $msgNewLine - } else { - Write-Output $msgNewLine + } elseif ($_.Exception.Message -eq "The remote server returned an error: (400) Bad Request.") { $host.ui.RawUI.ForegroundColor = "Green" Write-Output "We sent an test request to the ECP Virtual Directory of the server requested" - $host.ui.RawUI.ForegroundColor = "Red" + $host.ui.RawUI.ForegroundColor = "Yellow" Write-Output "The remote server returned an error: (400) Bad Request" $host.ui.RawUI.ForegroundColor = "Green" - Write-Output "---------------------------------------------------------------------------------------------------------------" + $bar = "" + 1..($Host.UI.RawUI.WindowSize.Width) | ForEach-Object { $bar += "-" } + Write-Output $bar $host.ui.RawUI.ForegroundColor = "Yellow" Write-Output "This may be indicative of a potential block from AMSI" $host.ui.RawUI.ForegroundColor = "Green" - $msgCheckLogs = "Check your log files located in " + $installPath + "Logging\HttpRequestFiltering\" + $msgCheckLogs = "You can check your log files located in %ExchangeInstallPath%\Logging\HttpRequestFiltering\" Write-Output $msgCheckLogs - $msgDetectedTimeStamp = "for a Detected result around " + $dateTime.ToUniversalTime() + $host.ui.RawUI.ForegroundColor = $currentForegroundColor + $msgDetectedTimeStamp = "You should find result around $((Get-Date).ToUniversalTime().ToString("M/d/yyy h:mm:ss tt")) UTC" Write-Output $msgDetectedTimeStamp + if ( $isServer ) { + Write-Output "" + Write-Host "Checking logs on $server at $($testTime.ToString("M/d/yyy h:mm:ss tt"))" + $HttpRequestFilteringLogFolder = $null + if ( $server.ToLower() -eq $env:COMPUTERNAME.ToLower() ) { + $HttpRequestFilteringLogFolder = Join-Path $env:ExchangeInstallPath "Logging\HttpRequestFiltering\" + } else { + $remoteExchangePath = (Invoke-Command -ComputerName $server -ScriptBlock { (Get-ChildItem Env:ExchangeInstallPath).Value } -ErrorAction SilentlyContinue -ErrorVariable InvokeError) + if ( $remoteExchangePath ) { + $HttpRequestFilteringLogFolder = Join-Path "\\$server\$($remoteExchangePath.Replace(':','$'))" "Logging\HttpRequestFiltering\" + } else { + Write-Warning "Cannot get Remote Exchange installation path on $server" + } + } + if ( Test-Path $HttpRequestFilteringLogFolder -PathType Container ) { + $file = $null + $timeout = (Get-Date).AddMinutes(1) + $detected = $false + do { + $file = $null + Get-ChildItem $HttpRequestFilteringLogFolder | Sort-Object LastWriteTime -Descending | Select-Object -First 1 -Property * | Out-Null + $file = Get-ChildItem $HttpRequestFilteringLogFolder -Filter "HttpRequestFiltering_*.log" | Where-Object { $_.LastWriteTime -ge $testTime } + if ( $file ) { + $csv = Import-Csv $file.FullName + foreach ($line in $csv) { + $DateTime = $null + try { + $DateTime = [DateTime]::ParseExact($line.DateTime, 'M/d/yyyy h:mm:ss tt', $null) + } catch { + Write-Verbose ("We could not parse the date time on: {0}" -f $line) + } + if ( $DateTime ) { + $marginTime = New-TimeSpan -Seconds 5 + if ($testTime.ToUniversalTime().Subtract($DateTime) -lt $marginTime -and $testTime.ToUniversalTime().Subtract($DateTime) -gt - $marginTime ) { + if ( $line.UrlHost.ToLower() -eq $server.ToLower() -and $line.UrlStem.ToLower() -eq '/ecp/x.js'.ToLower() -and $line.ScanResult.ToLower() -eq 'Detected'.ToLower() ) { + Write-Output "" + Write-Host "We found a detection in HttpRequestFiltering logs: " -ForegroundColor Green + Write-Host "$line" + $detected = $true + } + } + } + } + } + Start-Sleep -Seconds 2 + } while ( ( -not $detected ) -and ((Get-Date) -lt $timeout) ) + if ((Get-Date) -ge $timeout) { + Write-Warning "We have not found activity on the server in the last minute." + } + if ( -not $detected ) { + Write-Warning "We have not found a detection." + } + } else { + Write-Host "We could not access HttpRequestFiltering folder on $server" -ForegroundColor Red + } + } else { + Write-Host "Check your log files located in %ExchangeInstallPath%\Logging\HttpRequestFiltering\ in all server that provide $server endpoint" + } + } elseif ( $_.Exception.Message -eq "The remote server returned an error: (500) Internal Server Error.") { + $host.ui.RawUI.ForegroundColor = "Red" + Write-Output $msgNewLine + Write-Output $Message + Write-Output $msgNewLine + $host.ui.RawUI.ForegroundColor = "Yellow" + Write-Output "If you are using Microsoft Defender, RealTime protection could be disabled or then AMSI may be disabled." + Write-Output "If you are using a 3rd Party AntiVirus Product that may not be AMSI capable (Please Check with your AntiVirus Provider for Exchange AMSI Support)" + $host.ui.RawUI.ForegroundColor = $currentForegroundColor + } elseif ( $_.Exception.Message.StartsWith("The remote name could not be resolved:") ) { + $host.ui.RawUI.ForegroundColor = "Red" + Write-Output $msgNewLine + Write-Output $Message + Write-Output $msgNewLine $host.ui.RawUI.ForegroundColor = $currentForegroundColor + } else { + $host.ui.RawUI.ForegroundColor = "Red" + Write-Output $msgNewLine + Write-Output $Message Write-Output $msgNewLine + $host.ui.RawUI.ForegroundColor = "Yellow" + Write-Output "If you are using Microsoft Defender, RealTime protection could be disabled or then AMSI may be disabled." + Write-Output "If you are using a 3rd Party AntiVirus Product that may not be AMSI capable (Please Check with your AntiVirus Provider for Exchange AMSI Support)" + $host.ui.RawUI.ForegroundColor = $currentForegroundColor } } finally { if ($IgnoreSSL) { - SetCertificateValidationBehavior -Default + [System.Net.ServicePointManager]::ServerCertificateValidationCallback = $null + } + } + Write-Host "Ended test at $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")" + try { + Invoke-WebRequest https://$ExchangeServer -TimeoutSec 1 -ErrorAction SilentlyContinue | Out-Null + } catch { + Write-Verbose "We could not connect to https://$ExchangeServer" + } + } + + function GetWindowsMayorVersion { + param( + [Parameter(Mandatory = $true)] + [string]$ExchangeServer + ) + + if ( $ExchangeServer.ToLower() -eq $env:COMPUTERNAME.ToLower() ) { + [System.Environment]::OSVersion.Version.Major + } else { + $temp = $null + $temp = Get-CimInstance -ComputerName $ExchangeServer -ClassName Win32_OperatingSystem -ErrorAction SilentlyContinue + if ( $null -eq $temp ) { + $temp = Get-WmiObject -ComputerName $ExchangeServer -Query 'Select Version from Win32_OperatingSystem' -ErrorAction SilentlyContinue + } + if ( $temp ) { + $temp.Version.Split('.')[0] + } else { + 0 } } - return } - if ($CheckAMSIProviders) { - $AMSI = Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\AMSI\Providers' -Recurse - $AMSI -match '[0-9A-Fa-f\-]{36}' | Out-Null - $Matches.Values | ForEach-Object { Get-ChildItem "HKLM:\SOFTWARE\Classes\ClSid\{$_}" | Format-Table -AutoSize } + + function CheckAMSIProviders { + param( + [Parameter(Mandatory = $true)] + [string]$ExchangeServer + ) + + $AMSIProviders = $null + Write-Output $msgNewLine + if ( $ExchangeServer.ToLower() -eq $env:COMPUTERNAME.ToLower() ) { + Write-Host "Checking local AMSI Provider on $ExchangeServer" + $AMSIProviders = Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\AMSI\Providers' -Recurse -ErrorAction SilentlyContinue + } else { + Write-Host "Checking remote AMSI Provider on $ExchangeServer" + $AMSIProviders = Invoke-Command -ComputerName $ExchangeServer -ScriptBlock { Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\AMSI\Providers' -Recurse -ErrorAction SilentlyContinue } -ErrorAction SilentlyContinue -ErrorVariable InvokeError + if ( $InvokeError ) { + Write-Warning "We could not connect with server $ExchangeServer" + return + } + } + if ( $null -eq $AMSIProviders ) { + Write-Warning "We did not find any AMSI Provider" + } else { + foreach ( $provider in $AMSIProviders) { + $provider -match '[0-9A-Fa-f\-]{36}' | Out-Null + foreach ($m in $Matches.Values ) { + $key = "HKLM:\SOFTWARE\Classes\ClSid\{$m}" + Write-Output $msgNewLine + if ( $ExchangeServer.ToLower() -eq $env:COMPUTERNAME.ToLower() ) { + Write-Host "Local AMSI Providers detection:" -ForegroundColor Green + $providers = Get-ChildItem $key -ErrorAction SilentlyContinue + $providers | Format-Table -AutoSize + $path = $null + $path = ($providers | Where-Object { $_.PSChildName -eq 'InprocServer32' } ).GetValue('') + if ( $path ) { + $WindowsDefenderPath = $path.Substring(1, $path.LastIndexOf("\")) + if ( $WindowsDefenderPath -like '*Windows Defender*') { + Write-Host "Windows Defender with AMSI integration found." -ForegroundColor Green + $checkCmdLet = $null + $checkCmdLet = Get-Command Get-MpComputerStatus -ErrorAction SilentlyContinue + if ($null -eq $checkCmdLet) { + Write-Warning "Get-MpComputerStatus cmdLet is not available" + } else { + if ( (Get-MpComputerStatus).RealTimeProtectionEnabled ) { + Write-Host "Windows Defender has Real Time Protection Enabled" -ForegroundColor Green + } else { + Write-Warning "Windows Defender has Real Time Protection Disabled" + } + } + Write-Host "It should be version 1.1.18300.4 or newest." + if ( Test-Path $WindowsDefenderPath -PathType Container ) { + $folder = Get-ChildItem $WindowsDefenderPath | Sort-Object LastWriteTime -Descending | Select-Object -First 1 + $process = Join-Path $folder.FullName "MpCmdRun.exe" + if ( Test-Path $process -PathType Leaf ) { + $DefenderVersion = (& $process -SignatureUpdate | Where-Object { $_.StartsWith('Engine Version:') }).Split(' ')[2] + if ( [int]$DefenderVersion.Split('.')[0] -gt 1 -or + ( [int]$DefenderVersion.Split('.')[0] -eq 1 -and [int]$DefenderVersion.Split('.')[1] -gt 1 ) -or + ( [int]$DefenderVersion.Split('.')[0] -eq 1 -and [int]$DefenderVersion.Split('.')[1] -eq 1 -and [int]$DefenderVersion.Split('.')[2] -gt 18300 ) -or + ( [int]$DefenderVersion.Split('.')[0] -eq 1 -and [int]$DefenderVersion.Split('.')[1] -eq 1 -and [int]$DefenderVersion.Split('.')[2] -eq 18300 -and [int]$DefenderVersion.Split('.')[3] -ge 4) ) { + Write-Host "Windows Defender version supported for AMSI: $DefenderVersion" -ForegroundColor Green + } else { + Write-Warning "Windows Defender version Non-supported for AMSI: $DefenderVersion" + } + } else { + Write-Warning "We did not find Windows Defender MpCmdRun.exe." + } + } else { + Write-Warning "We did not find Windows Defender Path." + } + } else { + Write-Warning "It is not Windows Defender AV, check with your provider." + } + } else { + Write-Warning "We did not find AMSI providers." + } + } else { + Invoke-Command -ComputerName $ExchangeServer -ScriptBlock { Get-ChildItem $using:key -ErrorAction SilentlyContinue } -ErrorAction SilentlyContinue | Format-Table -AutoSize + $remoteExchangePath = Invoke-Command -ComputerName $server -ScriptBlock { + Write-Host "Remote AMSI Providers detection ($($using:ExchangeServer)): " + $providers = Get-ChildItem $using:key -ErrorAction SilentlyContinue + + $path = $null + $path = ($providers | Where-Object { $_.PSChildName -eq 'InprocServer32' } ).GetValue('') + if ( $path ) { + $WindowsDefenderPath = $path.Substring(1, $path.LastIndexOf("\")) + + if ( $WindowsDefenderPath -like '*Windows Defender*') { + Write-Host "Windows Defender with AMSI integration found." -ForegroundColor Green + Write-Host "It should be version 1.1.18300.4 or newest." + if ( Test-Path $WindowsDefenderPath -PathType Container ) { + $folder = Get-ChildItem $WindowsDefenderPath | Sort-Object LastWriteTime -Descending | Select-Object -First 1 + $process = Join-Path $folder.FullName "MpCmdRun.exe" + if ( Test-Path $process -PathType Leaf ) { + $DefenderVersion = (& $process -SignatureUpdate | Where-Object { $_.StartsWith('Engine Version:') }).Split(' ')[2] + if ( [int]$DefenderVersion.Split('.')[0] -gt 1 -or + ( [int]$DefenderVersion.Split('.')[0] -eq 1 -and [int]$DefenderVersion.Split('.')[1] -gt 1 ) -or + ( [int]$DefenderVersion.Split('.')[0] -eq 1 -and [int]$DefenderVersion.Split('.')[1] -eq 1 -and [int]$DefenderVersion.Split('.')[2] -gt 18300 ) -or + ( [int]$DefenderVersion.Split('.')[0] -eq 1 -and [int]$DefenderVersion.Split('.')[1] -eq 1 -and [int]$DefenderVersion.Split('.')[2] -eq 18300 -and [int]$DefenderVersion.Split('.')[3] -ge 4) ) { + Write-Host "Windows Defender version supported for AMSI: $DefenderVersion" -ForegroundColor Green + } else { + Write-Warning "Windows Defender version Non-supported for AMSI: $DefenderVersion" + } + } else { + Write-Warning "We did not find Windows Defender MpCmdRun.exe." + } + } else { + Write-Warning "We did not find Windows Defender Path." + } + } else { + Write-Warning "It is not Windows Defender AV, check with your provider." + } + } else { + Write-Warning "We did not find AMSI providers." + } + } + $remoteExchangePath + } + } + } + } } - if ($EnableAMSI) { - Remove-SettingOverride -Identity DisablingAMSIScan -Confirm:$false - Get-ExchangeDiagnosticInfo -Process Microsoft.Exchange.Directory.TopologyService -Component VariantConfiguration -Argument Refresh - Write-Warning "Remember to restart IIS for this to take affect. You can accomplish this by running .\Test-AMSI.ps1 -RestartIIS" - return + + function CheckAMSIConfig { + param( + [Parameter(Mandatory = $true)] + [string]$ExchangeServer + ) + $getSO = $null + $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { $_.SectionName -eq 'HttpRequestFiltering' -and $_.Parameters -eq 'Enabled=False' -and $_.Server -contains $ExchangeServer } + if ( $getSO ) { + $getSO | Out-Host + if ( $getSO.Status -eq "Accepted" ) { + Write-Warning "AMSI is Disabled by $($getSO.Identity) SettingOverride for $ExchangeServer" + } else { + Write-Host "We found SettingOverride for $ExchangeServer ($($getSO.Identity))" + Write-Warning "The Status of $($getSO.Identity) is not Accepted. Should not apply for $ExchangeServer." + } + } else { + Write-Output "" + Write-Host "AMSI is Enabled on Server $ExchangeServer. We did not find any Settings Override that remove AMSI." -ForegroundColor Green + if ( $ExchangeServer.ToLower() -eq $env:COMPUTERNAME.ToLower() ) { + $FEEcpWebConfig = Join-Path $env:ExchangeInstallPath "FrontEnd\HttpProxy\ecp\web.config" + $CAEEcpWebConfig = Join-Path $env:ExchangeInstallPath "ClientAccess\ecp\web.config" + } else { + $remoteExchangePath = (Invoke-Command -ComputerName $ExchangeServer -ScriptBlock { (Get-ChildItem Env:ExchangeInstallPath).Value } -ArgumentList $env:ExchangeInstallPath -ErrorAction SilentlyContinue -ErrorVariable InvokeError) + if ( $remoteExchangePath ) { + $FEEcpWebConfig = Join-Path "\\$ExchangeServer\$($remoteExchangePath.Replace(':','$'))" "FrontEnd\HttpProxy\ecp\web.config" + $CAEEcpWebConfig = Join-Path "\\$ExchangeServer\$($remoteExchangePath.Replace(':','$'))" "ClientAccess\ecp\web.config" + } else { + Write-Host "We could not get ExchangeInstallPath on $ExchangeServer." -ForegroundColor Red + Exit + } + } + + if ( Test-Path $FEEcpWebConfig -PathType Leaf) { + $FEFilterModule = $null + $FEFilterModule = Get-Content $FEEcpWebConfig | Where-Object { $_ -match ' Date: Thu, 18 May 2023 15:39:00 +0200 Subject: [PATCH 02/19] Fix lowercaser isse and change the logic on that. --- Admin/Test-AMSI.ps1 | 187 +++++++++++++++++++++++++------------------- 1 file changed, 106 insertions(+), 81 deletions(-) diff --git a/Admin/Test-AMSI.ps1 b/Admin/Test-AMSI.ps1 index dc99a071ce..5f693ec5a5 100644 --- a/Admin/Test-AMSI.ps1 +++ b/Admin/Test-AMSI.ps1 @@ -353,64 +353,27 @@ begin { Write-Output $msgNewLine if ( $ExchangeServer.ToLower() -eq $env:COMPUTERNAME.ToLower() ) { Write-Host "Local AMSI Providers detection:" -ForegroundColor Green + $providers = $null $providers = Get-ChildItem $key -ErrorAction SilentlyContinue - $providers | Format-Table -AutoSize - $path = $null - $path = ($providers | Where-Object { $_.PSChildName -eq 'InprocServer32' } ).GetValue('') - if ( $path ) { - $WindowsDefenderPath = $path.Substring(1, $path.LastIndexOf("\")) - if ( $WindowsDefenderPath -like '*Windows Defender*') { - Write-Host "Windows Defender with AMSI integration found." -ForegroundColor Green - $checkCmdLet = $null - $checkCmdLet = Get-Command Get-MpComputerStatus -ErrorAction SilentlyContinue - if ($null -eq $checkCmdLet) { - Write-Warning "Get-MpComputerStatus cmdLet is not available" - } else { - if ( (Get-MpComputerStatus).RealTimeProtectionEnabled ) { - Write-Host "Windows Defender has Real Time Protection Enabled" -ForegroundColor Green - } else { - Write-Warning "Windows Defender has Real Time Protection Disabled" - } - } - Write-Host "It should be version 1.1.18300.4 or newest." - if ( Test-Path $WindowsDefenderPath -PathType Container ) { - $folder = Get-ChildItem $WindowsDefenderPath | Sort-Object LastWriteTime -Descending | Select-Object -First 1 - $process = Join-Path $folder.FullName "MpCmdRun.exe" - if ( Test-Path $process -PathType Leaf ) { - $DefenderVersion = (& $process -SignatureUpdate | Where-Object { $_.StartsWith('Engine Version:') }).Split(' ')[2] - if ( [int]$DefenderVersion.Split('.')[0] -gt 1 -or - ( [int]$DefenderVersion.Split('.')[0] -eq 1 -and [int]$DefenderVersion.Split('.')[1] -gt 1 ) -or - ( [int]$DefenderVersion.Split('.')[0] -eq 1 -and [int]$DefenderVersion.Split('.')[1] -eq 1 -and [int]$DefenderVersion.Split('.')[2] -gt 18300 ) -or - ( [int]$DefenderVersion.Split('.')[0] -eq 1 -and [int]$DefenderVersion.Split('.')[1] -eq 1 -and [int]$DefenderVersion.Split('.')[2] -eq 18300 -and [int]$DefenderVersion.Split('.')[3] -ge 4) ) { - Write-Host "Windows Defender version supported for AMSI: $DefenderVersion" -ForegroundColor Green - } else { - Write-Warning "Windows Defender version Non-supported for AMSI: $DefenderVersion" - } - } else { - Write-Warning "We did not find Windows Defender MpCmdRun.exe." - } - } else { - Write-Warning "We did not find Windows Defender Path." - } - } else { - Write-Warning "It is not Windows Defender AV, check with your provider." - } - } else { - Write-Warning "We did not find AMSI providers." - } - } else { - Invoke-Command -ComputerName $ExchangeServer -ScriptBlock { Get-ChildItem $using:key -ErrorAction SilentlyContinue } -ErrorAction SilentlyContinue | Format-Table -AutoSize - $remoteExchangePath = Invoke-Command -ComputerName $server -ScriptBlock { - Write-Host "Remote AMSI Providers detection ($($using:ExchangeServer)): " - $providers = Get-ChildItem $using:key -ErrorAction SilentlyContinue - + if ($providers) { + $providers | Format-Table -AutoSize $path = $null $path = ($providers | Where-Object { $_.PSChildName -eq 'InprocServer32' } ).GetValue('') if ( $path ) { $WindowsDefenderPath = $path.Substring(1, $path.LastIndexOf("\")) - if ( $WindowsDefenderPath -like '*Windows Defender*') { Write-Host "Windows Defender with AMSI integration found." -ForegroundColor Green + $checkCmdLet = $null + $checkCmdLet = Get-Command Get-MpComputerStatus -ErrorAction SilentlyContinue + if ($null -eq $checkCmdLet) { + Write-Warning "Get-MpComputerStatus cmdLet is not available" + } else { + if ( (Get-MpComputerStatus).RealTimeProtectionEnabled ) { + Write-Host "Windows Defender has Real Time Protection Enabled" -ForegroundColor Green + } else { + Write-Warning "Windows Defender has Real Time Protection Disabled" + } + } Write-Host "It should be version 1.1.18300.4 or newest." if ( Test-Path $WindowsDefenderPath -PathType Container ) { $folder = Get-ChildItem $WindowsDefenderPath | Sort-Object LastWriteTime -Descending | Select-Object -First 1 @@ -437,8 +400,64 @@ begin { } else { Write-Warning "We did not find AMSI providers." } + } else { + Write-Host "We did not find any AMSI provider" -ForegroundColor Red + } + } else { + Write-Host "Remote AMSI Providers detection:" -ForegroundColor Green + $providers = $null + $providers = Invoke-Command -ComputerName $ExchangeServer -ScriptBlock { Get-ChildItem $using:key -ErrorAction SilentlyContinue } -ErrorAction SilentlyContinue | Format-Table -AutoSize + if ($providers) { + $providers | Format-Table -AutoSize + Invoke-Command -ComputerName $server -ScriptBlock { + $providers = Get-ChildItem $using:key -ErrorAction SilentlyContinue + $path = $null + $path = ($providers | Where-Object { $_.PSChildName -eq 'InprocServer32' } ).GetValue('') + if ( $path ) { + $WindowsDefenderPath = $path.Substring(1, $path.LastIndexOf("\")) + if ( $WindowsDefenderPath -like '*Windows Defender*') { + Write-Host "Windows Defender with AMSI integration found." -ForegroundColor Green + $checkCmdLet = $null + $checkCmdLet = Get-Command Get-MpComputerStatus -ErrorAction SilentlyContinue + if ($null -eq $checkCmdLet) { + Write-Warning "Get-MpComputerStatus cmdLet is not available" + } else { + if ( (Get-MpComputerStatus).RealTimeProtectionEnabled ) { + Write-Host "Windows Defender has Real Time Protection Enabled" -ForegroundColor Green + } else { + Write-Warning "Windows Defender has Real Time Protection Disabled" + } + } + Write-Host "It should be version 1.1.18300.4 or newest." + if ( Test-Path $WindowsDefenderPath -PathType Container ) { + $folder = Get-ChildItem $WindowsDefenderPath | Sort-Object LastWriteTime -Descending | Select-Object -First 1 + $process = Join-Path $folder.FullName "MpCmdRun.exe" + if ( Test-Path $process -PathType Leaf ) { + $DefenderVersion = (& $process -SignatureUpdate | Where-Object { $_.StartsWith('Engine Version:') }).Split(' ')[2] + if ( [int]$DefenderVersion.Split('.')[0] -gt 1 -or + ( [int]$DefenderVersion.Split('.')[0] -eq 1 -and [int]$DefenderVersion.Split('.')[1] -gt 1 ) -or + ( [int]$DefenderVersion.Split('.')[0] -eq 1 -and [int]$DefenderVersion.Split('.')[1] -eq 1 -and [int]$DefenderVersion.Split('.')[2] -gt 18300 ) -or + ( [int]$DefenderVersion.Split('.')[0] -eq 1 -and [int]$DefenderVersion.Split('.')[1] -eq 1 -and [int]$DefenderVersion.Split('.')[2] -eq 18300 -and [int]$DefenderVersion.Split('.')[3] -ge 4) ) { + Write-Host "Windows Defender version supported for AMSI: $DefenderVersion" -ForegroundColor Green + } else { + Write-Warning "Windows Defender version Non-supported for AMSI: $DefenderVersion" + } + } else { + Write-Warning "We did not find Windows Defender MpCmdRun.exe." + } + } else { + Write-Warning "We did not find Windows Defender Path." + } + } else { + Write-Warning "It is not Windows Defender AV, check with your provider." + } + } else { + Write-Warning "We did not find AMSI providers." + } + } -ErrorAction SilentlyContinue + } else { + Write-Host "We did not find any AMSI provider" -ForegroundColor Red } - $remoteExchangePath } } } @@ -463,50 +482,56 @@ begin { } else { Write-Output "" Write-Host "AMSI is Enabled on Server $ExchangeServer. We did not find any Settings Override that remove AMSI." -ForegroundColor Green + + $FEEcpWebConfig = $null + $CAEEcpWebConfig = $null if ( $ExchangeServer.ToLower() -eq $env:COMPUTERNAME.ToLower() ) { - $FEEcpWebConfig = Join-Path $env:ExchangeInstallPath "FrontEnd\HttpProxy\ecp\web.config" - $CAEEcpWebConfig = Join-Path $env:ExchangeInstallPath "ClientAccess\ecp\web.config" + if ( $env:ExchangeInstallPath ) { + $FEEcpWebConfig = Join-Path $env:ExchangeInstallPath "FrontEnd\HttpProxy\ecp\web.config" + $CAEEcpWebConfig = Join-Path $env:ExchangeInstallPath "ClientAccess\ecp\web.config" + } } else { $remoteExchangePath = (Invoke-Command -ComputerName $ExchangeServer -ScriptBlock { (Get-ChildItem Env:ExchangeInstallPath).Value } -ArgumentList $env:ExchangeInstallPath -ErrorAction SilentlyContinue -ErrorVariable InvokeError) if ( $remoteExchangePath ) { $FEEcpWebConfig = Join-Path "\\$ExchangeServer\$($remoteExchangePath.Replace(':','$'))" "FrontEnd\HttpProxy\ecp\web.config" $CAEEcpWebConfig = Join-Path "\\$ExchangeServer\$($remoteExchangePath.Replace(':','$'))" "ClientAccess\ecp\web.config" - } else { - Write-Host "We could not get ExchangeInstallPath on $ExchangeServer." -ForegroundColor Red - Exit } } - if ( Test-Path $FEEcpWebConfig -PathType Leaf) { - $FEFilterModule = $null - $FEFilterModule = Get-Content $FEEcpWebConfig | Where-Object { $_ -match ' Date: Tue, 23 May 2023 13:37:07 +0200 Subject: [PATCH 03/19] Included the changes requested --- Admin/Test-AMSI.ps1 | 461 ++++++++++++++++++++-------------------- docs/Admin/Test-AMSI.md | 2 +- 2 files changed, 237 insertions(+), 226 deletions(-) diff --git a/Admin/Test-AMSI.ps1 b/Admin/Test-AMSI.ps1 index 5f693ec5a5..1e7889e2c9 100644 --- a/Admin/Test-AMSI.ps1 +++ b/Admin/Test-AMSI.ps1 @@ -50,7 +50,7 @@ If you need to test and ignoring the certificate check. .EXAMPLE - .\Test-AMSI.ps1 -CheckAMSIProviders + .\Test-AMSI.ps1 CheckAMSIConfig If you want to see what AMSI Providers are installed on the local machine. .EXAMPLE @@ -142,25 +142,23 @@ param( begin { - Add-Type @" - using System; - using System.Net; - using System.Net.Security; - using System.Security.Cryptography.X509Certificates; - public class ServerCertificateValidationBehavior - { - public static void Ignore() - { - ServicePointManager.ServerCertificateValidationCallback += - delegate(Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) - { - return true; - }; - } - } -"@ . $PSScriptRoot\..\Shared\Confirm-Administrator.ps1 . $PSScriptRoot\..\Shared\Confirm-ExchangeShell.ps1 + . $PSScriptRoot\..\Shared\Invoke-ScriptBlockHandler.ps1 + . $PSScriptRoot\..\Shared\CertificateFunctions\Enable-TrustAnyCertificateCallback.ps1 + + function Get-ExchangeInstallPath { + param( + [Parameter(Mandatory = $false)] + [string]$ExchangeServer + ) + $getMSIInstallPathSB = { (Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ExchangeServer\v15\Setup -ErrorAction SilentlyContinue).MsiInstallPath } + if ($ExchangeServer) { + Invoke-ScriptBlockHandler -ComputerName $ExchangeServer -ScriptBlock $getMSIInstallPathSB + } else { + Invoke-ScriptBlockHandler -ComputerName $env:COMPUTERNAME -ScriptBlock $getMSIInstallPathSB + } + } function CheckServerAMSI { param( @@ -177,7 +175,7 @@ begin { $testTime = Get-Date Write-Host "Starting test at $($testTime -f "yyyy-MM-dd HH:mm:ss")" if ($IgnoreSSL -and ![System.Net.ServicePointManager]::ServerCertificateValidationCallback ) { - [ServerCertificateValidationBehavior]::Ignore() + Enable-TrustAnyCertificateCallback } Invoke-WebRequest https://$ExchangeServer/ecp/x.js -Method POST -Headers @{"Host" = "$ExchangeServer" } -WebSession $CookieContainer } catch [System.Net.WebException] { @@ -185,49 +183,54 @@ begin { $currentForegroundColor = $host.ui.RawUI.ForegroundColor if ( $_.Exception.Message -eq "The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.") { $host.ui.RawUI.ForegroundColor = "Red" - Write-Output $Message + Write-Host $Message $host.ui.RawUI.ForegroundColor = "Yellow" - Write-Output "You could use the -IgnoreSSL parameter" + Write-Host "You could use the -IgnoreSSL parameter" $host.ui.RawUI.ForegroundColor = $currentForegroundColor } elseif ($_.Exception.Message -eq "The remote server returned an error: (400) Bad Request.") { $host.ui.RawUI.ForegroundColor = "Green" - Write-Output "We sent an test request to the ECP Virtual Directory of the server requested" + Write-Host "We sent an test request to the ECP Virtual Directory of the server requested" $host.ui.RawUI.ForegroundColor = "Yellow" - Write-Output "The remote server returned an error: (400) Bad Request" + Write-Host "The remote server returned an error: (400) Bad Request" $host.ui.RawUI.ForegroundColor = "Green" - $bar = "" - 1..($Host.UI.RawUI.WindowSize.Width) | ForEach-Object { $bar += "-" } - Write-Output $bar $host.ui.RawUI.ForegroundColor = "Yellow" - Write-Output "This may be indicative of a potential block from AMSI" + Write-Host "This may be indicative of a potential block from AMSI" $host.ui.RawUI.ForegroundColor = "Green" - $msgCheckLogs = "You can check your log files located in %ExchangeInstallPath%\Logging\HttpRequestFiltering\" - Write-Output $msgCheckLogs + if ($isServer) { + $installPath = Get-ExchangeInstallPath -ExchangeServer $ExchangeServer + $msgCheckLogs = "You can check your log files located in $($installPath)Logging\HttpRequestFiltering\" + } else { + $msgCheckLogs = "You can check your log files located in %ExchangeInstallPath%\Logging\HttpRequestFiltering\ in all server included in $ExchangeServer endpoint" + } + Write-Host $msgCheckLogs $host.ui.RawUI.ForegroundColor = $currentForegroundColor $msgDetectedTimeStamp = "You should find result around $((Get-Date).ToUniversalTime().ToString("M/d/yyy h:mm:ss tt")) UTC" - Write-Output $msgDetectedTimeStamp + Write-Host $msgDetectedTimeStamp if ( $isServer ) { - Write-Output "" + Write-Host "" Write-Host "Checking logs on $server at $($testTime.ToString("M/d/yyy h:mm:ss tt"))" $HttpRequestFilteringLogFolder = $null - if ( $server.ToLower() -eq $env:COMPUTERNAME.ToLower() ) { - $HttpRequestFilteringLogFolder = Join-Path $env:ExchangeInstallPath "Logging\HttpRequestFiltering\" - } else { - $remoteExchangePath = (Invoke-Command -ComputerName $server -ScriptBlock { (Get-ChildItem Env:ExchangeInstallPath).Value } -ErrorAction SilentlyContinue -ErrorVariable InvokeError) - if ( $remoteExchangePath ) { - $HttpRequestFilteringLogFolder = Join-Path "\\$server\$($remoteExchangePath.Replace(':','$'))" "Logging\HttpRequestFiltering\" + $ExchangePath = Get-ExchangeInstallPath -ExchangeServer $server + + if ( $ExchangePath ) { + if ( $server.ToLower() -eq $env:COMPUTERNAME.ToLower() ) { + $HttpRequestFilteringLogFolder = Join-Path $ExchangePath "Logging\HttpRequestFiltering\" } else { - Write-Warning "Cannot get Remote Exchange installation path on $server" + $HttpRequestFilteringLogFolder = Join-Path "\\$server\$($ExchangePath.Replace(':','$'))" "Logging\HttpRequestFiltering\" } + } else { + Write-Host "Cannot get Exchange installation path on $server" -ForegroundColor Red } + if ( Test-Path $HttpRequestFilteringLogFolder -PathType Container ) { $file = $null $timeout = (Get-Date).AddMinutes(1) $detected = $false + $marginTime = New-TimeSpan -Seconds 5 do { + Start-Sleep -Seconds 2 $file = $null - Get-ChildItem $HttpRequestFilteringLogFolder | Sort-Object LastWriteTime -Descending | Select-Object -First 1 -Property * | Out-Null - $file = Get-ChildItem $HttpRequestFilteringLogFolder -Filter "HttpRequestFiltering_*.log" | Where-Object { $_.LastWriteTime -ge $testTime } + $file = Get-ChildItem $HttpRequestFilteringLogFolder -Filter "HttpRequestFiltering_*.log" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 -Property * if ( $file ) { $csv = Import-Csv $file.FullName foreach ($line in $csv) { @@ -238,10 +241,12 @@ begin { Write-Verbose ("We could not parse the date time on: {0}" -f $line) } if ( $DateTime ) { - $marginTime = New-TimeSpan -Seconds 5 - if ($testTime.ToUniversalTime().Subtract($DateTime) -lt $marginTime -and $testTime.ToUniversalTime().Subtract($DateTime) -gt - $marginTime ) { - if ( $line.UrlHost.ToLower() -eq $server.ToLower() -and $line.UrlStem.ToLower() -eq '/ecp/x.js'.ToLower() -and $line.ScanResult.ToLower() -eq 'Detected'.ToLower() ) { - Write-Output "" + if ( ( $testTime.ToUniversalTime().Subtract($DateTime) -lt $marginTime ) -and + ($testTime.ToUniversalTime().Subtract($DateTime) -gt - $marginTime ) ) { + if ( ($line.UrlHost.ToLower() -eq $server.ToLower() ) -and + ($line.UrlStem.ToLower() -eq '/ecp/x.js'.ToLower() ) -and + ($line.ScanResult.ToLower() -eq 'Detected'.ToLower() ) ) { + Write-Host "" Write-Host "We found a detection in HttpRequestFiltering logs: " -ForegroundColor Green Write-Host "$line" $detected = $true @@ -250,7 +255,6 @@ begin { } } } - Start-Sleep -Seconds 2 } while ( ( -not $detected ) -and ((Get-Date) -lt $timeout) ) if ((Get-Date) -ge $timeout) { Write-Warning "We have not found activity on the server in the last minute." @@ -266,40 +270,35 @@ begin { } } elseif ( $_.Exception.Message -eq "The remote server returned an error: (500) Internal Server Error.") { $host.ui.RawUI.ForegroundColor = "Red" - Write-Output $msgNewLine - Write-Output $Message - Write-Output $msgNewLine + Write-Host $msgNewLine + Write-Host $Message + Write-Host $msgNewLine $host.ui.RawUI.ForegroundColor = "Yellow" - Write-Output "If you are using Microsoft Defender, RealTime protection could be disabled or then AMSI may be disabled." - Write-Output "If you are using a 3rd Party AntiVirus Product that may not be AMSI capable (Please Check with your AntiVirus Provider for Exchange AMSI Support)" + Write-Host "If you are using Microsoft Defender, RealTime protection could be disabled or then AMSI may be disabled." + Write-Host "If you are using a 3rd Party AntiVirus Product that may not be AMSI capable (Please Check with your AntiVirus Provider for Exchange AMSI Support)" $host.ui.RawUI.ForegroundColor = $currentForegroundColor } elseif ( $_.Exception.Message.StartsWith("The remote name could not be resolved:") ) { $host.ui.RawUI.ForegroundColor = "Red" - Write-Output $msgNewLine - Write-Output $Message - Write-Output $msgNewLine + Write-Host $msgNewLine + Write-Host $Message + Write-Host $msgNewLine $host.ui.RawUI.ForegroundColor = $currentForegroundColor } else { $host.ui.RawUI.ForegroundColor = "Red" - Write-Output $msgNewLine - Write-Output $Message - Write-Output $msgNewLine + Write-Host $msgNewLine + Write-Host $Message + Write-Host $msgNewLine $host.ui.RawUI.ForegroundColor = "Yellow" - Write-Output "If you are using Microsoft Defender, RealTime protection could be disabled or then AMSI may be disabled." - Write-Output "If you are using a 3rd Party AntiVirus Product that may not be AMSI capable (Please Check with your AntiVirus Provider for Exchange AMSI Support)" + Write-Host "If you are using Microsoft Defender, RealTime protection could be disabled or then AMSI may be disabled." + Write-Host "If you are using a 3rd Party AntiVirus Product that may not be AMSI capable (Please Check with your AntiVirus Provider for Exchange AMSI Support)" $host.ui.RawUI.ForegroundColor = $currentForegroundColor } } finally { if ($IgnoreSSL) { - [System.Net.ServicePointManager]::ServerCertificateValidationCallback = $null + Disable-TrustAnyCertificateCallback } } Write-Host "Ended test at $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")" - try { - Invoke-WebRequest https://$ExchangeServer -TimeoutSec 1 -ErrorAction SilentlyContinue | Out-Null - } catch { - Write-Verbose "We could not connect to https://$ExchangeServer" - } } function GetWindowsMayorVersion { @@ -324,39 +323,46 @@ begin { } } - function CheckAMSIProviders { + function GetExchangeVersion { + param( + [Parameter(Mandatory = $true)] + [ValidatePattern('Version 15.\d \(Build \d{1,4}.\d{1,2}\)')] + [string]$AdminDisplayVersion + ) + $ExchangeVersion = $null + $ExchangeVersion = [System.Version]::new( [int]($AdminDisplayVersion.Split(' ')[1]).Split('.')[0], + [int]($AdminDisplayVersion.Split(' ')[1]).Split('.')[1], + [int]($AdminDisplayVersion.Split(' ')[3]).Split('.')[0], + [int](($AdminDisplayVersion.Split(' ')[3]).Split('.')[1]).Split(')')[0]) + $ExchangeVersion + } + + function CheckAMSIConfig { param( [Parameter(Mandatory = $true)] [string]$ExchangeServer ) - $AMSIProviders = $null - Write-Output $msgNewLine - if ( $ExchangeServer.ToLower() -eq $env:COMPUTERNAME.ToLower() ) { - Write-Host "Checking local AMSI Provider on $ExchangeServer" + Write-Host "" + Write-Host "AMSI Providers detection:" -ForegroundColor Green + + $AMSIProvidersSB = { $AMSIProviders = Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\AMSI\Providers' -Recurse -ErrorAction SilentlyContinue - } else { - Write-Host "Checking remote AMSI Provider on $ExchangeServer" - $AMSIProviders = Invoke-Command -ComputerName $ExchangeServer -ScriptBlock { Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\AMSI\Providers' -Recurse -ErrorAction SilentlyContinue } -ErrorAction SilentlyContinue -ErrorVariable InvokeError - if ( $InvokeError ) { - Write-Warning "We could not connect with server $ExchangeServer" - return - } - } - if ( $null -eq $AMSIProviders ) { - Write-Warning "We did not find any AMSI Provider" - } else { - foreach ( $provider in $AMSIProviders) { - $provider -match '[0-9A-Fa-f\-]{36}' | Out-Null - foreach ($m in $Matches.Values ) { - $key = "HKLM:\SOFTWARE\Classes\ClSid\{$m}" - Write-Output $msgNewLine - if ( $ExchangeServer.ToLower() -eq $env:COMPUTERNAME.ToLower() ) { - Write-Host "Local AMSI Providers detection:" -ForegroundColor Green + if ($AMSIProviders) { + Write-Host "Providers:" + $AMSIProviders.Name | Out-Host + $providerCount = 0 + foreach ( $provider in $AMSIProviders) { + $provider -match '[0-9A-Fa-f\-]{36}' | Out-Null + foreach ($m in $Matches.Values ) { + $key = "HKLM:\SOFTWARE\Classes\ClSid\{$m}" $providers = $null $providers = Get-ChildItem $key -ErrorAction SilentlyContinue if ($providers) { - $providers | Format-Table -AutoSize + $providerCount++ + Write-Host "" + Write-Host "Provider $($providerCount):" + $providers | Format-Table -AutoSize | Out-Host $path = $null $path = ($providers | Where-Object { $_.PSChildName -eq 'InprocServer32' } ).GetValue('') if ( $path ) { @@ -379,14 +385,19 @@ begin { $folder = Get-ChildItem $WindowsDefenderPath | Sort-Object LastWriteTime -Descending | Select-Object -First 1 $process = Join-Path $folder.FullName "MpCmdRun.exe" if ( Test-Path $process -PathType Leaf ) { - $DefenderVersion = (& $process -SignatureUpdate | Where-Object { $_.StartsWith('Engine Version:') }).Split(' ')[2] - if ( [int]$DefenderVersion.Split('.')[0] -gt 1 -or - ( [int]$DefenderVersion.Split('.')[0] -eq 1 -and [int]$DefenderVersion.Split('.')[1] -gt 1 ) -or - ( [int]$DefenderVersion.Split('.')[0] -eq 1 -and [int]$DefenderVersion.Split('.')[1] -eq 1 -and [int]$DefenderVersion.Split('.')[2] -gt 18300 ) -or - ( [int]$DefenderVersion.Split('.')[0] -eq 1 -and [int]$DefenderVersion.Split('.')[1] -eq 1 -and [int]$DefenderVersion.Split('.')[2] -eq 18300 -and [int]$DefenderVersion.Split('.')[3] -ge 4) ) { - Write-Host "Windows Defender version supported for AMSI: $DefenderVersion" -ForegroundColor Green + $DefenderVersion = $null + $DefenderVersion = [System.Version]::new( (& $process -SignatureUpdate | Where-Object { $_.StartsWith('Engine Version:') }).Split(' ')[2] ) + if ($DefenderVersion) { + if ( ( $DefenderVersion.Major -gt 1 ) -or + ( ( $DefenderVersion.Major -eq 1 ) -and ( $DefenderVersion.Minor -gt 1 ) ) -or + ( ( $DefenderVersion.Major -eq 1 ) -and ( $DefenderVersion.Minor -eq 1 ) -and ( $DefenderVersion.Build -gt 18300 ) ) -or + ( ( $DefenderVersion.Major -eq 1 ) -and ( $DefenderVersion.Minor -eq 1 ) -and ( $DefenderVersion.Build -eq 18300 ) -and ( $DefenderVersion.Revision -ge 4 ) ) ) { + Write-Host "Windows Defender version supported for AMSI: $DefenderVersion" -ForegroundColor Green + } else { + Write-Warning "Windows Defender version Non-supported for AMSI: $DefenderVersion" + } } else { - Write-Warning "Windows Defender version Non-supported for AMSI: $DefenderVersion" + Write-Warning "We could not get Windows Defender version " } } else { Write-Warning "We did not find Windows Defender MpCmdRun.exe." @@ -401,76 +412,25 @@ begin { Write-Warning "We did not find AMSI providers." } } else { - Write-Host "We did not find any AMSI provider" -ForegroundColor Red - } - } else { - Write-Host "Remote AMSI Providers detection:" -ForegroundColor Green - $providers = $null - $providers = Invoke-Command -ComputerName $ExchangeServer -ScriptBlock { Get-ChildItem $using:key -ErrorAction SilentlyContinue } -ErrorAction SilentlyContinue | Format-Table -AutoSize - if ($providers) { - $providers | Format-Table -AutoSize - Invoke-Command -ComputerName $server -ScriptBlock { - $providers = Get-ChildItem $using:key -ErrorAction SilentlyContinue - $path = $null - $path = ($providers | Where-Object { $_.PSChildName -eq 'InprocServer32' } ).GetValue('') - if ( $path ) { - $WindowsDefenderPath = $path.Substring(1, $path.LastIndexOf("\")) - if ( $WindowsDefenderPath -like '*Windows Defender*') { - Write-Host "Windows Defender with AMSI integration found." -ForegroundColor Green - $checkCmdLet = $null - $checkCmdLet = Get-Command Get-MpComputerStatus -ErrorAction SilentlyContinue - if ($null -eq $checkCmdLet) { - Write-Warning "Get-MpComputerStatus cmdLet is not available" - } else { - if ( (Get-MpComputerStatus).RealTimeProtectionEnabled ) { - Write-Host "Windows Defender has Real Time Protection Enabled" -ForegroundColor Green - } else { - Write-Warning "Windows Defender has Real Time Protection Disabled" - } - } - Write-Host "It should be version 1.1.18300.4 or newest." - if ( Test-Path $WindowsDefenderPath -PathType Container ) { - $folder = Get-ChildItem $WindowsDefenderPath | Sort-Object LastWriteTime -Descending | Select-Object -First 1 - $process = Join-Path $folder.FullName "MpCmdRun.exe" - if ( Test-Path $process -PathType Leaf ) { - $DefenderVersion = (& $process -SignatureUpdate | Where-Object { $_.StartsWith('Engine Version:') }).Split(' ')[2] - if ( [int]$DefenderVersion.Split('.')[0] -gt 1 -or - ( [int]$DefenderVersion.Split('.')[0] -eq 1 -and [int]$DefenderVersion.Split('.')[1] -gt 1 ) -or - ( [int]$DefenderVersion.Split('.')[0] -eq 1 -and [int]$DefenderVersion.Split('.')[1] -eq 1 -and [int]$DefenderVersion.Split('.')[2] -gt 18300 ) -or - ( [int]$DefenderVersion.Split('.')[0] -eq 1 -and [int]$DefenderVersion.Split('.')[1] -eq 1 -and [int]$DefenderVersion.Split('.')[2] -eq 18300 -and [int]$DefenderVersion.Split('.')[3] -ge 4) ) { - Write-Host "Windows Defender version supported for AMSI: $DefenderVersion" -ForegroundColor Green - } else { - Write-Warning "Windows Defender version Non-supported for AMSI: $DefenderVersion" - } - } else { - Write-Warning "We did not find Windows Defender MpCmdRun.exe." - } - } else { - Write-Warning "We did not find Windows Defender Path." - } - } else { - Write-Warning "It is not Windows Defender AV, check with your provider." - } - } else { - Write-Warning "We did not find AMSI providers." - } - } -ErrorAction SilentlyContinue - } else { - Write-Host "We did not find any AMSI provider" -ForegroundColor Red + Write-Host "We did not find any AMSI provider on ClSid for $m" -ForegroundColor Red } } } + } else { + Write-Host " We did not find any AMSI provider" -ForegroundColor Red } } - } - function CheckAMSIConfig { - param( - [Parameter(Mandatory = $true)] - [string]$ExchangeServer - ) + Write-Host "" + "Checking AMSI Provider on $ExchangeServer" + Write-Host "" + Invoke-ScriptBlockHandler -ComputerName $ExchangeServer -ScriptBlock $AMSIProvidersSB + $getSO = $null - $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { $_.SectionName -eq 'HttpRequestFiltering' -and $_.Parameters -eq 'Enabled=False' -and $_.Server -contains $ExchangeServer } + $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { + ($_.SectionName -eq 'HttpRequestFiltering') -and + ($_.Parameters -eq 'Enabled=False') -and + ($_.Server -contains $ExchangeServer) } if ( $getSO ) { $getSO | Out-Host if ( $getSO.Status -eq "Accepted" ) { @@ -480,72 +440,81 @@ begin { Write-Warning "The Status of $($getSO.Identity) is not Accepted. Should not apply for $ExchangeServer." } } else { - Write-Output "" - Write-Host "AMSI is Enabled on Server $ExchangeServer. We did not find any Settings Override that remove AMSI." -ForegroundColor Green $FEEcpWebConfig = $null $CAEEcpWebConfig = $null - if ( $ExchangeServer.ToLower() -eq $env:COMPUTERNAME.ToLower() ) { - if ( $env:ExchangeInstallPath ) { - $FEEcpWebConfig = Join-Path $env:ExchangeInstallPath "FrontEnd\HttpProxy\ecp\web.config" - $CAEEcpWebConfig = Join-Path $env:ExchangeInstallPath "ClientAccess\ecp\web.config" - } - } else { - $remoteExchangePath = (Invoke-Command -ComputerName $ExchangeServer -ScriptBlock { (Get-ChildItem Env:ExchangeInstallPath).Value } -ArgumentList $env:ExchangeInstallPath -ErrorAction SilentlyContinue -ErrorVariable InvokeError) - if ( $remoteExchangePath ) { - $FEEcpWebConfig = Join-Path "\\$ExchangeServer\$($remoteExchangePath.Replace(':','$'))" "FrontEnd\HttpProxy\ecp\web.config" - $CAEEcpWebConfig = Join-Path "\\$ExchangeServer\$($remoteExchangePath.Replace(':','$'))" "ClientAccess\ecp\web.config" + + $ExchangePath = Get-ExchangeInstallPath -ExchangeServer $ExchangeServer + + if ( $ExchangePath ) { + if ( $ExchangeServer.ToLower() -eq $env:COMPUTERNAME.ToLower() ) { + $FEEcpWebConfig = Join-Path $ExchangePath "FrontEnd\HttpProxy\ecp\web.config" + $CAEEcpWebConfig = Join-Path $ExchangePath "ClientAccess\ecp\web.config" + } else { + $FEEcpWebConfig = Join-Path "\\$ExchangeServer\$($ExchangePath.Replace(':','$'))" "FrontEnd\HttpProxy\ecp\web.config" + $CAEEcpWebConfig = Join-Path "\\$ExchangeServer\$($ExchangePath.Replace(':','$'))" "ClientAccess\ecp\web.config" } - } - if ( $FEEcpWebConfig -and $CAEEcpWebConfig) { - if ( Test-Path $FEEcpWebConfig -PathType Leaf) { - $FEFilterModule = $null - $FEFilterModule = Get-Content $FEEcpWebConfig | Where-Object { $_ -match ' Date: Tue, 30 May 2023 17:39:59 +0200 Subject: [PATCH 04/19] Changes from the last Requests --- Admin/Test-AMSI.ps1 | 397 +++++++++++++++++++++++--------------------- 1 file changed, 204 insertions(+), 193 deletions(-) diff --git a/Admin/Test-AMSI.ps1 b/Admin/Test-AMSI.ps1 index 1e7889e2c9..dd1545a62e 100644 --- a/Admin/Test-AMSI.ps1 +++ b/Admin/Test-AMSI.ps1 @@ -137,7 +137,20 @@ param( [string[]]$Sites = $null, [Parameter(ParameterSetName = 'RestartIIS', Mandatory = $false)] [Parameter(ParameterSetName = 'RestartIISAll', Mandatory = $false)] - [switch]$Force + [switch]$Force, + [Parameter(ParameterSetName = 'TestAMSI', Mandatory = $false)] + [Parameter(ParameterSetName = 'TestAMSIAll', Mandatory = $false)] + [Parameter(ParameterSetName = 'CheckAMSIConfig', Mandatory = $false)] + [Parameter(ParameterSetName = 'CheckAMSIConfigAll', Mandatory = $false)] + [Parameter(ParameterSetName = 'EnableAMSI', Mandatory = $false)] + [Parameter(ParameterSetName = 'EnableAMSIAll', Mandatory = $false)] + [Parameter(ParameterSetName = 'DisableAMSI', Mandatory = $false)] + [Parameter(ParameterSetName = 'DisableAMSIAll', Mandatory = $false)] + [Parameter(ParameterSetName = 'RestartIIS', Mandatory = $false)] + [Parameter(ParameterSetName = 'RestartIISAll', Mandatory = $false)] + [switch]$SkipVersionCheck, + [Parameter(Mandatory = $true, ParameterSetName = "ScriptUpdateOnly")] + [switch]$ScriptUpdateOnly ) begin { @@ -146,19 +159,8 @@ begin { . $PSScriptRoot\..\Shared\Confirm-ExchangeShell.ps1 . $PSScriptRoot\..\Shared\Invoke-ScriptBlockHandler.ps1 . $PSScriptRoot\..\Shared\CertificateFunctions\Enable-TrustAnyCertificateCallback.ps1 - - function Get-ExchangeInstallPath { - param( - [Parameter(Mandatory = $false)] - [string]$ExchangeServer - ) - $getMSIInstallPathSB = { (Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ExchangeServer\v15\Setup -ErrorAction SilentlyContinue).MsiInstallPath } - if ($ExchangeServer) { - Invoke-ScriptBlockHandler -ComputerName $ExchangeServer -ScriptBlock $getMSIInstallPathSB - } else { - Invoke-ScriptBlockHandler -ComputerName $env:COMPUTERNAME -ScriptBlock $getMSIInstallPathSB - } - } + . $PSScriptRoot\..\Shared\ScriptUpdateFunctions\Test-ScriptVersion.ps1 + . $PSScriptRoot\..\Shared\Get-ExchangeBuildVersionInformation.ps1 function CheckServerAMSI { param( @@ -174,14 +176,14 @@ begin { $CookieContainer.Cookies.Add($Cookie) $testTime = Get-Date Write-Host "Starting test at $($testTime -f "yyyy-MM-dd HH:mm:ss")" - if ($IgnoreSSL -and ![System.Net.ServicePointManager]::ServerCertificateValidationCallback ) { + if ($IgnoreSSL -and ![System.Net.ServicePointManager]::ServerCertificateValidationCallback) { Enable-TrustAnyCertificateCallback } - Invoke-WebRequest https://$ExchangeServer/ecp/x.js -Method POST -Headers @{"Host" = "$ExchangeServer" } -WebSession $CookieContainer + Invoke-WebRequest https://$ExchangeServer/ecp/x.js -Method POST -Headers @{ "Host" = "$ExchangeServer" } -WebSession $CookieContainer } catch [System.Net.WebException] { $Message = ($_.Exception.Message).ToString().Trim() $currentForegroundColor = $host.ui.RawUI.ForegroundColor - if ( $_.Exception.Message -eq "The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.") { + if ($_.Exception.Message -eq "The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.") { $host.ui.RawUI.ForegroundColor = "Red" Write-Host $Message $host.ui.RawUI.ForegroundColor = "Yellow" @@ -197,8 +199,9 @@ begin { Write-Host "This may be indicative of a potential block from AMSI" $host.ui.RawUI.ForegroundColor = "Green" if ($isServer) { - $installPath = Get-ExchangeInstallPath -ExchangeServer $ExchangeServer - $msgCheckLogs = "You can check your log files located in $($installPath)Logging\HttpRequestFiltering\" + $getMSIInstallPathSB = { (Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ExchangeServer\v15\Setup -ErrorAction SilentlyContinue).MsiInstallPath } + $ExchangePath = Invoke-ScriptBlockHandler -ComputerName $ExchangeServer -ScriptBlock $getMSIInstallPathSB + $msgCheckLogs = "You can check your log files located in $($ExchangePath)Logging\HttpRequestFiltering\" } else { $msgCheckLogs = "You can check your log files located in %ExchangeInstallPath%\Logging\HttpRequestFiltering\ in all server included in $ExchangeServer endpoint" } @@ -206,14 +209,13 @@ begin { $host.ui.RawUI.ForegroundColor = $currentForegroundColor $msgDetectedTimeStamp = "You should find result around $((Get-Date).ToUniversalTime().ToString("M/d/yyy h:mm:ss tt")) UTC" Write-Host $msgDetectedTimeStamp - if ( $isServer ) { + if ($isServer) { Write-Host "" Write-Host "Checking logs on $server at $($testTime.ToString("M/d/yyy h:mm:ss tt"))" $HttpRequestFilteringLogFolder = $null - $ExchangePath = Get-ExchangeInstallPath -ExchangeServer $server - if ( $ExchangePath ) { - if ( $server.ToLower() -eq $env:COMPUTERNAME.ToLower() ) { + if ($ExchangePath) { + if ($server.ToLower() -eq $env:COMPUTERNAME.ToLower()) { $HttpRequestFilteringLogFolder = Join-Path $ExchangePath "Logging\HttpRequestFiltering\" } else { $HttpRequestFilteringLogFolder = Join-Path "\\$server\$($ExchangePath.Replace(':','$'))" "Logging\HttpRequestFiltering\" @@ -222,7 +224,7 @@ begin { Write-Host "Cannot get Exchange installation path on $server" -ForegroundColor Red } - if ( Test-Path $HttpRequestFilteringLogFolder -PathType Container ) { + if (Test-Path $HttpRequestFilteringLogFolder -PathType Container) { $file = $null $timeout = (Get-Date).AddMinutes(1) $detected = $false @@ -231,7 +233,7 @@ begin { Start-Sleep -Seconds 2 $file = $null $file = Get-ChildItem $HttpRequestFilteringLogFolder -Filter "HttpRequestFiltering_*.log" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 -Property * - if ( $file ) { + if ($file) { $csv = Import-Csv $file.FullName foreach ($line in $csv) { $DateTime = $null @@ -240,12 +242,12 @@ begin { } catch { Write-Verbose ("We could not parse the date time on: {0}" -f $line) } - if ( $DateTime ) { - if ( ( $testTime.ToUniversalTime().Subtract($DateTime) -lt $marginTime ) -and - ($testTime.ToUniversalTime().Subtract($DateTime) -gt - $marginTime ) ) { - if ( ($line.UrlHost.ToLower() -eq $server.ToLower() ) -and - ($line.UrlStem.ToLower() -eq '/ecp/x.js'.ToLower() ) -and - ($line.ScanResult.ToLower() -eq 'Detected'.ToLower() ) ) { + if ($DateTime) { + if (($testTime.ToUniversalTime().Subtract($DateTime) -lt $marginTime) -and + ($testTime.ToUniversalTime().Subtract($DateTime) -gt - $marginTime)) { + if (($line.UrlHost.ToLower() -eq $server.ToLower()) -and + ($line.UrlStem.ToLower() -eq '/ecp/x.js'.ToLower()) -and + ($line.ScanResult.ToLower() -eq 'Detected'.ToLower())) { Write-Host "" Write-Host "We found a detection in HttpRequestFiltering logs: " -ForegroundColor Green Write-Host "$line" @@ -255,11 +257,11 @@ begin { } } } - } while ( ( -not $detected ) -and ((Get-Date) -lt $timeout) ) + } while ((-not $detected) -and ((Get-Date) -lt $timeout)) if ((Get-Date) -ge $timeout) { Write-Warning "We have not found activity on the server in the last minute." } - if ( -not $detected ) { + if (-not $detected) { Write-Warning "We have not found a detection." } } else { @@ -268,7 +270,7 @@ begin { } else { Write-Host "Check your log files located in %ExchangeInstallPath%\Logging\HttpRequestFiltering\ in all server that provide $server endpoint" } - } elseif ( $_.Exception.Message -eq "The remote server returned an error: (500) Internal Server Error.") { + } elseif ($_.Exception.Message -eq "The remote server returned an error: (500) Internal Server Error.") { $host.ui.RawUI.ForegroundColor = "Red" Write-Host $msgNewLine Write-Host $Message @@ -277,7 +279,7 @@ begin { Write-Host "If you are using Microsoft Defender, RealTime protection could be disabled or then AMSI may be disabled." Write-Host "If you are using a 3rd Party AntiVirus Product that may not be AMSI capable (Please Check with your AntiVirus Provider for Exchange AMSI Support)" $host.ui.RawUI.ForegroundColor = $currentForegroundColor - } elseif ( $_.Exception.Message.StartsWith("The remote name could not be resolved:") ) { + } elseif ($_.Exception.Message.StartsWith("The remote name could not be resolved:")) { $host.ui.RawUI.ForegroundColor = "Red" Write-Host $msgNewLine Write-Host $Message @@ -295,48 +297,17 @@ begin { } } finally { if ($IgnoreSSL) { - Disable-TrustAnyCertificateCallback + [System.Net.ServicePointManager]::ServerCertificateValidationCallback = $null + try { + Invoke-WebRequest https://$ExchangeServer -TimeoutSec 1 -ErrorAction SilentlyContinue | Out-Null + } catch { + Write-Verbose "We could not connect to https://$ExchangeServer (Expected)" + } } } Write-Host "Ended test at $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")" } - function GetWindowsMayorVersion { - param( - [Parameter(Mandatory = $true)] - [string]$ExchangeServer - ) - - if ( $ExchangeServer.ToLower() -eq $env:COMPUTERNAME.ToLower() ) { - [System.Environment]::OSVersion.Version.Major - } else { - $temp = $null - $temp = Get-CimInstance -ComputerName $ExchangeServer -ClassName Win32_OperatingSystem -ErrorAction SilentlyContinue - if ( $null -eq $temp ) { - $temp = Get-WmiObject -ComputerName $ExchangeServer -Query 'Select Version from Win32_OperatingSystem' -ErrorAction SilentlyContinue - } - if ( $temp ) { - $temp.Version.Split('.')[0] - } else { - 0 - } - } - } - - function GetExchangeVersion { - param( - [Parameter(Mandatory = $true)] - [ValidatePattern('Version 15.\d \(Build \d{1,4}.\d{1,2}\)')] - [string]$AdminDisplayVersion - ) - $ExchangeVersion = $null - $ExchangeVersion = [System.Version]::new( [int]($AdminDisplayVersion.Split(' ')[1]).Split('.')[0], - [int]($AdminDisplayVersion.Split(' ')[1]).Split('.')[1], - [int]($AdminDisplayVersion.Split(' ')[3]).Split('.')[0], - [int](($AdminDisplayVersion.Split(' ')[3]).Split('.')[1]).Split(')')[0]) - $ExchangeVersion - } - function CheckAMSIConfig { param( [Parameter(Mandatory = $true)] @@ -352,9 +323,9 @@ begin { Write-Host "Providers:" $AMSIProviders.Name | Out-Host $providerCount = 0 - foreach ( $provider in $AMSIProviders) { + foreach ($provider in $AMSIProviders) { $provider -match '[0-9A-Fa-f\-]{36}' | Out-Null - foreach ($m in $Matches.Values ) { + foreach ($m in $Matches.Values) { $key = "HKLM:\SOFTWARE\Classes\ClSid\{$m}" $providers = $null $providers = Get-ChildItem $key -ErrorAction SilentlyContinue @@ -364,34 +335,34 @@ begin { Write-Host "Provider $($providerCount):" $providers | Format-Table -AutoSize | Out-Host $path = $null - $path = ($providers | Where-Object { $_.PSChildName -eq 'InprocServer32' } ).GetValue('') - if ( $path ) { + $path = ($providers | Where-Object { $_.PSChildName -eq 'InprocServer32' }).GetValue('') + if ($path) { $WindowsDefenderPath = $path.Substring(1, $path.LastIndexOf("\")) - if ( $WindowsDefenderPath -like '*Windows Defender*') { + if ($WindowsDefenderPath -like '*Windows Defender*') { Write-Host "Windows Defender with AMSI integration found." -ForegroundColor Green $checkCmdLet = $null $checkCmdLet = Get-Command Get-MpComputerStatus -ErrorAction SilentlyContinue if ($null -eq $checkCmdLet) { Write-Warning "Get-MpComputerStatus cmdLet is not available" } else { - if ( (Get-MpComputerStatus).RealTimeProtectionEnabled ) { + if ((Get-MpComputerStatus).RealTimeProtectionEnabled) { Write-Host "Windows Defender has Real Time Protection Enabled" -ForegroundColor Green } else { Write-Warning "Windows Defender has Real Time Protection Disabled" } } Write-Host "It should be version 1.1.18300.4 or newest." - if ( Test-Path $WindowsDefenderPath -PathType Container ) { + if (Test-Path $WindowsDefenderPath -PathType Container) { $folder = Get-ChildItem $WindowsDefenderPath | Sort-Object LastWriteTime -Descending | Select-Object -First 1 $process = Join-Path $folder.FullName "MpCmdRun.exe" - if ( Test-Path $process -PathType Leaf ) { + if (Test-Path $process -PathType Leaf) { $DefenderVersion = $null - $DefenderVersion = [System.Version]::new( (& $process -SignatureUpdate | Where-Object { $_.StartsWith('Engine Version:') }).Split(' ')[2] ) + $DefenderVersion = [System.Version]::new((& $process -SignatureUpdate | Where-Object { $_.StartsWith('Engine Version:') }).Split(' ')[2]) if ($DefenderVersion) { - if ( ( $DefenderVersion.Major -gt 1 ) -or - ( ( $DefenderVersion.Major -eq 1 ) -and ( $DefenderVersion.Minor -gt 1 ) ) -or - ( ( $DefenderVersion.Major -eq 1 ) -and ( $DefenderVersion.Minor -eq 1 ) -and ( $DefenderVersion.Build -gt 18300 ) ) -or - ( ( $DefenderVersion.Major -eq 1 ) -and ( $DefenderVersion.Minor -eq 1 ) -and ( $DefenderVersion.Build -eq 18300 ) -and ( $DefenderVersion.Revision -ge 4 ) ) ) { + if (($DefenderVersion.Major -gt 1) -or + (($DefenderVersion.Major -eq 1) -and ($DefenderVersion.Minor -gt 1)) -or + (($DefenderVersion.Major -eq 1) -and ($DefenderVersion.Minor -eq 1) -and ($DefenderVersion.Build -gt 18300)) -or + (($DefenderVersion.Major -eq 1) -and ($DefenderVersion.Minor -eq 1) -and ($DefenderVersion.Build -eq 18300) -and ($DefenderVersion.Revision -ge 4))) { Write-Host "Windows Defender version supported for AMSI: $DefenderVersion" -ForegroundColor Green } else { Write-Warning "Windows Defender version Non-supported for AMSI: $DefenderVersion" @@ -431,9 +402,9 @@ begin { ($_.SectionName -eq 'HttpRequestFiltering') -and ($_.Parameters -eq 'Enabled=False') -and ($_.Server -contains $ExchangeServer) } - if ( $getSO ) { + if ($getSO) { $getSO | Out-Host - if ( $getSO.Status -eq "Accepted" ) { + if ($getSO.Status -eq "Accepted") { Write-Warning "AMSI is Disabled by $($getSO.Identity) SettingOverride for $ExchangeServer" } else { Write-Host "We found SettingOverride for $ExchangeServer ($($getSO.Identity))" @@ -444,10 +415,11 @@ begin { $FEEcpWebConfig = $null $CAEEcpWebConfig = $null - $ExchangePath = Get-ExchangeInstallPath -ExchangeServer $ExchangeServer + $getMSIInstallPathSB = { (Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ExchangeServer\v15\Setup -ErrorAction SilentlyContinue).MsiInstallPath } + $ExchangePath = Invoke-ScriptBlockHandler -ComputerName $ExchangeServer -ScriptBlock $getMSIInstallPathSB - if ( $ExchangePath ) { - if ( $ExchangeServer.ToLower() -eq $env:COMPUTERNAME.ToLower() ) { + if ($ExchangePath) { + if ($ExchangeServer.ToLower() -eq $env:COMPUTERNAME.ToLower()) { $FEEcpWebConfig = Join-Path $ExchangePath "FrontEnd\HttpProxy\ecp\web.config" $CAEEcpWebConfig = Join-Path $ExchangePath "ClientAccess\ecp\web.config" } else { @@ -455,12 +427,12 @@ begin { $CAEEcpWebConfig = Join-Path "\\$ExchangeServer\$($ExchangePath.Replace(':','$'))" "ClientAccess\ecp\web.config" } - if ( $FEEcpWebConfig -and $CAEEcpWebConfig) { - if ( Test-Path $FEEcpWebConfig -PathType Leaf) { + if ($FEEcpWebConfig -and $CAEEcpWebConfig) { + if (Test-Path $FEEcpWebConfig -PathType Leaf) { $FEFilterModule = $null $FEFilterModule = Get-Content $FEEcpWebConfig | Select-String ' Date: Wed, 31 May 2023 10:46:01 +0200 Subject: [PATCH 05/19] Remove PSAvoidUsingWMICmdlet --- Admin/Test-AMSI.ps1 | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/Admin/Test-AMSI.ps1 b/Admin/Test-AMSI.ps1 index dd1545a62e..9cc44ac3e8 100644 --- a/Admin/Test-AMSI.ps1 +++ b/Admin/Test-AMSI.ps1 @@ -96,7 +96,6 @@ #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "TestAMSI", HelpUri = "https://microsoft.github.io/CSS-Exchange/Admin/Test-AMSI/")] -[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWMICmdlet', '', Justification = 'Used Get-WmiObject just in case Get-CimInstance does not get the Windows version as a fallback.')] param( [Parameter(ParameterSetName = 'TestAMSI', Mandatory = $false)] [Parameter(ParameterSetName = 'TestAMSIAll', Mandatory = $false)] @@ -398,10 +397,11 @@ begin { Invoke-ScriptBlockHandler -ComputerName $ExchangeServer -ScriptBlock $AMSIProvidersSB $getSO = $null - $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { - ($_.SectionName -eq 'HttpRequestFiltering') -and - ($_.Parameters -eq 'Enabled=False') -and - ($_.Server -contains $ExchangeServer) } + $getSO = Get-FilteredSettingOverrideInformation -ErrorAction SilentlyContinue | Where-Object { + ($_.Component.ToLower() -eq 'Cafe'.ToLower()) -and + ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and + ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and + ($_.Server.ToLower() -contains $ExchangeServer.ToLower()) } if ($getSO) { $getSO | Out-Host if ($getSO.Status -eq "Accepted") { @@ -638,9 +638,10 @@ process { Write-Host $bar Write-Host "" $getSO = $null - $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { - ($_.SectionName -eq 'HttpRequestFiltering') -and - ($_.Parameters -eq 'Enabled=False') -and + $getSO = Get-FilteredSettingOverrideInformation -ErrorAction SilentlyContinue | Where-Object { + ($_.Component.ToLower() -eq 'Cafe'.ToLower()) -and + ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and + ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and ($null -eq $_.Server) } if ($getSO) { $getSO | Out-Host @@ -664,10 +665,11 @@ process { foreach ($server in $filterList) { Write-Host $bar Write-Host "" - $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { - ($_.SectionName -eq 'HttpRequestFiltering') -and - ($_.Parameters -eq 'Enabled=False') -and - ($_.Server -contains $server) } + $getSO = Get-FilteredSettingOverrideInformation -ErrorAction SilentlyContinue | Where-Object { + ($_.Component.ToLower() -eq 'Cafe'.ToLower()) -and + ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and + ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and + ($_.Server.ToLower() -contains $server.ToLower()) } if ($null -eq $getSO) { Write-Host "We did not find Get-SettingOverride that disabled AMSI on $server" Write-Warning "AMSI is NOT disabled on $server" @@ -688,8 +690,9 @@ process { Write-Host $bar Write-Host "" $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { - ($_.SectionName -eq 'HttpRequestFiltering') -and - ($_.Parameters -eq 'Enabled=False') -and + ($_.Component.ToLower() -eq 'Cafe'.ToLower()) -and + ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and + ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and ($null -eq $_.Server) } if ($null -eq $getSO) { Write-Host "We did not find Get-SettingOverride that disabled AMSI at Organization level" @@ -717,9 +720,10 @@ process { Write-Host $bar Write-Host "" $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { - ($_.SectionName -eq 'HttpRequestFiltering') -and - ($_.Parameters -eq 'Enabled=False') -and - ($_.Server -contains $server) } + ($_.Component.ToLower() -eq 'Cafe'.ToLower()) -and + ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and + ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and + ($_.Server.ToLower() -contains $server.ToLower()) } if ($null -eq $getSO) { New-SettingOverride -Name "DisablingAMSIScan-$server" -Component Cafe -Section HttpRequestFiltering -Parameters ("Enabled=False") -Reason "Disabled via CSS-Exchange Script" -Server $server -WhatIf:$WhatIfPreference if (-not $WhatIfPreference) { @@ -737,8 +741,9 @@ process { Write-Host "" $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { ($null -eq $_.Server) -and - ($_.SectionName -eq 'HttpRequestFiltering') -and - ($_.Parameters -eq 'Enabled=False') } + ($_.Component.ToLower() -eq 'Cafe'.ToLower()) -and + ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and + ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) } if ($null -eq $getSO) { New-SettingOverride -Name DisablingAMSIScan-OrgLevel -Component Cafe -Section HttpRequestFiltering -Parameters ("Enabled=False") -Reason "Disabled via CSS-Exchange Script" -WhatIf:$WhatIfPreference if (-not $WhatIfPreference) { From 4f9551963d26809b6deabb8b427ae53a678fc210 Mon Sep 17 00:00:00 2001 From: iserrano76 Date: Wed, 31 May 2023 11:05:53 +0200 Subject: [PATCH 06/19] Added ComponentName on filter --- Admin/Test-AMSI.ps1 | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Admin/Test-AMSI.ps1 b/Admin/Test-AMSI.ps1 index 9cc44ac3e8..d65a82d0b8 100644 --- a/Admin/Test-AMSI.ps1 +++ b/Admin/Test-AMSI.ps1 @@ -397,8 +397,8 @@ begin { Invoke-ScriptBlockHandler -ComputerName $ExchangeServer -ScriptBlock $AMSIProvidersSB $getSO = $null - $getSO = Get-FilteredSettingOverrideInformation -ErrorAction SilentlyContinue | Where-Object { - ($_.Component.ToLower() -eq 'Cafe'.ToLower()) -and + $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { + ($_.ComponentName.ToLower() -eq 'Cafe'.ToLower()) -and ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and ($_.Server.ToLower() -contains $ExchangeServer.ToLower()) } @@ -638,8 +638,8 @@ process { Write-Host $bar Write-Host "" $getSO = $null - $getSO = Get-FilteredSettingOverrideInformation -ErrorAction SilentlyContinue | Where-Object { - ($_.Component.ToLower() -eq 'Cafe'.ToLower()) -and + $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { + ($_.ComponentName.ToLower() -eq 'Cafe'.ToLower()) -and ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and ($null -eq $_.Server) } @@ -665,8 +665,9 @@ process { foreach ($server in $filterList) { Write-Host $bar Write-Host "" - $getSO = Get-FilteredSettingOverrideInformation -ErrorAction SilentlyContinue | Where-Object { - ($_.Component.ToLower() -eq 'Cafe'.ToLower()) -and + $getSO = $null + $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { + ($_.ComponentName.ToLower() -eq 'Cafe'.ToLower()) -and ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and ($_.Server.ToLower() -contains $server.ToLower()) } @@ -689,8 +690,9 @@ process { } else { Write-Host $bar Write-Host "" + $getSO = $null $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { - ($_.Component.ToLower() -eq 'Cafe'.ToLower()) -and + ($_.ComponentName.ToLower() -eq 'Cafe'.ToLower()) -and ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and ($null -eq $_.Server) } @@ -719,8 +721,9 @@ process { foreach ($server in $filterList) { Write-Host $bar Write-Host "" + $getSO = $null $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { - ($_.Component.ToLower() -eq 'Cafe'.ToLower()) -and + ($_.ComponentName.ToLower() -eq 'Cafe'.ToLower()) -and ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and ($_.Server.ToLower() -contains $server.ToLower()) } @@ -739,9 +742,10 @@ process { } else { Write-Host $bar Write-Host "" + $getSO = $null $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { ($null -eq $_.Server) -and - ($_.Component.ToLower() -eq 'Cafe'.ToLower()) -and + ($_.ComponentName.ToLower() -eq 'Cafe'.ToLower()) -and ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) } if ($null -eq $getSO) { From 3365051a7886f20fab97fa4f2403b88b85ce72ec Mon Sep 17 00:00:00 2001 From: iserrano76 Date: Thu, 1 Jun 2023 18:39:17 +0200 Subject: [PATCH 07/19] Change Version veritication & fix management tools --- Admin/Test-AMSI.ps1 | 214 ++++++++++++++++++++++++++------------------ 1 file changed, 125 insertions(+), 89 deletions(-) diff --git a/Admin/Test-AMSI.ps1 b/Admin/Test-AMSI.ps1 index d65a82d0b8..5154bfd84a 100644 --- a/Admin/Test-AMSI.ps1 +++ b/Admin/Test-AMSI.ps1 @@ -161,6 +161,27 @@ begin { . $PSScriptRoot\..\Shared\ScriptUpdateFunctions\Test-ScriptVersion.ps1 . $PSScriptRoot\..\Shared\Get-ExchangeBuildVersionInformation.ps1 + function HasWindowsVersionAmsiSupport { + param( + [Parameter(Mandatory = $true)] + [string]$server + ) + + $Version + $Version = Invoke-ScriptBlockHandler -ComputerName $server -ScriptBlock { [System.Environment]::OSVersion.Version.Major } + if ($Version) { + if ($Version -ge 10) { + return $true + } else { + Write-Warning "$server is not a Windows version with AMSI support." + return $false + } + } else { + Write-Warning "We could not get Windows version for $server." + return $false + } + } + function CheckServerAMSI { param( [Parameter(Mandatory = $true)] @@ -396,22 +417,19 @@ begin { Write-Host "" Invoke-ScriptBlockHandler -ComputerName $ExchangeServer -ScriptBlock $AMSIProvidersSB - $getSO = $null - $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { + $getSOs = $null + $getSOs = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { ($_.ComponentName.ToLower() -eq 'Cafe'.ToLower()) -and ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and - ($_.Server.ToLower() -contains $ExchangeServer.ToLower()) } - if ($getSO) { - $getSO | Out-Host - if ($getSO.Status -eq "Accepted") { - Write-Warning "AMSI is Disabled by $($getSO.Identity) SettingOverride for $ExchangeServer" - } else { - Write-Host "We found SettingOverride for $ExchangeServer ($($getSO.Identity))" - Write-Warning "The Status of $($getSO.Identity) is not Accepted. Should not apply for $ExchangeServer." + ($_.Status -eq 'Accepted') -and + ($null -ne $_.Server -and ($_.Server.ToLower() -contains $ExchangeServer.ToLower())) } + if ($getSOs) { + $getSOs | Out-Host + foreach ($so in $getSOs) { + Write-Warning "AMSI is Disabled by $($so.Name) SettingOverride for $ExchangeServer" } } else { - $FEEcpWebConfig = $null $CAEEcpWebConfig = $null @@ -509,7 +527,9 @@ process { exit } - if ((Get-ExchangeServer $env:COMPUTERNAME).IsEdgeServer) { + $localServer = $null + $localServer = Get-ExchangeServer $env:COMPUTERNAME -ErrorAction SilentlyContinue + if ($localServer -and $localServer.IsEdgeServer) { Write-Host $msgNewLine Write-Warning "This script cannot be executed in an Edge Server." exit @@ -527,6 +547,11 @@ process { } if ($null -eq $ServerList -and ($RestartIIS -or $CheckAMSIConfig -or $TestAMSI) -and (-not $AllServers)) { + if (-not $localServer) { + Write-Host $msgNewLine + Write-Warning "This option is not available in a management tools server. You must select a server with any of the following parameters: ServerList, AllServers, Sites." + exit + } $ServerList = $env:COMPUTERNAME } @@ -534,23 +559,35 @@ process { Write-Host "AMSI only support on Exchange 2016 CU21 or newer, Exchange 2019 CU10 or newer and running on Windows 2016 or newer" -ForegroundColor Green Write-Host "" - $SupportedExchangeServers = Get-ExchangeServer | Where-Object { - ($_.IsClientAccessServer) -and - ((((Get-ExchangeBuildVersionInformation -AdminDisplayVersion ($_.adminDisplayVersion.ToString())).BuildVersion.Minor -eq 1) -and - ((Get-ExchangeBuildVersionInformation -AdminDisplayVersion ($_.adminDisplayVersion.ToString())).BuildVersion.Build -ge 2308)) -or - (((Get-ExchangeBuildVersionInformation -AdminDisplayVersion ($_.adminDisplayVersion.ToString())).BuildVersion.Minor -eq 2) -and - ((Get-ExchangeBuildVersionInformation -AdminDisplayVersion ($_.adminDisplayVersion.ToString())).BuildVersion.Build -ge 922))) } | - Select-Object Name, Site + $SupportedExchangeServers = New-Object 'System.Collections.Generic.List[object]' + Get-ExchangeServer | Where-Object { $_.IsClientAccessServer } | ForEach-Object { + $server = $_ + $versionInformation = Get-ExchangeBuildVersionInformation -AdminDisplayVersion $_.AdminDisplayVersion.ToString() + switch ($versionInformation.MajorVersion) { + "Exchange2016" { if ($versionInformation.BuildVersion -ge "15.1.2308.8") { $SupportedExchangeServers.Add($server); break } } + "Exchange2019" { if ($versionInformation.BuildVersion -ge "15.2.922.7") { $SupportedExchangeServers.Add($server); break } } + } + } - if ($Sites) { - $uniqueSites = $null + $uniqueSites = $null + if ($localServer) { $uniqueSites = $SupportedExchangeServers.Site.Name | Get-Unique + } else { + $uniqueSites = $SupportedExchangeServers.Site | Get-Unique | ForEach-Object { $_.split('/')[-1] } + } + $sitesCounter = $uniqueSites.count + + if ($Sites) { foreach ($site in $Sites) { if ($uniqueSites -notcontains $site) { Write-Warning "We did not find site $site" } } - $fullList = ($SupportedExchangeServers | Where-Object { $Sites -contains $_.Site.Name } | Select-Object Name).Name + if ($localServer) { + $fullList = ($SupportedExchangeServers | Where-Object { $Sites -contains $_.Site.Name } | Select-Object Name).Name + } else { + $fullList = ($SupportedExchangeServers | Where-Object { $Sites -contains $_.Site.split('/')[-1] } | Select-Object Name).Name + } } else { $fullList = ($SupportedExchangeServers | Select-Object Name).Name } @@ -560,16 +597,8 @@ process { foreach ($server in $fullList) { $serverName = $server.Split('.')[0] if ($SupportedExchangeServers.Name -contains $serverName) { - $Version = Invoke-ScriptBlockHandler -ComputerName $server -ScriptBlock { [System.Environment]::OSVersion.Version.Major } - if ($Version) { - if ($Version -ge 10) { - $filterList += $serverName - } else { - Write-Warning "$server is not a Windows version with AMSI support." - } - } else { - Write-Warning "We could not get Windows version for $server." - Write-Warning "Try to run the script locally." + if (HasWindowsVersionAmsiSupport -server $server) { + $filterList += $serverName } } else { Write-Warning "$server is not an Exchange version with AMSI support." @@ -580,16 +609,8 @@ process { $serverName = $server.Split('.')[0] if ($fullList -contains $serverName) { if ($SupportedExchangeServers.Name -contains $serverName) { - $Version = Invoke-ScriptBlockHandler -ComputerName $server -ScriptBlock { [System.Environment]::OSVersion.Version.Major } - if ($Version) { - if ($Version -ge 10) { - $filterList += $serverName - } else { - Write-Warning "$server is not a Windows version with AMSI support." - } - } else { - Write-Warning "We could not get Windows version for $server." - Write-Warning "Try to run the script locally." + if (HasWindowsVersionAmsiSupport -server $server) { + $filterList += $serverName } } else { Write-Warning "$server is not an Exchange version with AMSI support." @@ -637,19 +658,17 @@ process { } Write-Host $bar Write-Host "" - $getSO = $null - $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { + $getSOs = $null + $getSOs = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { ($_.ComponentName.ToLower() -eq 'Cafe'.ToLower()) -and ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and + ($_.Status -eq 'Accepted') -and ($null -eq $_.Server) } - if ($getSO) { - $getSO | Out-Host - if ($getSO.Status -eq "Accepted") { - Write-Warning "AMSI is Disabled by $($getSO.Identity) SettingOverride at organization Level." - } else { - Write-Host "We found SettingOverride for $ExchangeServer ($($getSO.Identity))" - Write-Warning "The Status of $($getSO.Identity) is not Accepted. Should not apply at organization Level." + if ($getSOs) { + $getSOs | Out-Host + foreach ($so in $getSOs) { + Write-Warning "AMSI is Disabled by $($so.Name) SettingOverride at organization Level." } } else { Write-Host "AMSI is Enabled for Exchange at Organization Level." -ForegroundColor Green @@ -660,26 +679,27 @@ process { $needsRefresh = 0 if ($EnableAMSI) { - $getSO = $null if ($filterList) { foreach ($server in $filterList) { Write-Host $bar Write-Host "" - $getSO = $null - $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { + $getSOs = $null + $getSOs = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { ($_.ComponentName.ToLower() -eq 'Cafe'.ToLower()) -and ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and - ($_.Server.ToLower() -contains $server.ToLower()) } - if ($null -eq $getSO) { + ($_.Status -eq 'Accepted') -and + ($null -ne $_.Server -and ($_.Server.ToLower() -contains $server.ToLower())) } + if ($null -eq $getSOs) { Write-Host "We did not find Get-SettingOverride that disabled AMSI on $server" Write-Warning "AMSI is NOT disabled on $server" } else { - if (-not $WhatIfPreference) { Write-Host "Removing SettingOverride $($getSO.Identity)" } - Remove-SettingOverride -Identity $getSO.Identity -Confirm:$false -WhatIf:$WhatIfPreference - $getSO | Out-Host + foreach ($so in $getSOs) { + $so | Out-Host + if (-not $WhatIfPreference) { Write-Host "Removing SettingOverride $($so.Name)" } + Remove-SettingOverride -Identity $so.Identity -Confirm:$false -WhatIf:$WhatIfPreference + } if (-not $WhatIfPreference) { - Write-Host "Removing SettingOverride $($getSO.Identity)" Write-Host "Enabled on $server" -ForegroundColor Green Get-ExchangeDiagnosticInfo -Process Microsoft.Exchange.Directory.TopologyService -Component VariantConfiguration -Argument Refresh -Server $server | Out-Null Write-Host "Refreshed Get-ExchangeDiagnosticInfo on $server" @@ -690,44 +710,50 @@ process { } else { Write-Host $bar Write-Host "" - $getSO = $null - $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { + $getSOs = $null + $getSOs = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { ($_.ComponentName.ToLower() -eq 'Cafe'.ToLower()) -and ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and + ($_.Status -eq 'Accepted') -and ($null -eq $_.Server) } - if ($null -eq $getSO) { + if ($null -eq $getSOs) { Write-Host "We did not find Get-SettingOverride that disabled AMSI at Organization level" Write-Warning "AMSI is NOT disabled on Exchange configuration at organization level" } else { - if (-not $WhatIfPreference) { Write-Host "Removing SettingOverride $($getSO.Identity)" } - $getSO | Out-Host - Remove-SettingOverride -Identity $getSO.Identity -Confirm:$false -WhatIf:$WhatIfPreference - if (-not $WhatIfPreference) { - Write-Host "Enabled AMSI at Organization Level" -ForegroundColor Green - foreach ($server in $filterList) { - Get-ExchangeDiagnosticInfo -Process Microsoft.Exchange.Directory.TopologyService -Component VariantConfiguration -Argument Refresh -Server $server | Out-Null - Write-Host "Refreshed Get-ExchangeDiagnosticInfo on $server" + foreach ($so in $getSOs) { + $so | Out-Host + if (-not $WhatIfPreference) { Write-Host "Removing SettingOverride $($so.Name)" } + Remove-SettingOverride -Identity $so.Name -Confirm:$false -WhatIf:$WhatIfPreference + if (-not $WhatIfPreference) { + Write-Host "Enabled AMSI at Organization Level" -ForegroundColor Green + foreach ($server in $SupportedExchangeServers) { + Get-ExchangeDiagnosticInfo -Process Microsoft.Exchange.Directory.TopologyService -Component VariantConfiguration -Argument Refresh -Server $server | Out-Null + Write-Host "Refreshed Get-ExchangeDiagnosticInfo on $server" + $needsRefresh++ + } } - $needsRefresh++ } } } } if ($DisableAMSI) { - $getSO = $null if ($filterList) { foreach ($server in $filterList) { Write-Host $bar Write-Host "" - $getSO = $null - $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { + $getSOs = $null + $getSOs = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { ($_.ComponentName.ToLower() -eq 'Cafe'.ToLower()) -and ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and - ($_.Server.ToLower() -contains $server.ToLower()) } - if ($null -eq $getSO) { + ($_.Status -eq 'Accepted') -and + ($null -ne $_.Server -and ($_.Server.ToLower() -contains $server.ToLower())) } + if ($null -eq $getSOs) { + if (-not $WhatIfPreference) { + Write-Warning "Disabling on $server by DisablingAMSIScan-$server SettingOverride" + } New-SettingOverride -Name "DisablingAMSIScan-$server" -Component Cafe -Section HttpRequestFiltering -Parameters ("Enabled=False") -Reason "Disabled via CSS-Exchange Script" -Server $server -WhatIf:$WhatIfPreference if (-not $WhatIfPreference) { Write-Warning "Disabled on $server by DisablingAMSIScan-$server SettingOverride" @@ -736,42 +762,52 @@ process { $needsRefresh++ } } else { - Write-Warning "AMSI is already disabled on Exchange configuration for $server by SettingOverride $($getSO.Identity)" + foreach ($so in $getSOs) { + Write-Warning "AMSI is already disabled on Exchange configuration for $server by SettingOverride $($so.Name)" + } } } } else { Write-Host $bar Write-Host "" - $getSO = $null - $getSO = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { + $getSOs = $null + $getSOs = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { ($null -eq $_.Server) -and ($_.ComponentName.ToLower() -eq 'Cafe'.ToLower()) -and ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and + ($_.Status -eq 'Accepted') -and ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) } - if ($null -eq $getSO) { + if ($null -eq $getSOs) { + if (-not $WhatIfPreference) { + Write-Warning "Disabling AMSI at Organization Level by DisablingAMSIScan-OrgLevel SettingOverride" + } New-SettingOverride -Name DisablingAMSIScan-OrgLevel -Component Cafe -Section HttpRequestFiltering -Parameters ("Enabled=False") -Reason "Disabled via CSS-Exchange Script" -WhatIf:$WhatIfPreference if (-not $WhatIfPreference) { Write-Warning "Disabled AMSI at Organization Level by DisablingAMSIScan-OrgLevel SettingOverride" - foreach ($server in $filterList) { + foreach ($server in $SupportedExchangeServers) { Get-ExchangeDiagnosticInfo -Process Microsoft.Exchange.Directory.TopologyService -Component VariantConfiguration -Argument Refresh -Server $server | Out-Null Write-Host "Refreshed Get-ExchangeDiagnosticInfo on $server" + $needsRefresh++ } - $needsRefresh++ } } else { - Write-Warning "AMSI is already disabled on Exchange configuration by SettingOverride $($getSO.Identity)" + foreach ($so in $getSOs) { + Write-Warning "AMSI is already disabled on Exchange configuration by SettingOverride $($so.Name)" + } } } } - if ($needsRefresh -gt 0 -and ($SupportedExchangeServers.Site.Name | Get-Unique).count -gt 1) { + if ($needsRefresh -gt 0) { Write-Host "" Write-Host $bar Write-Host "" - Write-Warning "You have a multi site environment, confirm that all affected Exchange sites has replicated changes." - Write-Host "You can push changes on your DCs with: repadmin /syncall /AdeP" - Write-Host "" - Write-Warning "Remember to restart IIS to be effective." + if ($sitesCounter -gt 1) { + Write-Warning "You have a multi site environment, confirm that all affected Exchange sites has replicated changes." + Write-Host "You can push changes on your DCs with: repadmin /syncall /AdeP" + Write-Host "" + } + Write-Warning "Remember to restart IIS to be effective on all affected servers." Write-Host "You can accomplish this by running .\Test-AMSI.ps1 -RestartIIS" Write-Host "" } From bdb3e2d8aa34ec08814cc1abad836a5ca2890102 Mon Sep 17 00:00:00 2001 From: iserrano76 Date: Tue, 13 Jun 2023 16:02:17 +0200 Subject: [PATCH 08/19] Change Ex verification and added Accept set check. --- Admin/Test-AMSI.ps1 | 216 ++++++++++++++++++++++++++------------------ 1 file changed, 130 insertions(+), 86 deletions(-) diff --git a/Admin/Test-AMSI.ps1 b/Admin/Test-AMSI.ps1 index 5154bfd84a..01de967a32 100644 --- a/Admin/Test-AMSI.ps1 +++ b/Admin/Test-AMSI.ps1 @@ -203,13 +203,14 @@ begin { } catch [System.Net.WebException] { $Message = ($_.Exception.Message).ToString().Trim() $currentForegroundColor = $host.ui.RawUI.ForegroundColor - if ($_.Exception.Message -eq "The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.") { + if ($_.Exception.Status -eq [System.Net.WebExceptionStatus]::TrustFailure) { $host.ui.RawUI.ForegroundColor = "Red" Write-Host $Message $host.ui.RawUI.ForegroundColor = "Yellow" Write-Host "You could use the -IgnoreSSL parameter" $host.ui.RawUI.ForegroundColor = $currentForegroundColor - } elseif ($_.Exception.Message -eq "The remote server returned an error: (400) Bad Request.") { + } elseif ($_.Exception.Status -eq [System.Net.WebExceptionStatus]::ProtocolError -and + $_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::BadRequest ) { $host.ui.RawUI.ForegroundColor = "Green" Write-Host "We sent an test request to the ECP Virtual Directory of the server requested" $host.ui.RawUI.ForegroundColor = "Yellow" @@ -422,73 +423,76 @@ begin { ($_.ComponentName.ToLower() -eq 'Cafe'.ToLower()) -and ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and - ($_.Status -eq 'Accepted') -and ($null -ne $_.Server -and ($_.Server.ToLower() -contains $ExchangeServer.ToLower())) } if ($getSOs) { $getSOs | Out-Host foreach ($so in $getSOs) { - Write-Warning "AMSI is Disabled by $($so.Name) SettingOverride for $ExchangeServer" + if ($so.Status -eq 'Accepted') { + Write-Warning "AMSI is Disabled by $($so.Name) SettingOverride for $ExchangeServer" + } else { + Write-Host "AMSI is Disabled by $($so.Name) SettingOverride for $ExchangeServer but it is not Accepted." -ForegroundColor Red + } } - } else { - $FEEcpWebConfig = $null - $CAEEcpWebConfig = $null + } - $getMSIInstallPathSB = { (Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ExchangeServer\v15\Setup -ErrorAction SilentlyContinue).MsiInstallPath } - $ExchangePath = Invoke-ScriptBlockHandler -ComputerName $ExchangeServer -ScriptBlock $getMSIInstallPathSB + $FEEcpWebConfig = $null + $CAEEcpWebConfig = $null - if ($ExchangePath) { - if ($ExchangeServer.ToLower() -eq $env:COMPUTERNAME.ToLower()) { - $FEEcpWebConfig = Join-Path $ExchangePath "FrontEnd\HttpProxy\ecp\web.config" - $CAEEcpWebConfig = Join-Path $ExchangePath "ClientAccess\ecp\web.config" - } else { - $FEEcpWebConfig = Join-Path "\\$ExchangeServer\$($ExchangePath.Replace(':','$'))" "FrontEnd\HttpProxy\ecp\web.config" - $CAEEcpWebConfig = Join-Path "\\$ExchangeServer\$($ExchangePath.Replace(':','$'))" "ClientAccess\ecp\web.config" - } + $getMSIInstallPathSB = { (Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ExchangeServer\v15\Setup -ErrorAction SilentlyContinue).MsiInstallPath } + $ExchangePath = Invoke-ScriptBlockHandler -ComputerName $ExchangeServer -ScriptBlock $getMSIInstallPathSB - if ($FEEcpWebConfig -and $CAEEcpWebConfig) { - if (Test-Path $FEEcpWebConfig -PathType Leaf) { - $FEFilterModule = $null - $FEFilterModule = Get-Content $FEEcpWebConfig | Select-String ' Date: Mon, 19 Jun 2023 15:37:21 +0200 Subject: [PATCH 09/19] Changed string comparison. --- Admin/Test-AMSI.ps1 | 54 ++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/Admin/Test-AMSI.ps1 b/Admin/Test-AMSI.ps1 index 01de967a32..75796be9e9 100644 --- a/Admin/Test-AMSI.ps1 +++ b/Admin/Test-AMSI.ps1 @@ -236,7 +236,7 @@ begin { $HttpRequestFilteringLogFolder = $null if ($ExchangePath) { - if ($server.ToLower() -eq $env:COMPUTERNAME.ToLower()) { + if ($server.Equals($env:COMPUTERNAME, [System.StringComparison]::OrdinalIgnoreCase)) { $HttpRequestFilteringLogFolder = Join-Path $ExchangePath "Logging\HttpRequestFiltering\" } else { $HttpRequestFilteringLogFolder = Join-Path "\\$server\$($ExchangePath.Replace(':','$'))" "Logging\HttpRequestFiltering\" @@ -266,9 +266,9 @@ begin { if ($DateTime) { if (($testTime.ToUniversalTime().Subtract($DateTime) -lt $marginTime) -and ($testTime.ToUniversalTime().Subtract($DateTime) -gt - $marginTime)) { - if (($line.UrlHost.ToLower() -eq $server.ToLower()) -and - ($line.UrlStem.ToLower() -eq '/ecp/x.js'.ToLower()) -and - ($line.ScanResult.ToLower() -eq 'Detected'.ToLower())) { + if (($line.UrlHost.Equals($server, [System.StringComparison]::OrdinalIgnoreCase)) -and + ($line.UrlStem.Equals('/ecp/x.js', [System.StringComparison]::OrdinalIgnoreCase)) -and + ($line.ScanResult.Equals('Detected', [System.StringComparison]::OrdinalIgnoreCase))) { Write-Host "" Write-Host "We found a detection in HttpRequestFiltering logs: " -ForegroundColor Green Write-Host "$line" @@ -420,10 +420,10 @@ begin { $getSOs = $null $getSOs = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { - ($_.ComponentName.ToLower() -eq 'Cafe'.ToLower()) -and - ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and - ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and - ($null -ne $_.Server -and ($_.Server.ToLower() -contains $ExchangeServer.ToLower())) } + ($_.ComponentName.Equals('Cafe', [System.StringComparison]::OrdinalIgnoreCase)) -and + ($_.SectionName.Equals('HttpRequestFiltering', [System.StringComparison]::OrdinalIgnoreCase)) -and + ($_.Parameters -contains 'Enabled=False') -and + ($null -ne $_.Server -and ($_.Server -contains $ExchangeServer)) } if ($getSOs) { $getSOs | Out-Host foreach ($so in $getSOs) { @@ -442,7 +442,7 @@ begin { $ExchangePath = Invoke-ScriptBlockHandler -ComputerName $ExchangeServer -ScriptBlock $getMSIInstallPathSB if ($ExchangePath) { - if ($ExchangeServer.ToLower() -eq $env:COMPUTERNAME.ToLower()) { + if ($ExchangeServer.Equals($env:COMPUTERNAME, [System.StringComparison]::OrdinalIgnoreCase)) { $FEEcpWebConfig = Join-Path $ExchangePath "FrontEnd\HttpProxy\ecp\web.config" $CAEEcpWebConfig = Join-Path $ExchangePath "ClientAccess\ecp\web.config" } else { @@ -664,9 +664,9 @@ process { Write-Host "" $getSOs = $null $getSOs = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { - ($_.ComponentName.ToLower() -eq 'Cafe'.ToLower()) -and - ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and - ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and + ($_.ComponentName.Equals('Cafe', [System.StringComparison]::OrdinalIgnoreCase)) -and + ($_.SectionName.Equals('HttpRequestFiltering', [System.StringComparison]::OrdinalIgnoreCase)) -and + ($_.Parameters -contains 'Enabled=False') -and ($null -eq $_.Server) } if ($getSOs) { $getSOs | Out-Host @@ -692,10 +692,10 @@ process { Write-Host "" $getSOs = $null $getSOs = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { - ($_.ComponentName.ToLower() -eq 'Cafe'.ToLower()) -and - ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and - ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and - ($null -ne $_.Server -and ($_.Server.ToLower() -contains $server.ToLower())) } + ($_.ComponentName.Equals('Cafe', [System.StringComparison]::OrdinalIgnoreCase)) -and + ($_.SectionName.Equals('HttpRequestFiltering', [System.StringComparison]::OrdinalIgnoreCase)) -and + ($_.Parameters -contains 'Enabled=False') -and + ($null -ne $_.Server -and ($_.Server -contains $server)) } if ($null -eq $getSOs) { Write-Host "We did not find Get-SettingOverride that disabled AMSI on $server" Write-Warning "AMSI is NOT disabled on $server" @@ -728,9 +728,9 @@ process { Write-Host "" $getSOs = $null $getSOs = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { - ($_.ComponentName.ToLower() -eq 'Cafe'.ToLower()) -and - ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and - ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and + ($_.ComponentName.Equals('Cafe', [System.StringComparison]::OrdinalIgnoreCase)) -and + ($_.SectionName.Equals('HttpRequestFiltering', [System.StringComparison]::OrdinalIgnoreCase)) -and + ($_.Parameters -contains 'Enabled=False') -and ($null -eq $_.Server) } if ($null -eq $getSOs) { Write-Host "We did not find Get-SettingOverride that disabled AMSI at Organization level" @@ -770,10 +770,10 @@ process { Write-Host "" $getSOs = $null $getSOs = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { - ($_.ComponentName.ToLower() -eq 'Cafe'.ToLower()) -and - ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and - ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) -and - ($null -ne $_.Server -and ($_.Server.ToLower() -contains $server.ToLower())) } + ($_.ComponentName.Equals('Cafe', [System.StringComparison]::OrdinalIgnoreCase)) -and + ($_.SectionName.Equals('HttpRequestFiltering', [System.StringComparison]::OrdinalIgnoreCase)) -and + ($_.Parameters -contains 'Enabled=False') -and + ($null -ne $_.Server -and ($_.Server -contains $server)) } if ($null -eq $getSOs) { if (-not $WhatIfPreference) { Write-Warning "Disabling on $server by DisablingAMSIScan-$server SettingOverride" @@ -806,9 +806,9 @@ process { $getSOs = $null $getSOs = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { ($null -eq $_.Server) -and - ($_.ComponentName.ToLower() -eq 'Cafe'.ToLower()) -and - ($_.SectionName.ToLower() -eq 'HttpRequestFiltering'.ToLower()) -and - ($_.Parameters.ToLower() -eq 'Enabled=False'.ToLower()) } + ($_.ComponentName.Equals('Cafe', [System.StringComparison]::OrdinalIgnoreCase)) -and + ($_.SectionName.Equals('HttpRequestFiltering', [System.StringComparison]::OrdinalIgnoreCase)) -and + ($_.Parameters -contains 'Enabled=False') } if ($null -eq $getSOs) { if (-not $WhatIfPreference) { Write-Warning "Disabling AMSI at Organization Level by DisablingAMSIScan-OrgLevel SettingOverride" @@ -869,7 +869,7 @@ process { foreach ($server in $filterList) { Write-Host $msgNewLine if ($Force -or $filterList.Count -eq 1 -or $PSCmdlet.ShouldContinue("Are you sure you want to do it?", "You will restart IIS on server $server", $true, [ref]$yesToAll, [ref]$noToAll)) { - if ($server.ToLower() -eq $env:COMPUTERNAME.ToLower()) { + if ($server.Equals($env:COMPUTERNAME, [System.StringComparison]::OrdinalIgnoreCase)) { if (-not $WhatIfPreference) { Write-Host "Restarting local IIS on $server" } Get-Service W3SVC, WAS | Restart-Service -Force -WhatIf:$WhatIfPreference } else { From 1295835c508dbdd869352862f70ea04d1048ddcf Mon Sep 17 00:00:00 2001 From: iserrano76 Date: Fri, 23 Jun 2023 15:28:17 +0200 Subject: [PATCH 10/19] Added improvements suggested by @lusassl-msft --- Admin/Test-AMSI.ps1 | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/Admin/Test-AMSI.ps1 b/Admin/Test-AMSI.ps1 index 75796be9e9..dc56ac0076 100644 --- a/Admin/Test-AMSI.ps1 +++ b/Admin/Test-AMSI.ps1 @@ -187,7 +187,7 @@ begin { [Parameter(Mandatory = $true)] [string]$ExchangeServer, [Parameter(Mandatory = $false)] - [switch]$isServer + [switch]$IsServer ) try { @@ -219,7 +219,7 @@ begin { $host.ui.RawUI.ForegroundColor = "Yellow" Write-Host "This may be indicative of a potential block from AMSI" $host.ui.RawUI.ForegroundColor = "Green" - if ($isServer) { + if ($IsServer) { $getMSIInstallPathSB = { (Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ExchangeServer\v15\Setup -ErrorAction SilentlyContinue).MsiInstallPath } $ExchangePath = Invoke-ScriptBlockHandler -ComputerName $ExchangeServer -ScriptBlock $getMSIInstallPathSB $msgCheckLogs = "You can check your log files located in $($ExchangePath)Logging\HttpRequestFiltering\" @@ -230,7 +230,7 @@ begin { $host.ui.RawUI.ForegroundColor = $currentForegroundColor $msgDetectedTimeStamp = "You should find result around $((Get-Date).ToUniversalTime().ToString("M/d/yyy h:mm:ss tt")) UTC" Write-Host $msgDetectedTimeStamp - if ($isServer) { + if ($IsServer) { Write-Host "" Write-Host "Checking logs on $server at $($testTime.ToString("M/d/yyy h:mm:ss tt"))" $HttpRequestFilteringLogFolder = $null @@ -291,7 +291,8 @@ begin { } else { Write-Host "Check your log files located in %ExchangeInstallPath%\Logging\HttpRequestFiltering\ in all server that provide $server endpoint" } - } elseif ($_.Exception.Message -eq "The remote server returned an error: (500) Internal Server Error.") { + } elseif ($_.Exception.Status -eq [System.Net.WebExceptionStatus]::ProtocolError -and + $_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::InternalServerError) { $host.ui.RawUI.ForegroundColor = "Red" Write-Host $msgNewLine Write-Host $Message @@ -300,7 +301,7 @@ begin { Write-Host "If you are using Microsoft Defender, RealTime protection could be disabled or then AMSI may be disabled." Write-Host "If you are using a 3rd Party AntiVirus Product that may not be AMSI capable (Please Check with your AntiVirus Provider for Exchange AMSI Support)" $host.ui.RawUI.ForegroundColor = $currentForegroundColor - } elseif ($_.Exception.Message.StartsWith("The remote name could not be resolved:")) { + } elseif ($_.Exception.Status -eq [System.Net.WebExceptionStatus]::NameResolutionFailure) { $host.ui.RawUI.ForegroundColor = "Red" Write-Host $msgNewLine Write-Host $Message @@ -380,10 +381,7 @@ begin { $DefenderVersion = $null $DefenderVersion = [System.Version]::new((& $process -SignatureUpdate | Where-Object { $_.StartsWith('Engine Version:') }).Split(' ')[2]) if ($DefenderVersion) { - if (($DefenderVersion.Major -gt 1) -or - (($DefenderVersion.Major -eq 1) -and ($DefenderVersion.Minor -gt 1)) -or - (($DefenderVersion.Major -eq 1) -and ($DefenderVersion.Minor -eq 1) -and ($DefenderVersion.Build -gt 18300)) -or - (($DefenderVersion.Major -eq 1) -and ($DefenderVersion.Minor -eq 1) -and ($DefenderVersion.Build -eq 18300) -and ($DefenderVersion.Revision -ge 4))) { + if ($DefenderVersion -ge "1.1.18300.4") { Write-Host "Windows Defender version supported for AMSI: $DefenderVersion" -ForegroundColor Green } else { Write-Warning "Windows Defender version Non-supported for AMSI: $DefenderVersion" @@ -527,7 +525,7 @@ process { $exchangeShell = Confirm-ExchangeShell if (-not($exchangeShell.ShellLoaded)) { Write-Host $msgNewLine - Write-Warning "Failed to load Exchange Shell Module..." + Write-Warning "Failed to load Exchange Management Shell..." exit } @@ -641,7 +639,7 @@ process { Write-Host "Testing $($server):" -ForegroundColor Magenta Write-Host "" if ($fullList -contains $server) { - CheckServerAMSI -ExchangeServer $server -isServer + CheckServerAMSI -ExchangeServer $server -IsServer } else { CheckServerAMSI -ExchangeServer $server } @@ -694,7 +692,7 @@ process { $getSOs = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { ($_.ComponentName.Equals('Cafe', [System.StringComparison]::OrdinalIgnoreCase)) -and ($_.SectionName.Equals('HttpRequestFiltering', [System.StringComparison]::OrdinalIgnoreCase)) -and - ($_.Parameters -contains 'Enabled=False') -and + ($_.Parameters -contains 'Enabled=False') -and ($null -ne $_.Server -and ($_.Server -contains $server)) } if ($null -eq $getSOs) { Write-Host "We did not find Get-SettingOverride that disabled AMSI on $server" @@ -730,7 +728,7 @@ process { $getSOs = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { ($_.ComponentName.Equals('Cafe', [System.StringComparison]::OrdinalIgnoreCase)) -and ($_.SectionName.Equals('HttpRequestFiltering', [System.StringComparison]::OrdinalIgnoreCase)) -and - ($_.Parameters -contains 'Enabled=False') -and + ($_.Parameters -contains 'Enabled=False') -and ($null -eq $_.Server) } if ($null -eq $getSOs) { Write-Host "We did not find Get-SettingOverride that disabled AMSI at Organization level" @@ -772,7 +770,7 @@ process { $getSOs = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { ($_.ComponentName.Equals('Cafe', [System.StringComparison]::OrdinalIgnoreCase)) -and ($_.SectionName.Equals('HttpRequestFiltering', [System.StringComparison]::OrdinalIgnoreCase)) -and - ($_.Parameters -contains 'Enabled=False') -and + ($_.Parameters -contains 'Enabled=False') -and ($null -ne $_.Server -and ($_.Server -contains $server)) } if ($null -eq $getSOs) { if (-not $WhatIfPreference) { From 699f47802a2e1a03fbc8755880d9249ed46a71e5 Mon Sep 17 00:00:00 2001 From: iserrano76 Date: Mon, 26 Jun 2023 13:15:26 +0200 Subject: [PATCH 11/19] Changes requested --- Admin/Test-AMSI.ps1 | 253 ++++++++++++++++++++------------------------ 1 file changed, 112 insertions(+), 141 deletions(-) diff --git a/Admin/Test-AMSI.ps1 b/Admin/Test-AMSI.ps1 index dc56ac0076..091b1b910f 100644 --- a/Admin/Test-AMSI.ps1 +++ b/Admin/Test-AMSI.ps1 @@ -185,21 +185,20 @@ begin { function CheckServerAMSI { param( [Parameter(Mandatory = $true)] - [string]$ExchangeServer, + [string]$Server, [Parameter(Mandatory = $false)] - [switch]$IsServer + [switch]$IsExchangeServer ) try { $CookieContainer = New-Object Microsoft.PowerShell.Commands.WebRequestSession - $Cookie = New-Object System.Net.Cookie("X-BEResource", "a]@$($ExchangeServer):444/ecp/proxyLogon.ecp#~1941997017", "/", "$ExchangeServer") + $Cookie = New-Object System.Net.Cookie("X-BEResource", "a]@$($Server):444/ecp/proxyLogon.ecp#~1941997017", "/", "$Server") $CookieContainer.Cookies.Add($Cookie) - $testTime = Get-Date - Write-Host "Starting test at $($testTime -f "yyyy-MM-dd HH:mm:ss")" if ($IgnoreSSL -and ![System.Net.ServicePointManager]::ServerCertificateValidationCallback) { Enable-TrustAnyCertificateCallback } - Invoke-WebRequest https://$ExchangeServer/ecp/x.js -Method POST -Headers @{ "Host" = "$ExchangeServer" } -WebSession $CookieContainer + $StringDate = (Get-Date -Format yyyyMMddhhmmss) + Invoke-WebRequest https://$Server/ecp/CSS-Test-$StringDate.js -Method POST -Headers @{ "Host" = "$Server" } -WebSession $CookieContainer } catch [System.Net.WebException] { $Message = ($_.Exception.Message).ToString().Trim() $currentForegroundColor = $host.ui.RawUI.ForegroundColor @@ -215,96 +214,67 @@ begin { Write-Host "We sent an test request to the ECP Virtual Directory of the server requested" $host.ui.RawUI.ForegroundColor = "Yellow" Write-Host "The remote server returned an error: (400) Bad Request" - $host.ui.RawUI.ForegroundColor = "Green" - $host.ui.RawUI.ForegroundColor = "Yellow" Write-Host "This may be indicative of a potential block from AMSI" $host.ui.RawUI.ForegroundColor = "Green" - if ($IsServer) { + if ($IsExchangeServer) { $getMSIInstallPathSB = { (Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ExchangeServer\v15\Setup -ErrorAction SilentlyContinue).MsiInstallPath } - $ExchangePath = Invoke-ScriptBlockHandler -ComputerName $ExchangeServer -ScriptBlock $getMSIInstallPathSB - $msgCheckLogs = "You can check your log files located in $($ExchangePath)Logging\HttpRequestFiltering\" + $ExchangePath = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $getMSIInstallPathSB + Write-Host "You can check your log files located in $($ExchangePath)Logging\HttpRequestFiltering\ in $Server" } else { - $msgCheckLogs = "You can check your log files located in %ExchangeInstallPath%\Logging\HttpRequestFiltering\ in all server included in $ExchangeServer endpoint" + Write-Host "You can check your log files located in %ExchangeInstallPath%\Logging\HttpRequestFiltering\ in all server included in $Server endpoint" } - Write-Host $msgCheckLogs $host.ui.RawUI.ForegroundColor = $currentForegroundColor - $msgDetectedTimeStamp = "You should find result around $((Get-Date).ToUniversalTime().ToString("M/d/yyy h:mm:ss tt")) UTC" - Write-Host $msgDetectedTimeStamp - if ($IsServer) { + Write-Host "You should find a request for CSS-Test-$StringDate.js in the HttpRequestFiltering logs" + if ($IsExchangeServer) { Write-Host "" - Write-Host "Checking logs on $server at $($testTime.ToString("M/d/yyy h:mm:ss tt"))" + Write-Host "Looking for a request CSS-Test-$StringDate.js in the HttpRequestFiltering logs" $HttpRequestFilteringLogFolder = $null if ($ExchangePath) { - if ($server.Equals($env:COMPUTERNAME, [System.StringComparison]::OrdinalIgnoreCase)) { + if ($Server.Equals($env:COMPUTERNAME, [System.StringComparison]::OrdinalIgnoreCase)) { $HttpRequestFilteringLogFolder = Join-Path $ExchangePath "Logging\HttpRequestFiltering\" } else { $HttpRequestFilteringLogFolder = Join-Path "\\$server\$($ExchangePath.Replace(':','$'))" "Logging\HttpRequestFiltering\" } - } else { - Write-Host "Cannot get Exchange installation path on $server" -ForegroundColor Red - } - - if (Test-Path $HttpRequestFilteringLogFolder -PathType Container) { - $file = $null - $timeout = (Get-Date).AddMinutes(1) - $detected = $false - $marginTime = New-TimeSpan -Seconds 5 - do { - Start-Sleep -Seconds 2 + if (Test-Path $HttpRequestFilteringLogFolder -PathType Container) { $file = $null - $file = Get-ChildItem $HttpRequestFilteringLogFolder -Filter "HttpRequestFiltering_*.log" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 -Property * - if ($file) { - $csv = Import-Csv $file.FullName - foreach ($line in $csv) { - $DateTime = $null - try { - $DateTime = [DateTime]::ParseExact($line.DateTime, 'M/d/yyyy h:mm:ss tt', $null) - } catch { - Write-Verbose ("We could not parse the date time on: {0}" -f $line) - } - if ($DateTime) { - if (($testTime.ToUniversalTime().Subtract($DateTime) -lt $marginTime) -and - ($testTime.ToUniversalTime().Subtract($DateTime) -gt - $marginTime)) { - if (($line.UrlHost.Equals($server, [System.StringComparison]::OrdinalIgnoreCase)) -and - ($line.UrlStem.Equals('/ecp/x.js', [System.StringComparison]::OrdinalIgnoreCase)) -and - ($line.ScanResult.Equals('Detected', [System.StringComparison]::OrdinalIgnoreCase))) { - Write-Host "" - Write-Host "We found a detection in HttpRequestFiltering logs: " -ForegroundColor Green - Write-Host "$line" - $detected = $true - } + $timeout1min = (Get-Date).AddMinutes(1) + $foundRequest = $false + do { + Start-Sleep -Seconds 2 + $file = $null + $file = Get-ChildItem $HttpRequestFilteringLogFolder -Filter "HttpRequestFiltering_*.log" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 -Property * + if ($file) { + $found = $null + $found = $file | Get-Content | Select-String "/ecp/CSS-Test-$StringDate.js" + if ($found) { + if ($found.Line -match "Detected") { + Write-Host "We found the request Detected in HttpRequestFiltering logs: " -ForegroundColor Green + } else { + Write-Warning "We found the request in HttpRequestFiltering logs but was not detected: " } + Write-Host "$($found.Line)" + $foundRequest = $true } } + } while ((-not $foundRequest) -and ((Get-Date) -lt $timeout1min)) + if (-not $foundRequest) { + Write-Warning "We have not found the request." } - } while ((-not $detected) -and ((Get-Date) -lt $timeout)) - if ((Get-Date) -ge $timeout) { - Write-Warning "We have not found activity on the server in the last minute." - } - if (-not $detected) { - Write-Warning "We have not found a detection." + } else { + Write-Host "We could not access HttpRequestFiltering folder on $Server" -ForegroundColor Red } } else { - Write-Host "We could not access HttpRequestFiltering folder on $server" -ForegroundColor Red + Write-Host "Cannot get Exchange installation path on $Server" -ForegroundColor Red } } else { - Write-Host "Check your log files located in %ExchangeInstallPath%\Logging\HttpRequestFiltering\ in all server that provide $server endpoint" + Write-Host "Check your log files located in %ExchangeInstallPath%\Logging\HttpRequestFiltering\ in all server that provide $Server endpoint" } - } elseif ($_.Exception.Status -eq [System.Net.WebExceptionStatus]::ProtocolError -and - $_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::InternalServerError) { - $host.ui.RawUI.ForegroundColor = "Red" - Write-Host $msgNewLine - Write-Host $Message - Write-Host $msgNewLine - $host.ui.RawUI.ForegroundColor = "Yellow" - Write-Host "If you are using Microsoft Defender, RealTime protection could be disabled or then AMSI may be disabled." - Write-Host "If you are using a 3rd Party AntiVirus Product that may not be AMSI capable (Please Check with your AntiVirus Provider for Exchange AMSI Support)" - $host.ui.RawUI.ForegroundColor = $currentForegroundColor } elseif ($_.Exception.Status -eq [System.Net.WebExceptionStatus]::NameResolutionFailure) { $host.ui.RawUI.ForegroundColor = "Red" Write-Host $msgNewLine Write-Host $Message + Write-Host "`nWe could not find the server requested. Please check the name of the server." Write-Host $msgNewLine $host.ui.RawUI.ForegroundColor = $currentForegroundColor } else { @@ -321,13 +291,12 @@ begin { if ($IgnoreSSL) { [System.Net.ServicePointManager]::ServerCertificateValidationCallback = $null try { - Invoke-WebRequest https://$ExchangeServer -TimeoutSec 1 -ErrorAction SilentlyContinue | Out-Null + Invoke-WebRequest https://$Server -TimeoutSec 1 -ErrorAction SilentlyContinue | Out-Null } catch { - Write-Verbose "We could not connect to https://$ExchangeServer (Expected)" + Write-Verbose "We could not connect to https://$Server (Expected)" } } } - Write-Host "Ended test at $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")" } function CheckAMSIConfig { @@ -343,67 +312,69 @@ begin { $AMSIProviders = Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\AMSI\Providers' -Recurse -ErrorAction SilentlyContinue if ($AMSIProviders) { Write-Host "Providers:" - $AMSIProviders.Name | Out-Host $providerCount = 0 foreach ($provider in $AMSIProviders) { - $provider -match '[0-9A-Fa-f\-]{36}' | Out-Null - foreach ($m in $Matches.Values) { - $key = "HKLM:\SOFTWARE\Classes\ClSid\{$m}" - $providers = $null - $providers = Get-ChildItem $key -ErrorAction SilentlyContinue - if ($providers) { - $providerCount++ - Write-Host "" - Write-Host "Provider $($providerCount):" - $providers | Format-Table -AutoSize | Out-Host - $path = $null - $path = ($providers | Where-Object { $_.PSChildName -eq 'InprocServer32' }).GetValue('') - if ($path) { - $WindowsDefenderPath = $path.Substring(1, $path.LastIndexOf("\")) - if ($WindowsDefenderPath -like '*Windows Defender*') { - Write-Host "Windows Defender with AMSI integration found." -ForegroundColor Green - $checkCmdLet = $null - $checkCmdLet = Get-Command Get-MpComputerStatus -ErrorAction SilentlyContinue - if ($null -eq $checkCmdLet) { - Write-Warning "Get-MpComputerStatus cmdLet is not available" - } else { - if ((Get-MpComputerStatus).RealTimeProtectionEnabled) { - Write-Host "Windows Defender has Real Time Protection Enabled" -ForegroundColor Green + Write-Host "`nProvider $($providerCount+1): $($provider.PSChildName)" -ForegroundColor DarkGreen + # when using -match we set the variable $Match when a true value is performed. + $foundMatch = $provider -match '[0-9A-Fa-f\-]{36}' + if ($foundMatch) { + foreach ($m in $Matches.Values) { + $key = "HKLM:\SOFTWARE\Classes\ClSid\{$m}" + $providers = $null + $providers = Get-ChildItem $key -ErrorAction SilentlyContinue + if ($providers) { + $providerCount++ + $providers | Format-Table -AutoSize | Out-Host + $path = $null + $path = ($providers | Where-Object { $_.PSChildName -eq 'InprocServer32' }).GetValue('') + if ($path) { + $WindowsDefenderPath = $path.Substring(1, $path.LastIndexOf("\")) + if ($WindowsDefenderPath -like '*Windows Defender*') { + Write-Host "Windows Defender with AMSI integration found." -ForegroundColor Green + $checkCmdLet = $null + $checkCmdLet = Get-Command Get-MpComputerStatus -ErrorAction SilentlyContinue + if ($null -eq $checkCmdLet) { + Write-Warning "Get-MpComputerStatus cmdLet is not available" } else { - Write-Warning "Windows Defender has Real Time Protection Disabled" - } - } - Write-Host "It should be version 1.1.18300.4 or newest." - if (Test-Path $WindowsDefenderPath -PathType Container) { - $folder = Get-ChildItem $WindowsDefenderPath | Sort-Object LastWriteTime -Descending | Select-Object -First 1 - $process = Join-Path $folder.FullName "MpCmdRun.exe" - if (Test-Path $process -PathType Leaf) { - $DefenderVersion = $null - $DefenderVersion = [System.Version]::new((& $process -SignatureUpdate | Where-Object { $_.StartsWith('Engine Version:') }).Split(' ')[2]) - if ($DefenderVersion) { - if ($DefenderVersion -ge "1.1.18300.4") { - Write-Host "Windows Defender version supported for AMSI: $DefenderVersion" -ForegroundColor Green + if ((Get-MpComputerStatus).RealTimeProtectionEnabled) { + Write-Host "Windows Defender has Real Time Protection Enabled" -ForegroundColor Green + } else { + Write-Warning "Windows Defender has Real Time Protection Disabled" + } + Write-Host "It should be version 1.1.18300.4 or newest." + if (Test-Path $WindowsDefenderPath -PathType Container) { + $process = Join-Path -Path $WindowsDefenderPath -ChildPath "MpCmdRun.exe" + if (Test-Path $process -PathType Leaf) { + $DefenderVersion = $null + $DefenderVersion = [System.Version]::new((& $process -SignatureUpdate | Where-Object { $_.StartsWith('Engine Version:') }).Split(' ')[2]) + if ($DefenderVersion) { + if ($DefenderVersion -ge "1.1.18300.4") { + Write-Host "Windows Defender version supported for AMSI: $DefenderVersion" -ForegroundColor Green + } else { + Write-Warning "Windows Defender version Non-supported for AMSI: $DefenderVersion" + } + } else { + Write-Warning "We could not get Windows Defender version " + } } else { - Write-Warning "Windows Defender version Non-supported for AMSI: $DefenderVersion" + Write-Warning "We did not find Windows Defender MpCmdRun.exe." } } else { - Write-Warning "We could not get Windows Defender version " + Write-Warning "We did not find Windows Defender Path." } - } else { - Write-Warning "We did not find Windows Defender MpCmdRun.exe." } } else { - Write-Warning "We did not find Windows Defender Path." + Write-Warning "It is not Windows Defender AV, check with your provider." } } else { - Write-Warning "It is not Windows Defender AV, check with your provider." + Write-Warning "We did not find AMSI providers." } } else { - Write-Warning "We did not find AMSI providers." + Write-Host "We did not find $m ClSid registered" -ForegroundColor Red } - } else { - Write-Host "We did not find any AMSI provider on ClSid for $m" -ForegroundColor Red } + } else { + Write-Warning "We did not find any ClSid on $($provider.PSChildName) AMSI provider." } } } else { @@ -412,27 +383,10 @@ begin { } Write-Host "" - "Checking AMSI Provider on $ExchangeServer" + Write-Host "Checking AMSI Provider on $ExchangeServer" Write-Host "" Invoke-ScriptBlockHandler -ComputerName $ExchangeServer -ScriptBlock $AMSIProvidersSB - $getSOs = $null - $getSOs = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { - ($_.ComponentName.Equals('Cafe', [System.StringComparison]::OrdinalIgnoreCase)) -and - ($_.SectionName.Equals('HttpRequestFiltering', [System.StringComparison]::OrdinalIgnoreCase)) -and - ($_.Parameters -contains 'Enabled=False') -and - ($null -ne $_.Server -and ($_.Server -contains $ExchangeServer)) } - if ($getSOs) { - $getSOs | Out-Host - foreach ($so in $getSOs) { - if ($so.Status -eq 'Accepted') { - Write-Warning "AMSI is Disabled by $($so.Name) SettingOverride for $ExchangeServer" - } else { - Write-Host "AMSI is Disabled by $($so.Name) SettingOverride for $ExchangeServer but it is not Accepted." -ForegroundColor Red - } - } - } - $FEEcpWebConfig = $null $CAEEcpWebConfig = $null @@ -487,10 +441,27 @@ begin { Write-Host "Cannot get Exchange installation path on $server" -ForegroundColor Red } - Write-Host $msgNewLine - Write-Host "AMSI is Enabled on Server $ExchangeServer." -ForegroundColor Green - Write-Host "We did not find any Settings Override that remove AMSI on server $ExchangeServer." - Write-Host "" + $getSOs = $null + $getSOs = Get-SettingOverride -ErrorAction SilentlyContinue | Where-Object { + ($_.ComponentName.Equals('Cafe', [System.StringComparison]::OrdinalIgnoreCase)) -and + ($_.SectionName.Equals('HttpRequestFiltering', [System.StringComparison]::OrdinalIgnoreCase)) -and + ($_.Parameters -contains 'Enabled=False') -and + ($null -ne $_.Server -and ($_.Server -contains $ExchangeServer)) } + if ($getSOs) { + $getSOs | Out-Host + foreach ($so in $getSOs) { + if ($so.Status -eq 'Accepted') { + Write-Warning "AMSI is Disabled by $($so.Name) SettingOverride for $ExchangeServer" + } else { + Write-Host "AMSI is Disabled by $($so.Name) SettingOverride for $ExchangeServer but it is not Accepted." -ForegroundColor Red + } + } + } else { + Write-Host $msgNewLine + Write-Host "AMSI is Enabled on Server $ExchangeServer." -ForegroundColor Green + Write-Host "We did not find any Settings Override that remove AMSI on server $ExchangeServer." + Write-Host "" + } } } @@ -639,9 +610,9 @@ process { Write-Host "Testing $($server):" -ForegroundColor Magenta Write-Host "" if ($fullList -contains $server) { - CheckServerAMSI -ExchangeServer $server -IsServer + CheckServerAMSI -Server $server -IsExchangeServer } else { - CheckServerAMSI -ExchangeServer $server + CheckServerAMSI -Server $server } Write-Host "" } From 8ce9cdc949112d6d9ee9f879bf2aab1a0f1f2760 Mon Sep 17 00:00:00 2001 From: canthv0 Date: Mon, 17 Jul 2023 12:33:00 -0400 Subject: [PATCH 12/19] updated language around finding modules. --- Diagnostics/AVTester/Test-ExchAVExclusions.ps1 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 b/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 index ea4c13a232..17b0ebf3cc 100644 --- a/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 +++ b/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 @@ -27,6 +27,9 @@ Once the files are created it will wait 300 seconds for AV to "see" and remove t Pulls all Exchange processes and their modules. Excludes known modules and reports all unknown modules. +Unknown modules should be reviewed to ensure they are expected. +AV Modules loaded into Exchange Processes indicate that AV Process Exclusions are NOT properly configured. + .PARAMETER Recurse Will test not just the root folders but all SubFolders. Generally should not be needed unless all folders pass without -Recuse but AV is still suspected. @@ -394,8 +397,10 @@ foreach ($process in $ServerProcess) { # Final output for process detection if ($UnexpectedModuleFound -gt 0) { Write-SimpleLogFile -string ("Found $($UnexpectedModuleFound) processes with unexpected modules loaded") -Name $LogFile -OutHost + Write-SimpleLogFile ("AV Modules loaded in Exchange processess generally indicates that exclusions are not set properly.") -Name $LogFile -OutHost + Write-SimpleLogFile ("Non AV Modules loaded into Exchange processes maybe expected depending on applications installed.") -Name $LogFile -OutHost Write-Warning ("Review " + $OutputProcessPath + " For more information.") - Write-SimpleLogFile ("If a module is labeled `"Unexpected`" in error please submit the log file to ExToolsFeedback@microsoft.com" ) -Name $LogFile -OutHost + } else { Write-SimpleLogFile -string ("No Unexpected modules found loaded.") -Name $LogFile -OutHost } From 3843ded4a33d5e824bdc49449fec82147d7faacd Mon Sep 17 00:00:00 2001 From: canthv0 Date: Mon, 17 Jul 2023 12:47:46 -0400 Subject: [PATCH 13/19] Spelling fix --- Diagnostics/AVTester/Test-ExchAVExclusions.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 b/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 index 17b0ebf3cc..1dcf766375 100644 --- a/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 +++ b/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 @@ -397,7 +397,7 @@ foreach ($process in $ServerProcess) { # Final output for process detection if ($UnexpectedModuleFound -gt 0) { Write-SimpleLogFile -string ("Found $($UnexpectedModuleFound) processes with unexpected modules loaded") -Name $LogFile -OutHost - Write-SimpleLogFile ("AV Modules loaded in Exchange processess generally indicates that exclusions are not set properly.") -Name $LogFile -OutHost + Write-SimpleLogFile ("AV Modules loaded in Exchange processes generally indicates that exclusions are not set properly.") -Name $LogFile -OutHost Write-SimpleLogFile ("Non AV Modules loaded into Exchange processes maybe expected depending on applications installed.") -Name $LogFile -OutHost Write-Warning ("Review " + $OutputProcessPath + " For more information.") From 8130fe99c9858fef05f8c30a0cd4575cfb9535f1 Mon Sep 17 00:00:00 2001 From: canthv0 Date: Mon, 17 Jul 2023 13:04:21 -0400 Subject: [PATCH 14/19] Format Fix --- Diagnostics/AVTester/Test-ExchAVExclusions.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 b/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 index 1dcf766375..f3e241c52c 100644 --- a/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 +++ b/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 @@ -400,7 +400,6 @@ if ($UnexpectedModuleFound -gt 0) { Write-SimpleLogFile ("AV Modules loaded in Exchange processes generally indicates that exclusions are not set properly.") -Name $LogFile -OutHost Write-SimpleLogFile ("Non AV Modules loaded into Exchange processes maybe expected depending on applications installed.") -Name $LogFile -OutHost Write-Warning ("Review " + $OutputProcessPath + " For more information.") - } else { Write-SimpleLogFile -string ("No Unexpected modules found loaded.") -Name $LogFile -OutHost } From 9e84c742aa4b17d181bf26631e505fb18fb2f02c Mon Sep 17 00:00:00 2001 From: canthv0 Date: Mon, 17 Jul 2023 14:28:16 -0400 Subject: [PATCH 15/19] Updated from unknown to non-default --- .../AVTester/Test-ExchAVExclusions.ps1 | 16 +++++----- docs/Diagnostics/Test-ExchAVExclusions.md | 31 ++++++++++++++++--- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 b/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 index f3e241c52c..fd53f3fc63 100644 --- a/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 +++ b/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 @@ -11,7 +11,7 @@ .SYNOPSIS Uses EICAR files to verify that all Exchange paths that should be excluded from AV scanning are excluded. -Checks Exchange processes for "unknown" modules being loaded into them. +Checks Exchange processes for Non-Default modules being loaded into them. .DESCRIPTION Writes an EICAR test file https://en.wikipedia.org/wiki/EICAR_test_file to all paths specified by @@ -25,9 +25,9 @@ IF the file is not removed then it should be properly excluded. Once the files are created it will wait 300 seconds for AV to "see" and remove the file. Pulls all Exchange processes and their modules. -Excludes known modules and reports all unknown modules. +Excludes known modules and reports all Non-Default modules. -Unknown modules should be reviewed to ensure they are expected. +Non-Default modules should be reviewed to ensure they are expected. AV Modules loaded into Exchange Processes indicate that AV Process Exclusions are NOT properly configured. .PARAMETER Recurse @@ -41,8 +41,8 @@ $env:LOCALAPPDATA\ExchAvExclusions.log List of Scanned Folders: $env:LOCALAPPDATA\BadExclusions.txt -List of Unknown Processes -$env:LOCALAPPDATA UnknownModules.txt +List of Non-Default Processes +$env:LOCALAPPDATA NonDefaultModules.txt .EXAMPLE .\Test-ExchAVExclusions.ps1 @@ -374,8 +374,8 @@ foreach ($process in $ServerProcess) { # Remove Microsoft modules $ProcessModules = $ProcessModules | Where-Object { $_.FileVersionInfo.CompanyName -ne "Microsoft Corporation." -and $_.FileVersionInfo.CompanyName -ne "Microsoft" -and $_.FileVersionInfo.CompanyName -ne "Microsoft Corporation" } - # Generate and output path for an unknown modules file: - $OutputProcessPath = Join-Path $env:LOCALAPPDATA UnknownModules.txt + # Generate and output path for an Non-Default modules file: + $OutputProcessPath = Join-Path $env:LOCALAPPDATA NonDefaultModules.txt # Clear out modules from the allow list foreach ($module in $ModuleAllowList) { @@ -401,5 +401,5 @@ if ($UnexpectedModuleFound -gt 0) { Write-SimpleLogFile ("Non AV Modules loaded into Exchange processes maybe expected depending on applications installed.") -Name $LogFile -OutHost Write-Warning ("Review " + $OutputProcessPath + " For more information.") } else { - Write-SimpleLogFile -string ("No Unexpected modules found loaded.") -Name $LogFile -OutHost + Write-SimpleLogFile -string ("No Non-Default modules found loaded.") -Name $LogFile -OutHost } diff --git a/docs/Diagnostics/Test-ExchAVExclusions.md b/docs/Diagnostics/Test-ExchAVExclusions.md index 71ffd18b39..15dbd3b701 100644 --- a/docs/Diagnostics/Test-ExchAVExclusions.md +++ b/docs/Diagnostics/Test-ExchAVExclusions.md @@ -5,6 +5,7 @@ Download the latest release: [Test-ExchAVExclusions.ps1](https://github.com/micr Assists with testing Exchange Servers to determine if AV Exclusions have been properly set according to our documentation. [AV Exclusions Exchange 2016/2019](https://docs.microsoft.com/en-us/Exchange/antispam-and-antimalware/windows-antivirus-software?view=exchserver-2019) + [AV Exclusions Exchange 2013](https://docs.microsoft.com/en-us/exchange/anti-virus-software-in-the-operating-system-on-exchange-servers-exchange-2013-help) ## Usage @@ -17,13 +18,35 @@ IF the file is not removed then it should be properly excluded. Once the files are created it will wait 5 minutes for AV to "see" and remove the file. After finishing testing directories it will test Exchange Processes. -We pull all Exchange processes and the modules loaded into them. -Those are then compared to a list of known modules and anything "unknown" is reported. +Pulls all Exchange processes and their modules. +Excludes known modules and reports all Non-Default modules. + +Non-Default modules should be reviewed to ensure they are expected. +AV Modules loaded into Exchange Processes indicate that AV Process Exclusions are NOT properly configured. ... .\Test-ExchAVExclusions.ps1 ... +## Understanding the Output + +### File Output +Review the BadExclusions.txt file to see any file paths were identified as being scanned by AV. +Work with the AV Vendor to determine the best way to exclude these file paths according to our documentation: + +[AV Exclusions Exchange 2016/2019](https://docs.microsoft.com/en-us/Exchange/antispam-and-antimalware/windows-antivirus-software?view=exchserver-2019) + +### Process Output +Review NonDefaultModules.txt to determine if any Non-Default modules are loaded into Exchange processes. The output should have sufficient information to identity the souce of the flagged modules. + +```[FAIL] - PROCESS: msexchangerepl MODULE: scanner.dll COMPANY: Contoso Security LTT.``` + +If the Module is from an AV or Security software vendor it is a strong indication that process exclusions are not properly configured on the Exchange server. Please work with the security software vendor to ensure that they are properly configured according to: + +[AV Exclusions Exchange 2016/2019](https://docs.microsoft.com/en-us/Exchange/antispam-and-antimalware/windows-antivirus-software?view=exchserver-2019) + +[AV Exclusions Update](https://techcommunity.microsoft.com/t5/exchange-team-blog/update-on-the-exchange-server-antivirus-exclusions/ba-p/3751464) + ## Parameters @@ -40,5 +63,5 @@ $env:LOCALAPPDATA\ExchAvExclusions.log List of Folders and extensions Scanned by AV: $env:LOCALAPPDATA\BadExclusions.txt -List of Unknown Processes: -$env:LOCALAPPDATA UnknownModules.txt +List of Non-Default Processes: +$env:LOCALAPPDATA\NonDefaultModules.txt From c8db5853f58b464e84b043ecf281d075a2d72838 Mon Sep 17 00:00:00 2001 From: canthv0 Date: Mon, 17 Jul 2023 14:49:11 -0400 Subject: [PATCH 16/19] Spelling fix --- docs/Diagnostics/Test-ExchAVExclusions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Diagnostics/Test-ExchAVExclusions.md b/docs/Diagnostics/Test-ExchAVExclusions.md index 15dbd3b701..8c972679c8 100644 --- a/docs/Diagnostics/Test-ExchAVExclusions.md +++ b/docs/Diagnostics/Test-ExchAVExclusions.md @@ -37,9 +37,9 @@ Work with the AV Vendor to determine the best way to exclude these file paths ac [AV Exclusions Exchange 2016/2019](https://docs.microsoft.com/en-us/Exchange/antispam-and-antimalware/windows-antivirus-software?view=exchserver-2019) ### Process Output -Review NonDefaultModules.txt to determine if any Non-Default modules are loaded into Exchange processes. The output should have sufficient information to identity the souce of the flagged modules. +Review NonDefaultModules.txt to determine if any Non-Default modules are loaded into Exchange processes. The output should have sufficient information to identity the source of the flagged modules. -```[FAIL] - PROCESS: msexchangerepl MODULE: scanner.dll COMPANY: Contoso Security LTT.``` +```[FAIL] - PROCESS: ExchangeTransport MODULE: scanner.dll COMPANY: Contoso Security LTT.``` If the Module is from an AV or Security software vendor it is a strong indication that process exclusions are not properly configured on the Exchange server. Please work with the security software vendor to ensure that they are properly configured according to: From c3b0adb58470c421379783379facc287973b8915 Mon Sep 17 00:00:00 2001 From: canthv0 Date: Tue, 18 Jul 2023 13:22:39 -0400 Subject: [PATCH 17/19] Spelling and grammer changes --- Diagnostics/AVTester/Test-ExchAVExclusions.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 b/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 index fd53f3fc63..8e39c11bc5 100644 --- a/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 +++ b/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 @@ -28,7 +28,7 @@ Pulls all Exchange processes and their modules. Excludes known modules and reports all Non-Default modules. Non-Default modules should be reviewed to ensure they are expected. -AV Modules loaded into Exchange Processes indicate that AV Process Exclusions are NOT properly configured. +AV Modules loaded into Exchange Processes may indicate that AV Process Exclusions are NOT properly configured. .PARAMETER Recurse Will test not just the root folders but all SubFolders. @@ -397,8 +397,8 @@ foreach ($process in $ServerProcess) { # Final output for process detection if ($UnexpectedModuleFound -gt 0) { Write-SimpleLogFile -string ("Found $($UnexpectedModuleFound) processes with unexpected modules loaded") -Name $LogFile -OutHost - Write-SimpleLogFile ("AV Modules loaded in Exchange processes generally indicates that exclusions are not set properly.") -Name $LogFile -OutHost - Write-SimpleLogFile ("Non AV Modules loaded into Exchange processes maybe expected depending on applications installed.") -Name $LogFile -OutHost + Write-SimpleLogFile ("AV Modules loaded in Exchange processes may indicate that exclusions are not properly configured.") -Name $LogFile -OutHost + Write-SimpleLogFile ("Non AV Modules loaded into Exchange processes may be expected depending on applications installed.") -Name $LogFile -OutHost Write-Warning ("Review " + $OutputProcessPath + " For more information.") } else { Write-SimpleLogFile -string ("No Non-Default modules found loaded.") -Name $LogFile -OutHost From ff5ee9124cc6aacf49988baca22e2d4892878f4b Mon Sep 17 00:00:00 2001 From: canthv0 Date: Tue, 18 Jul 2023 13:30:52 -0400 Subject: [PATCH 18/19] Grammer Update --- Diagnostics/AVTester/Test-ExchAVExclusions.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 b/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 index 8e39c11bc5..7042f63f57 100644 --- a/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 +++ b/Diagnostics/AVTester/Test-ExchAVExclusions.ps1 @@ -401,5 +401,5 @@ if ($UnexpectedModuleFound -gt 0) { Write-SimpleLogFile ("Non AV Modules loaded into Exchange processes may be expected depending on applications installed.") -Name $LogFile -OutHost Write-Warning ("Review " + $OutputProcessPath + " For more information.") } else { - Write-SimpleLogFile -string ("No Non-Default modules found loaded.") -Name $LogFile -OutHost + Write-SimpleLogFile -string ("Did not find any Non-Default modules loaded.") -Name $LogFile -OutHost } From a8fabaf913820dfb05495ead8f421790d1adbebb Mon Sep 17 00:00:00 2001 From: canthv0 Date: Tue, 18 Jul 2023 13:35:06 -0400 Subject: [PATCH 19/19] grammar update --- docs/Diagnostics/Test-ExchAVExclusions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Diagnostics/Test-ExchAVExclusions.md b/docs/Diagnostics/Test-ExchAVExclusions.md index 8c972679c8..5ee01cfef1 100644 --- a/docs/Diagnostics/Test-ExchAVExclusions.md +++ b/docs/Diagnostics/Test-ExchAVExclusions.md @@ -41,7 +41,7 @@ Review NonDefaultModules.txt to determine if any Non-Default modules are loaded ```[FAIL] - PROCESS: ExchangeTransport MODULE: scanner.dll COMPANY: Contoso Security LTT.``` -If the Module is from an AV or Security software vendor it is a strong indication that process exclusions are not properly configured on the Exchange server. Please work with the security software vendor to ensure that they are properly configured according to: +If the Module is from an AV or Security software vendor it is a strong indication that process exclusions are not properly configured on the Exchange server. Please work with the vendor to ensure that they are properly configured according to: [AV Exclusions Exchange 2016/2019](https://docs.microsoft.com/en-us/Exchange/antispam-and-antimalware/windows-antivirus-software?view=exchserver-2019)