diff --git a/Security/README.md b/Security/README.md index 2d07f3c39f..ab3d1e2316 100644 --- a/Security/README.md +++ b/Security/README.md @@ -25,3 +25,16 @@ Formerly known as Test-Hafnium, this script automates all four of the commands f Download the latest release here: [Download Test-ProxyLogon.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/Test-ProxyLogon.ps1) + +The most typical usage of this script is to check all Exchange servers and save the output, +by using the following syntax from Exchange Management Shell: + +`Get-ExchangeServer | .\Test-ProxyLogon.ps1 -OutPath $home\desktop\logs` + +To check the local server only, just run the script: + +`.\Test-ProxyLogon.ps1 -OutPath $home\desktop\logs` + +To display the results without saving them, drop the -Outpath parameter from either example above: + +`.\Test-ProxyLogon.ps1` diff --git a/Security/Test-ProxyLogon.ps1 b/Security/Test-ProxyLogon.ps1 index 9aadd6fbd8..16d2904c50 100644 --- a/Security/Test-ProxyLogon.ps1 +++ b/Security/Test-ProxyLogon.ps1 @@ -1,97 +1,284 @@ -#Checks for signs of exploit from CVE-2021-26855, 26858, 26857, and 27065. - -$exchangePath = (Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ExchangeServer\v15\Setup).MsiInstallPath - -function Get-26855() { - Write-Host "Checking for CVE-2021-26855 in the HttpProxy logs" - $files = (Get-ChildItem -Recurse -Path "$exchangePath\Logging\HttpProxy" -Filter '*.log').FullName - $count = 0 - $allResults = @() - $sw = New-Object System.Diagnostics.Stopwatch - $sw.Start() - $files | ForEach-Object { - $count++ - if ($sw.ElapsedMilliseconds -gt 500) { - Write-Progress -Activity "Checking for CVE-2021-26855 in the HttpProxy logs" -Status "$count / $($files.Count)" -PercentComplete ($count * 100 / $files.Count) - $sw.Restart() - } - if ((Get-ChildItem $_ -ErrorAction SilentlyContinue | Select-String "ServerInfo~").Count -gt 0) { - $fileResults = @(Import-Csv -Path $_ -ErrorAction SilentlyContinue | Where-Object { $_.AnchorMailbox -like 'ServerInfo~*/*' }) - $fileResults | ForEach-Object { - $allResults += $_ +# Checks for signs of exploit from CVE-2021-26855, 26858, 26857, and 27065. +# +# Examples +# +# Check the local Exchange server only and save the report: +# .\Test-ProxyLogon.ps1 -OutputPath $home\desktop\logs +# +# Check all Exchange servers and save the reports: +# Get-ExchangeServer | .\Test-ProxyLogon.ps1 -OutputPath $home\desktop\logs +# +# Check all Exchange servers, but only display the results, don't save them: +# Get-ExchangeServer | .\Test-ProxyLogon.ps1 + + +[CmdletBinding()] +param ( + [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + [string[]] + $ComputerName = $env:COMPUTERNAME, + + [string] + $OutPath +) + +process { + + function Test-ExchangeHafnium { + <# + .SYNOPSIS + Checks targeted exchange servers for signs of Hafnium vulnerability compromise. + + .DESCRIPTION + Checks targeted exchange servers for signs of Hafnium vulnerability compromise. + Will do so in parallel if more than one server is specified, so long as names aren't provided by pipeline. + + The vulnerabilities are described in CVE-2021-26855, 26858, 26857, and 27065 + + .PARAMETER ComputerName + The list of server names to scan for signs of compromise. + Do not provide these by pipeline if you want parallel processing. + + .PARAMETER Credential + Credentials to use for remote connections. + + .EXAMPLE + PS C:\> Test-ExchangeHafnium + + Scans the current computer for signs of Hafnium vulnerability compromise. + + .EXAMPLE + PS C:\> Test-ExchangeHafnium -ComputerName (Get-ExchangeServer).Fqdn + + Scans all exchange servers in the organization for Hafnium vulnerability compromises +#> + [CmdletBinding()] + param ( + [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + [string[]] + $ComputerName = $env:COMPUTERNAME, + + [pscredential] + $Credential + ) + begin { + #region Remoting Scriptblock + $scriptBlock = { + #region Functions + function Get-ExchangeInstallPath { + $p = (Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ExchangeServer\v15\Setup -ErrorAction SilentlyContinue).MsiInstallPath + if ($null -eq $p) { + $p = (Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ExchangeServer\v14\Setup -ErrorAction SilentlyContinue).MsiInstallPath + } + + return $p + } + + function Get-Cve26855 { + [CmdletBinding()] + param () + + Write-Progress -Activity "Checking for CVE-2021-26855 in the HttpProxy logs" + + $exchangePath = Get-ExchangeInstallPath + + $files = (Get-ChildItem -Recurse -Path "$exchangePath\Logging\HttpProxy" -Filter '*.log').FullName + $count = 0 + $allResults = @() + $sw = New-Object System.Diagnostics.Stopwatch + $sw.Start() + $files | ForEach-Object { + $count++ + + if ($sw.ElapsedMilliseconds -gt 500) { + Write-Progress -Activity "Checking for CVE-2021-26855 in the HttpProxy logs" -Status "$count / $($files.Count)" -PercentComplete ($count * 100 / $files.Count) + $sw.Restart() + } + + if ((Get-ChildItem $_ -ErrorAction SilentlyContinue | Select-String "ServerInfo~").Count -gt 0) { + $fileResults = @(Import-Csv -Path $_ -ErrorAction SilentlyContinue | Where-Object AnchorMailbox -Like 'ServerInfo~*/*' | Select-Object -Property DateTime, RequestId, ClientIPAddress, UrlHost, UrlStem, RoutingHint, UserAgent, AnchorMailbox, HttpStatus) + $fileResults | ForEach-Object { + $allResults += $_ + } + } + } + + Write-Progress -Activity "Checking for CVE-2021-26855 in the HttpProxy logs" -Completed + + return $allResults + } + + function Get-Cve26857 { + [CmdletBinding()] + param () + + Get-WinEvent -FilterHashtable @{ + LogName = 'Application' + ProviderName = 'MSExchange Unified Messaging' + Level = '2' + } -ErrorAction SilentlyContinue | Where-Object Message -Like "*System.InvalidCastException*" + } + + function Get-Cve26858 { + [CmdletBinding()] + param () + + $exchangePath = Get-ExchangeInstallPath + + Get-ChildItem -Recurse -Path "$exchangePath\Logging\OABGeneratorLog" | Select-String "Download failed and temporary file" -List | Select-Object -ExpandProperty Path + } + + function Get-Cve27065 { + [CmdletBinding()] + param () + + $exchangePath = Get-ExchangeInstallPath + + Get-ChildItem -Recurse -Path "$exchangePath\Logging\ECP\Server\*.log" -ErrorAction SilentlyContinue | Select-String "Set-.*VirtualDirectory" -List | Select-Object -ExpandProperty Path + } + + function Get-SuspiciousFile { + [CmdletBinding()] + param () + + foreach ($file in Get-ChildItem -Recurse -Path "$env:WINDIR\temp\lsass.*dmp") { + [PSCustomObject]@{ + ComputerName = $env:COMPUTERNAME + Type = 'LsassDump' + Path = $file.FullName + Name = $file.Name + } + } + foreach ($file in Get-ChildItem -Recurse -Path "c:\root\lsass.*dmp" -ErrorAction SilentlyContinue) { + [PSCustomObject]@{ + ComputerName = $env:COMPUTERNAME + Type = 'LsassDump' + Path = $file.FullName + Name = $file.Name + } + } + foreach ($file in Get-ChildItem -Recurse -Path $env:ProgramData -ErrorAction SilentlyContinue | Where-Object Extension -Match ".7z$|.zip$|.rar$") { + [PSCustomObject]@{ + ComputerName = $env:COMPUTERNAME + Type = 'SuspiciousArchive' + Path = $file.FullName + Name = $file.Name + } + } + } + #endregion Functions + + [PSCustomObject]@{ + ComputerName = $env:COMPUTERNAME + Cve26855 = @(Get-Cve26855) + Cve26857 = @(Get-Cve26857) + Cve26858 = @(Get-Cve26858) + Cve27065 = @(Get-Cve27065) + Suspicious = @(Get-SuspiciousFile) + } + } + #endregion Remoting Scriptblock + $parameters = @{ + ScriptBlock = $scriptBlock } + if ($Credential) { $parameters.Credential = $Credential } + } + process { + Invoke-Command @parameters -ComputerName $ComputerName } } - Write-Progress -Activity "Checking for CVE-2021-26855 in the HttpProxy logs" -Completed + function Write-HafniumReport { + <# + .SYNOPSIS + Processes output of Test-ExchangeHafnium for reporting on the console screen. + + .DESCRIPTION + Processes output of Test-ExchangeHafnium for reporting on the console screen. + + .PARAMETER InputObject + The reports provided by Test-ExchangeHafnium - if ($allResults.Length -gt 0) { - Write-Warning "Suspicious entries found in $exchangePath\Logging\HttpProxy. Check the .\CVE-2021-26855.csv log for specific entries." - if (Test-Path "$PSScriptRoot\CVE-2021-26855.log") { - Remove-Item $PSScriptRoot\CVE-2021-26855.log -Force + .PARAMETER OutPath + Path to a FOLDER in which to generate output logfiles. + This command will only write to the console screen if no path is provided. + + .EXAMPLE + PS C:\> Test-ExchangeHafnium -ComputerName (Get-ExchangeServer).Fqdn | Write-HafniumReport -OutPath C:\logs + + Gather data from all exchange servers in the organization and write a report to C:\logs +#> + [CmdletBinding()] + param ( + [parameter(ValueFromPipeline = $true)] + $InputObject, + + [string] + $OutPath + ) + + begin { + New-Item $OutPath -ItemType Directory -Force | Out-Null } - $allResults | Select-Object DateTime, RequestId, ClientIPAddress, UrlHost, UrlStem, RoutingHint, UserAgent, AnchorMailbox, HttpStatus | Export-Csv $PSScriptRoot\CVE-2021-26855.log - } else { - Write-Host "No suspicious entries found." -ForegroundColor Green - } -} - -function Get-26858() { - Write-Host "`r`nChecking for CVE-2021-26858 in the OABGenerator logs" - $logs = Get-ChildItem -Recurse -Path "$exchangePath\Logging\OABGeneratorLog" | Select-String "Download failed and temporary file" -List | Select-Object Path - if ($logs.Path.Count -gt 0) { - Write-Warning "Suspicious OAB download entries found in the following logs, please review them for `"Download failed and temporary file`" entries:" - $logs.Path - } else { - Write-Host "No suspicious entries found." -ForegroundColor Green - } -} - -function Get-26857() { - Write-Host "`r`nChecking for CVE-2021-26857 in the Event Logs" - $eventLogs = @(Get-WinEvent -FilterHashtable @{LogName = 'Application'; ProviderName = 'MSExchange Unified Messaging'; Level = '2' } -ErrorAction SilentlyContinue | Where-Object { $_.Message -like "*System.InvalidCastException*" }) - if ($eventLogs.Count -gt 0) { - Write-Warning "Suspicious event log entries for Source `"MSExchange Unified Messaging`" and Message `"System.InvalidCastException`" were found. These may indicate exploitation. Please review these event log entries for more details." - } else { - Write-Host "No suspicious entries found." -ForegroundColor Green - } -} - -function Get-27065() { - Write-Host "`r`nChecking for CVE-2021-27065 in the ECP Logs" - $logs = Get-ChildItem -Recurse -Path "$exchangePath\Logging\ECP\Server\*.log" | Select-String "Set-.*VirtualDirectory" -List | Select-Object Path - if ($logs.Path.Count -gt 0) { - Write-Warning "Suspicious virtual directory modifications found in the following logs, please review them for `"Set-*VirtualDirectory`" entries:" - $logs.Path - } else { - Write-Host "No suspicious entries found." -ForegroundColor Green - } -} - -function Get-SuspiciousFiles() { - Write-Host "`r`nChecking for suspicious files" - $lsassFiles = @(Get-ChildItem -Recurse -Path "$env:WINDIR\temp\lsass.*dmp") - $lsassFiles += @(Get-ChildItem -Recurse -Path "c:\root\lsass.*dmp") - if ($lsassFiles.Count -gt 0) { - Write-Warning "lsass.exe dumps found, please verify these are expected:" - $lsassFiles.FullName - } else { - Write-Host "No suspicious lsass dumps found." -ForegroundColor Green - } - $zipFiles = @(Get-ChildItem -Recurse -Path "$env:ProgramData" -ErrorAction SilentlyContinue | Where-Object { $_.Extension -match ".7z|.zip|.rar" }) + process { + foreach ($report in $InputObject) { + Write-Host "Hafnium Status: Exchange Server $($report.ComputerName)" + if (-not ($report.Cve26855.Count -or $report.Cve26857.Count -or $report.Cve26858.Count -or $report.Cve27065.Count -or $report.Suspicious.Count)) { + Write-Host " Nothing suspicious detected" -ForegroundColor Green + Write-Host "" + continue + } - if ($zipFiles.Count -gt 0) { - Write-Warning "`r`nZipped files found in $env:ProgramData, please verify these are expected:" - $zipFiles.FullName - } else { - Write-Host "`r`nNo suspicious zip files found." -ForegroundColor Green + if ($report.Cve26855.Count -gt 0) { + Write-Host " [CVE-2021-26855] Suspicious activity found in Http Proxy log!" -ForegroundColor Red + if ($OutPath) { + $newFile = Join-Path -Path $OutPath -ChildPath "$($report.ComputerName)-Cve-2021-26855.csv" + $report.Cve26855 | Export-Csv -Path $newFile + Write-Host " Report exported to: $newFile" + } else { + $report.Cve26855 | Format-Table DateTime, AnchorMailbox -AutoSize | Out-Host + } + Write-Host "" + } + if ($report.Cve26857.Count -gt 0) { + Write-Host " [CVE-2021-26857] Suspicious activity found in Eventlog!" -ForegroundColor Red + Write-Host " $(@($report.Cve26857).Count) events found" + if ($OutPath) { + $newFile = Join-Path -Path $OutPath -ChildPath "$($report.ComputerName)-Cve-2021-26857.csv" + $report.Cve26857 | Select-Object TimeCreated, MachineName, Message | Export-Csv -Path $newFile + Write-Host " Report exported to: $newFile" + } + Write-Host "" + } + if ($report.Cve26858.Count -gt 0) { + Write-Host " [CVE-2021-26858] Suspicious activity found in OAB generator logs!" -ForegroundColor Red + Write-Host " Please review the following files for 'Download failed and temporary file' entries:" + foreach ($entry in $report.Cve26858) { + Write-Host " $entry" + } + if ($OutPath) { + $newFile = Join-Path -Path $OutPath -ChildPath "$($report.ComputerName)-Cve-2021-26858.log" + $report.Cve26858 | Set-Content -Path $newFile + Write-Host " Report exported to: $newFile" + } + Write-Host "" + } + if ($report.Suspicious.Count -gt 0) { + Write-Host " Other suspicious files found: $(@($report.Suspicious).Count)" + if ($OutPath) { + $newFile = Join-Path -Path $OutPath -ChildPath "$($report.ComputerName)-other.csv" + $report.Suspicious | Export-Csv -Path $newFile + Write-Host " Report exported to: $newFile" + } else { + foreach ($entry in $report.Suspicious) { + Write-Host " $($entry.Type) : $($entry.Path)" + } + } + } + } + } } -} - -Write-Host "This script checks for exploits using the instructions outlined in https://www.microsoft.com/security/blog/2021/03/02/hafnium-targeting-exchange-servers`r`n" -Get-26855 -Get-26858 -Get-26857 -Get-27065 -Get-SuspiciousFiles + + $ComputerName | Test-ExchangeHafnium | Write-HafniumReport -OutPath $OutPath +} \ No newline at end of file