-
-
Save TonyBunce/13eb919a3838b146603dd1200fb4a092 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#based off of https://github.com/microsoft/CSS-Exchange/blob/main/Security/Test-Hafnium.ps1 | |
#Checks for signs of exploit from CVE-2021-26855, 26858, 26857, and 27065. | |
#THis version checks all exchange servers in the domain | |
#The CVE 26855 check is also enhanced to check the s | |
#From https://key2consulting.com/powershell-how-to-display-job-progress/ | |
function WriteJobProgress { | |
param($Job) | |
#Make sure the first child job exists | |
if ($Job.ChildJobs[0].Progress -ne $null) { | |
#Extracts the latest progress of the job and writes the progress | |
$jobProgressHistory = $Job.ChildJobs[0].Progress; | |
$latestProgress = $jobProgressHistory[$jobProgressHistory.Count - 1]; | |
$latestPercentComplete = $latestProgress | Select -expand PercentComplete; | |
$latestActivity = $latestProgress | Select -expand Activity; | |
$latestStatus = $latestProgress | Select -expand StatusDescription; | |
#When adding multiple progress bars, a unique ID must be provided. Here I am providing the JobID as this | |
Write-Progress -Id $Job.Id -Activity $latestActivity -Status $latestStatus -PercentComplete $latestPercentComplete; | |
} | |
} | |
function Get-26855() { | |
$IOCs = @() | |
if (Test-Path "$PSScriptRoot\ExchangeIOCHunter-26855.xml") { | |
Write-Host "Using cached log search results" | |
$IOCs = Import-Clixml "$PSScriptRoot\ExchangeIOCHunter-26855.xml" | |
} | |
else { | |
Write-Host "Checking servers for CVE-2021-26855 (HTTPProxy logs)" -ForegroundColor Green | |
Write-Host "Note: This could take a while. Servers are checked in parallel to speed up the check" -ForegroundColor Green | |
$Jobs = @() | |
foreach ($server in $exchangeServers) { | |
Write-Host " Collecing data from $($server.Name)" -ForegroundColor Green | |
$Jobs += Invoke-Command -ComputerName $server.Name -AsJob -ScriptBlock { | |
$badIPs = ('161.35.1.207', '157.230.221.198', '103.77.192.219', '104.140.114.110', '104.250.191.110', '108.61.246.56', '149.28.14.163', '157.230.221.198', '167.99.168.251', '185.250.151.72', '192.81.208.169', '203.160.69.66', '211.56.98.146', '5.254.43.18', '80.92.205.81') | |
$ipRegEx = ($badIPs | % { [Regex]::Escape($_) }) -Join ('|') | |
$exchangePath = (Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ExchangeServer\v15\Setup).MsiInstallPath | |
$files = (Get-ChildItem -Recurse -Path "$exchangePath\Logging\HttpProxy" -Filter '*.log').FullName | |
#if you only want to check specifc files you can uncomment the lines below and adjust accordingly | |
#$files = (Get-ChildItem -Recurse -Path "$exchangePath\Logging\HttpProxy" -Filter '*210226*.log').FullName | |
#$files += (Get-ChildItem -Recurse -Path "$exchangePath\Logging\HttpProxy" -Filter '*210227*.log').FullName | |
#$files += (Get-ChildItem -Recurse -Path "$exchangePath\Logging\HttpProxy" -Filter '*210228*.log').FullName | |
#$files += (Get-ChildItem -Recurse -Path "$exchangePath\Logging\HttpProxy" -Filter '*210303*.log').FullName | |
$count = 0 | |
#$allResults = @() | |
$sw = New-Object System.Diagnostics.Stopwatch | |
$sw.Start() | |
Write-Progress -Activity "[$($env:ComputerName)] Checking for CVE-2021-26855 in the HttpProxy logs" -Status "$count / $($files.Count)" -PercentComplete ($count * 100 / $files.Count) | |
$files | ForEach-Object { | |
$count++ | |
#Write-Progress -Activity "Checking for CVE-2021-26855 in the HttpProxy logs" -Status "$count / $($files.Count)" -PercentComplete ($count * 100 / $files.Count) | |
if ($sw.ElapsedMilliseconds -gt 750) { | |
Write-Progress -Activity "[$($env:ComputerName)] 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~|$ipRegEx|/ecp/y.js").Count -gt 0) { | |
Import-Csv -Path $_ | Where-Object { ($_.AuthenticatedUser -eq '' -and $_.AnchorMailbox -like 'ServerInfo~*/*' ) -or ($_.ClientIpAddress -in $badIPs) -or ($_.UrlStem -eq '/ecp/y.js') } | |
} | |
} | |
} | |
} | |
while (($Jobs | Where-Object { $_.State -ne "Completed" }).Count -gt 0) { | |
($Jobs | Where-Object { $_.State -ne "Completed" }) | % { | |
WriteJobProgress($_); | |
} | |
Start-Sleep -Seconds 1 | |
} | |
$IOCs = $Jobs | Wait-Job | Receive-Job | |
$IOCs = $IOCs | Sort DateTime | |
$IOCs | Export-Clixml "$PSScriptRoot\ExchangeIOCHunter-26855.xml" | |
} | |
if ($IOCs) { | |
Write-Warning "Suspicious entries found in $exchangePath\Logging\HttpProxy. Service logs will be searched for additional info" | |
$IOCs | Select DateTime, ClientIpAddress, UrlStem, TargetServer, AnchorMailbox, HttpStatus, BackEndStatus, RequestId | ft -AutoSize | Out-String -Width 512 | |
$IOCs | Select DateTime, ClientIpAddress, UrlStem, TargetServer, AnchorMailbox, HttpStatus, BackEndStatus, RequestId | Export-Csv -NoTypeInformation "$PSScriptRoot\ExchangeIOCHunter-26855.csv" -Force | |
foreach ($ioc in $IOCs) { | |
$server = $ioc.TargetServer | |
$fullPath = ($ioc.AnchorMailbox -split '/', 2)[1] | |
$service = ($fullPath -split '/')[0] | |
$requestId = $ioc.RequestId | |
$dateString = ($ioc.DateTime | Get-Date).ToUniversalTime().ToString("yyMMdd") | |
if ($ioc.UrlStem -eq '/rpc/') { | |
$service = "RpcHttp" | |
$server = $ioc.ServerHostName | |
} | |
Write-Host "$server $path $service $dateString $requestId" | |
Write-Host "IOC Details:" | |
Write-Host " Date: $(($ioc.DateTime | Get-Date).ToUniversalTime())" | |
Write-Host " Client IP: $($ioc.ClientIpAddress)" | |
Write-Host " UrlStem: $($ioc.UrlStem)" | |
Write-Host " Server: $server" | |
Write-Host " Path: $fullPath" | |
Write-Host " RequestID: $requestId" | |
Write-Host " Service Logs Details:" | |
if (!($server)) { | |
Write-Host "Target server not found - No additional logs to search" | |
continue; | |
} | |
if ($service -eq 'mapi') { | |
$service = 'MapiHttp' | |
} | |
if ($service -in ('ecp','RpcHttp')) { | |
$Job = Invoke-Command -ComputerName $server -AsJob -ScriptBlock { | |
param($dateString, $service, $requestId) | |
Write-Host "Checking $service logs on $($env:COMPUTERNAME) for RequestID $requestId on $dateString" | |
$exchangePath = (Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ExchangeServer\v15\Setup).MsiInstallPath | |
Write-Host "Checking these files:" | |
Write-Host (Get-ChildItem "$exchangePath\Logging\$service" -Filter "*$dateString*.log" -Recurse | Select FullName | Out-String) | |
$details = Get-ChildItem "$exchangePath\Logging\$service" -Filter "*$dateString*.log" -Recurse | Select-STring $requestId | |
if ($details) { | |
$details | % { | |
Write-Host " Log File: $($_.Path)" | |
Write-Host " Line Number: $($_.LineNumber)" | |
Write-Host " Line: $($_.Line)`r`n" | |
} | |
} | |
else { | |
Write-Host "No Match Found - additional investigation needed" -ForegroundColor Yellow | |
} | |
} -ArgumentList $dateString, $service, $requestId | |
$Job | Wait-Job | Receive-Job | |
} | |
else { | |
Invoke-Command -ComputerName $server -ScriptBlock { | |
param($dateString, $service, $requestId) | |
Write-Host "Checking $service logs on $($env:COMPUTERNAME)" | |
$exchangePath = (Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ExchangeServer\v15\Setup).MsiInstallPath | |
Import-Csv -Path (Get-ChildItem -Recurse -Path "$exchangePath\Logging\$service" -Filter "*$dateString*.log").FullName | ? { $_.RequestId -eq $requestId } | fl | |
} -ArgumentList $dateString, $service, $requestId | |
} | |
Write-Host "`r`n" | |
} | |
} | |
} | |
function Get-26858() { | |
Write-Host "`r`nChecking for CVE-2021-26858 in the OABGenerator logs" | |
foreach ($server in $exchangeServers) { | |
Write-Host " Collecing data from $($server.Name)" -ForegroundColor Green | |
Invoke-Command -ComputerName $server.Name -ScriptBlock { | |
$exchangePath = (Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ExchangeServer\v15\Setup).MsiInstallPath | |
$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" | |
foreach ($server in $exchangeServers) { | |
Write-Host " Collecing data from $($server.Name)" -ForegroundColor Green | |
$eventLogs = @(Get-WinEvent -ComputerName $server -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" | |
foreach ($server in $exchangeServers) { | |
Write-Host " Collecing data from $($server.Name)" -ForegroundColor Green | |
Invoke-Command -ComputerName $server.Name -ScriptBlock { | |
$exchangePath = (Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ExchangeServer\v15\Setup).MsiInstallPath | |
$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" | |
foreach ($server in $exchangeServers) { | |
Write-Host " Collecing data from $($server.Name)" -ForegroundColor Green | |
Invoke-Command -ComputerName $server.Name -ScriptBlock { | |
$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" -Include *.7z, *.zip, *.rar -ErrorAction SilentlyContinue | |
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 | |
} | |
} | |
} | |
} | |
$logFile = "$PSScriptRoot\$(get-date -f yyyyMMdd.HHmmss)-ExchangeIOCHunter.log" | |
Start-Transcript -Path $logFile | |
Write-Host "Logging to $logFile" | |
$exchangeServers = Get-ExchangeServer #| Select -First 2 | |
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" | |
Write-Host "It is based off off this Microsoft script https://github.com/microsoft/CSS-Exchange/blob/main/Security/Test-Hafnium.ps1`r`n" | |
Write-Host "These Servers will be Checked:" | |
Write-Host ($exchangeServers.Name) | |
Write-Host "" | |
Get-26855 | |
Get-26858 | |
Get-26857 | |
Get-27065 | |
Get-SuspiciousFiles | |
Stop-Transcript | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment