Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Security/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
363 changes: 275 additions & 88 deletions Security/Test-ProxyLogon.ps1
Original file line number Diff line number Diff line change
@@ -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
}