Skip to content

Instantly share code, notes, and snippets.

@TonyBunce
Last active March 10, 2021 15:28
Show Gist options
  • Save TonyBunce/13eb919a3838b146603dd1200fb4a092 to your computer and use it in GitHub Desktop.
Save TonyBunce/13eb919a3838b146603dd1200fb4a092 to your computer and use it in GitHub Desktop.
#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