From 350b9906759c1e0f317a2a0a32623ae56a4fe095 Mon Sep 17 00:00:00 2001 From: Bill Long Date: Wed, 10 Mar 2021 11:18:49 -0600 Subject: [PATCH 01/10] Make the script titles link to release --- Security/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Security/README.md b/Security/README.md index 810ce1b699..5e1f19faee 100644 --- a/Security/README.md +++ b/Security/README.md @@ -1,6 +1,6 @@ # Security scripts -## Test-ProxyLogon.ps1 +## [Test-ProxyLogon.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/Test-ProxyLogon.ps1) Formerly known as Test-Hafnium, this script automates all four of the commands found in the [Hafnium blog post](https://www.microsoft.com/security/blog/2021/03/02/hafnium-targeting-exchange-servers/). It also has a progress bar and some performance tweaks to make the CVE-2021-26855 test run much faster. @@ -25,7 +25,7 @@ To display the results without saving them, pass -DisplayOnly: `.\Test-ProxyLogon.ps1 -DisplayOnly` -## ExchangeMitigations.ps1 +## [ExchangeMitigations.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/ExchangeMitigations.ps1) This script contains 4 mitigations to help address the following vulnerabilities: * CVE-2021-26855 @@ -77,7 +77,7 @@ To rollback multiple or specific mitigations `.\ExchangeMitigations.ps1 -WebSiteNames "Default Web Site" -RollbackECPAppPoolMitigation -RollbackOABAppPoolMitigation` -## CompareExchangeHashes.ps1 +## [CompareExchangeHashes.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/CompareExchangeHashes.ps1) This script provides a mechanism for malicious file detection on Exchange servers running E13, E16 or E19 versions. For more information please go to [https://aka.ms/exchangevulns](https://aka.ms/exchangevulns). @@ -104,7 +104,7 @@ Submitting files for analysis: * Please submit the output file for analysis in the malware analysis portal [here](https://www.microsoft.com/en-us/wdsi/filesubmission). Please add the text "ExchangeMarchCVE" in "Additional Information" field on the portal submission form. * Instructions on how to use the portal can be found [here](https://docs.microsoft.com/en-us/windows/security/threat-protection/intelligence/submission-guide). -## BackendCookieMitigation.ps1 +## [BackendCookieMitigation.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/BackendCookieMitigation.ps1) This mitigation will filter https requests that contain malicious X-AnonResource-Backend and malformed X-BEResource cookies which were found to be used in CVE-2021-26855. @@ -142,7 +142,7 @@ To rollback - Note: This does not remove the IIS Rewrite module, only the rules. `PS C:\> BackendCookieMitigation.ps1 -WebSiteNames "Default Web Site" -RollbackMitigation -Verbose` -## http-vuln-cve2021-26855.nse +## [http-vuln-cve2021-26855.nse](https://github.com/microsoft/CSS-Exchange/releases/latest/download/http-vuln-cve2021-26855.nse) This file is for use with nmap. It detects whether the specified URL is vulnerable to the Exchange Server SSRF Vulnerability (CVE-2021-26855). For usage information, please read the top of the file. From 474afc88d0ae5593e9073003dd3f0f0924f5a3b9 Mon Sep 17 00:00:00 2001 From: Bill Long Date: Wed, 10 Mar 2021 13:25:14 -0600 Subject: [PATCH 02/10] Make download links more prominent --- Setup/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Setup/README.md b/Setup/README.md index fddfe2353f..166104c0b6 100644 --- a/Setup/README.md +++ b/Setup/README.md @@ -1,4 +1,6 @@ -# SetupAssist.ps1 +# [SetupAssist.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/SetupAssist.ps1) + +Download the latest release here: [https://github.com/microsoft/CSS-Exchange/releases/latest/download/SetupAssist.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/SetupAssist.ps1) This script is meant to be run on the system where you are running setup from. It currently checks and displays the following when just running it: @@ -20,9 +22,9 @@ Parameter | Description ----------|------------ [string]OtherWellKnownObjectsContainer | Pass the Distinguished Name of Microsoft Exchange container to look at the OtherWellKnownObjects attributes -Download the latest release [here](https://github.com/microsoft/CSS-Exchange/releases/latest/download/SetupAssist.ps1) +# [SetupLogReviewer.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/SetupLogReviewer.ps1) -# SetupLogReviewer.ps1 +Download the latest release here: [https://github.com/microsoft/CSS-Exchange/releases/latest/download/SetupLogReviewer.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/SetupLogReviewer.ps1) This script is meant to be run against the Exchange Setup Logs located at `C:\ExchangeSetupLogs\ExchangeSetup.log`. You can run this on the server, or on a personal computer. From 74165688bac6fd29d852bc3bc1d08b67c98b7e53 Mon Sep 17 00:00:00 2001 From: Anshul Dube Date: Wed, 10 Mar 2021 12:12:49 -0800 Subject: [PATCH 03/10] Merge changes --- Security/CompareExchangeHashes.ps1 | 128 +++++++++++++++++------------ 1 file changed, 74 insertions(+), 54 deletions(-) diff --git a/Security/CompareExchangeHashes.ps1 b/Security/CompareExchangeHashes.ps1 index c70897b926..c28f2509b9 100644 --- a/Security/CompareExchangeHashes.ps1 +++ b/Security/CompareExchangeHashes.ps1 @@ -272,7 +272,7 @@ $VALID_VERSIONS = @{ ` } -$MARK_AS_SUSPICIOUS_FROM = (Get-Date -Date "12/01/2020") +$MARK_AS_SUSPICIOUS_FROM = (Get-Date -Date "12/01/2020" -Format "MM/dd/yyyy HH:mm:ss") function PerformComparison { [CmdletBinding()] @@ -295,10 +295,10 @@ function PerformComparison { $result = @{} $vdirBatches = GetVdirBatches $errFound = $false - $vdirBatches.Keys | ForEach-Object { + $vdirBatches.Keys | Sort-Object | ForEach-Object { $vdirs = $vdirBatches[$_] $jobs = @() - Write-Host "Processing $($vdirs.Count) directories in parallel" + Write-Host "Processing $($vdirs.Count) directories in parallel. Batch $($_ + 1) of $($vdirBatches.Count) batches." $vdirs | ForEach-Object { $j = Start-Job -ScriptBlock { param ($baselines, $pattern, $l, $known_bad, $KNOWN_ROOT_FILES, $mark_as_suspicious_from) @@ -313,22 +313,40 @@ function PerformComparison { $pdir = $pdir -replace "%SystemDrive%", $env:SystemDrive $pdir = $pdir -replace "%windir%", $env:windir + function GetFileHash([string] $filePath) { + $hash = "" + try { + $sha256 = New-Object -TypeName System.Security.Cryptography.SHA256CryptoServiceProvider + $hash = [System.BitConverter]::ToString($sha256.ComputeHash([System.IO.File]::ReadAllBytes($filePath))).Replace('-', '') + } catch { + return "" + } + + return $hash + } + + $datetime_format = "MM/dd/yyyy HH:mm:ss" + if ($pdir.StartsWith("$env:SystemDrive\inetpub\wwwroot")) { $inetpub_files = (Get-ChildItem -Recurse -Path $pdir -File -Exclude *aspx, *asmx, *asax, *js, *css, *htm, *html) foreach ($f in $inetpub_files) { - $hash = $f | Get-FileHash -ErrorAction SilentlyContinue + $hash = GetFileHash $f.FullName + + $creation_time = Get-Date ($f.CreationTimeUtc) -Format $datetime_format + $lastwrite_time = Get-Date ($f.LastWriteTimeUtc) -Format $datetime_format + $lastaccess_time = Get-Date ($f.LastAccessTimeUtc) -Format $datetime_format - if ($null -eq $hash) { + if ([string]::IsNullOrEmpty($hash)) { $newError = New-Object PSObject -Property @{ VDir = $vdir PDir = $pdir FileName = $f.Name FilePath = $f.FullName FileHash = "" - CreationTimeUtc = $f.CreationTimeUtc.ToString() - LastWriteTimeUtc = $f.LastWriteTimeUtc.ToString() - LastAccessTimeUtc = $f.LastAccessTimeUtc.ToString() + CreationTimeUtc = $creation_time + LastWriteTimeUtc = $lastwrite_time + LastAccessTimeUtc = $lastaccess_time Error = "ReadError" } $fErrors += $newError; @@ -340,10 +358,10 @@ function PerformComparison { PDir = $pdir FileName = $f.Name FilePath = $f.FullName - FileHash = $hash.Hash - CreationTimeUtc = $f.CreationTimeUtc.ToString() - LastWriteTimeUtc = $f.LastWriteTimeUtc.ToString() - LastAccessTimeUtc = $f.LastAccessTimeUtc.ToString() + FileHash = $hash + CreationTimeUtc = $creation_time + LastWriteTimeUtc = $lastwrite_time + LastAccessTimeUtc = $lastaccess_time Error = "Suspicious" } $fErrors += $newError; @@ -358,17 +376,22 @@ function PerformComparison { continue; } - $hash = $f | Get-FileHash -ErrorAction SilentlyContinue - if ($null -eq $hash) { + $creation_time = Get-Date ($f.CreationTimeUtc) -Format $datetime_format + $lastwrite_time = Get-Date ($f.LastWriteTimeUtc) -Format $datetime_format + $lastaccess_time = Get-Date ($f.LastAccessTimeUtc) -Format $datetime_format + + $hash = GetFileHash $f.FullName + + if ([string]::IsNullOrEmpty($hash)) { $newError = New-Object PSObject -Property @{ VDir = $vdir PDir = $pdir FileName = $f.Name FilePath = $f.FullName - FileHash = $hash.Hash - CreationTimeUtc = $f.CreationTimeUtc.ToString() - LastWriteTimeUtc = $f.LastWriteTimeUtc.ToString() - LastAccessTimeUtc = $f.LastAccessTimeUtc.ToString() + FileHash = "" + CreationTimeUtc = $creation_time + LastWriteTimeUtc = $lastwrite_time + LastAccessTimeUtc = $lastaccess_time Error = "ReadError" } $fErrors += $newError; @@ -382,10 +405,10 @@ function PerformComparison { PDir = $pdir FileName = $f.Name FilePath = $f.FullName - FileHash = $hash.Hash - CreationTimeUtc = $f.CreationTimeUtc.ToString() - LastWriteTimeUtc = $f.LastWriteTimeUtc.ToString() - LastAccessTimeUtc = $f.LastAccessTimeUtc.ToString() + FileHash = $hash + CreationTimeUtc = $creation_time + LastWriteTimeUtc = $lastwrite_time + LastAccessTimeUtc = $lastaccess_time Error = "Suspicious" } $fErrors += $newError; @@ -397,17 +420,17 @@ function PerformComparison { continue; } - if ($hash.Hash) { - if ($known_bad[$hash.Hash]) { + if ($hash) { + if ($known_bad[$hash]) { $newError = New-Object PSObject -Property @{ VDir = $vdir PDir = $pdir FileName = $f.Name FilePath = $f.FullName - FileHash = $hash.Hash - CreationTimeUtc = $f.CreationTimeUtc.ToString() - LastWriteTimeUtc = $f.LastWriteTimeUtc.ToString() - LastAccessTimeUtc = $f.LastAccessTimeUtc.ToString() + FileHash = $hash + CreationTimeUtc = $creation_time + LastWriteTimeUtc = $lastwrite_time + LastAccessTimeUtc = $lastaccess_time Error = "KnownBadHash" } $fErrors += $newError; @@ -420,7 +443,7 @@ function PerformComparison { continue; } - if ($baselines[$key] -and $null -ne $baselines[$key][$hash.Hash]) { + if ($baselines[$key] -and $null -ne $baselines[$key][$hash]) { $found = $true break; } @@ -432,10 +455,10 @@ function PerformComparison { PDir = $pdir FileName = $f.Name FilePath = $f.FullName - FileHash = $hash.Hash - CreationTimeUtc = $f.CreationTimeUtc.ToString() - LastWriteTimeUtc = $f.LastWriteTimeUtc.ToString() - LastAccessTimeUtc = $f.LastAccessTimeUtc.ToString() + FileHash = $hash + CreationTimeUtc = $creation_time + LastWriteTimeUtc = $lastwrite_time + LastAccessTimeUtc = $lastaccess_time Error = "NoHashMatch" } $fErrors += $newError; @@ -564,7 +587,7 @@ function FindInstalledVersions() { $vdir_paths += $vdir_physical_path } - $vdir_paths | Where-Object { Test-Path $_ } | ForEach-Object { Get-ChildItem -Directory -Path $_ } | Where-Object { $VALID_VERSIONS[$_.Name] -eq $true } | ForEach-Object { $versions[$_.Name] = $true } + $vdir_paths | Where-Object { Test-Path $_ } | ForEach-Object { Get-ChildItem -Directory -Path $_ -Recurse } | Where-Object { $VALID_VERSIONS[$_.Name] -eq $true } | ForEach-Object { $versions[$_.Name] = $true } return $exchange_version, $versions.Keys } @@ -574,6 +597,7 @@ function GetVdirBatches { $i = 0 $batchSize = 10 $logs = & (Join-Path $env:Windir "system32\inetsrv\appcmd.exe") LIST VDIRS + $logs | ForEach-Object { $bt = $i % $batchSize $grps[$bt] += @($_) @@ -623,28 +647,24 @@ function LoadBaseline($installed_versions) { function WriteScriptResult ($result, $exchVersion, $errFound) { $tmp_file = Join-Path (GetCurrDir) ($exchVersion + "_" + "result.csv") - $resData = @(); - $result.Keys | ForEach-Object { - $currentResult = $result[$_] - $file_hash = "" - if ($null -ne $fileError.FileHash) { - $file_hash = $fileError.FileHash - } - - foreach ($fileError in $currentResult.FileErrors) { - $resData += New-Object PsObject -Property @{ - 'FileName' = $fileError.FileName - 'VDir' = $fileError.VDir - 'Error' = [string]$fileError.Error - 'FilePath' = [string]$fileError.FilePath - 'FileHash' = [string]$file_hash - 'CreationTimeUtc' = [string]$fileError.CreationTimeUtc - 'LastWriteTimeUtc' = [string]$fileError.LastWriteTimeUtc - 'LastAccessTimeUtc' = [string]$fileError.LastAccessTimeUtc - 'PDir' = [string]$fileError.PDir + $resData = @( + $result.Keys | ForEach-Object { + $currentResult = $result[$_] + foreach ($fileError in $currentResult.FileErrors) { + New-Object PsObject -Property @{ + 'FileName' = $fileError.FileName + 'VDir' = $fileError.VDir + 'Error' = [string]$fileError.Error + 'FilePath' = [string]$fileError.FilePath + 'FileHash' = [string]$fileError.FileHash + 'CreationTimeUtc' = [string]$fileError.CreationTimeUtc + 'LastWriteTimeUtc' = [string]$fileError.LastWriteTimeUtc + 'LastAccessTimeUtc' = [string]$fileError.LastAccessTimeUtc + 'PDir' = [string]$fileError.PDir + } } } - } + ) Write-Host "Exporting ${resData.Count} objects to results" $resData | Select-Object | Export-Csv -Path $tmp_file -NoTypeInformation; From 47312b90a5f2230743e3fd6955fde8a1e0412c40 Mon Sep 17 00:00:00 2001 From: Bill Long Date: Wed, 10 Mar 2021 14:22:52 -0600 Subject: [PATCH 04/10] Simplify syntax for otherWellKnownObjects problem --- Setup/SetupAssist.ps1 | 160 ++++++++++++++++++------------------- Setup/SetupLogReviewer.ps1 | 2 +- 2 files changed, 81 insertions(+), 81 deletions(-) diff --git a/Setup/SetupAssist.ps1 b/Setup/SetupAssist.ps1 index 6e9c3878db..89e26b70e7 100644 --- a/Setup/SetupAssist.ps1 +++ b/Setup/SetupAssist.ps1 @@ -8,7 +8,7 @@ [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = 'Parameter is used')] [CmdletBinding()] param( - [string]$OtherWellKnownObjectsContainer + [switch]$OtherWellKnownObjects ) function IsAdministrator { @@ -196,6 +196,80 @@ function Get-ExchangeAdSetupObjects { return $hash } +Function Test-OtherWellKnownObjects { + [CmdletBinding()] + param () + + $rootDSE = [ADSI]("LDAP://RootDSE") + $exchangeContainerPath = ("CN=Microsoft Exchange,CN=Services," + $rootDSE.configurationNamingContext) + + ldifde -d $exchangeContainerPath -p Base -l otherWellKnownObjects -f ExchangeContainerOriginal.txt + + [array]$content = Get-Content .\ExchangeContainerOriginal.txt + + if ($null -eq $content -or + $content.Count -eq 0) { + throw "Failed to export ExchangeContainerOriginal.txt file" + } + + $owkoLine = "otherWellKnownObjects:" + $inOwkoLine = $false + $outputLines = New-Object 'System.Collections.Generic.List[string]' + $outputLines.Add($content[0]) + $outputLines.Add("changeType: modify") + $outputLines.Add("replace: otherWellKnownObjects") + + $index = 0 + while ($index -lt $content.Count) { + $line = $content[$index++] + + if ($line.Trim() -eq $owkoLine) { + + if ($null -ne $testStringLine -and + $null -ne $possibleAdd) { + + if (!($testStringLine -like "*CN=Deleted Objects*")) { + $outputLines.AddRange($possibleAdd) + } else { + Write-Host "Found object to remove: $testStringLine" + } + } + $inOwkoLine = $true + $possibleAdd = New-Object 'System.Collections.Generic.List[string]' + $possibleAdd.Add($line) + [string]$testStringLine = $line + continue + } + + if ($inOwkoLine) { + $possibleAdd.Add($line) + $testStringLine += $line + } + + if ($index -eq $content.Count) { + + if (!($testStringLine -like "*CN=Deleted Objects*")) { + $outputLines.AddRange($possibleAdd) + } else { + Write-Host "Found object to remove: $testStringLine" + } + } + } + + if ([string]::IsNullOrEmpty($outputLines[-1])) { + $outputLines[-1] = "-" + } else { + $outputLines.Add("-") + } + + $outputLines | Out-File -FilePath "ExchangeContainerImport.txt" + + Write-Host("`r`nVerify the results in ExchangeContainerImport.txt. Then run the following command:") + Write-Host("`r`n`tldifde -i -f ExchangeContainerImport.txt") + Write-Host("`r`nRun Setup.exe again afterwards.") + return +} + Function MainUse { $whoamiOutput = whoami /all @@ -277,85 +351,11 @@ Function MainUse { Function Main { - if (![string]::IsNullOrEmpty($OtherWellKnownObjectsContainer)) { - - ldifde -d $OtherWellKnownObjectsContainer -p Base -l otherWellKnownObjects -f ExchangeContainerOriginal.txt - - [array]$content = Get-Content .\ExchangeContainerOriginal.txt - - if ($null -eq $content -or - $content.Count -eq 0) { - throw "Failed to export ExchangeContainerOriginal.txt file" - } - - $owkoLine = "otherWellKnownObjects:" - $inOwkoLine = $false - $outputLines = New-Object 'System.Collections.Generic.List[string]' - $outputLines.Add($content[0]) - $outputLines.Add("changeType: modify") - $outputLines.Add("replace: otherWellKnownObjects") - - Function Test-DeleteObject ([string]$TestLine) { - - if ($TestLine.Contains("CN=Deleted Objects")) { - return $true - } - - return $false - } - - $index = 0 - while ($index -lt $content.Count) { - $line = $content[$index++] - - if ($line.Trim() -eq $owkoLine) { - - if ($null -ne $testStringLine -and - $null -ne $possibleAdd) { - - if (!(Test-DeleteObject $testStringLine)) { - $outputLines.AddRange($possibleAdd) - } else { - Write-Host "Found object to remove: $testStringLine" - } - } - $inOwkoLine = $true - $possibleAdd = New-Object 'System.Collections.Generic.List[string]' - $possibleAdd.Add($line) - [string]$testStringLine = $line - continue - } - - if ($inOwkoLine) { - $possibleAdd.Add($line) - $testStringLine += $line - } - - if ($index -eq $content.Count) { - - if (!(Test-DeleteObject $testStringLine)) { - $outputLines.AddRange($possibleAdd) - } else { - Write-Host "Found object to remove: $testStringLine" - } - } - } - - if ([string]::IsNullOrEmpty($outputLines[-1])) { - $outputLines[-1] = "-" - } else { - $outputLines.Add("-") - } - - $outputLines | Out-File -FilePath "ExchangeContainerImport.txt" - - Write-Host("`r`nVerify the results in ExchangeContainerImport.txt. Then run the following command:") - Write-Host("`tldifde -i -f ExchangeContainerImport.txt") - Write-Host("Run Setup.exe again afterwards.") - return + if ($OtherWellKnownObjects) { + Test-OtherWellKnownObjects + } else { + MainUse } - - MainUse } -Main \ No newline at end of file +Main diff --git a/Setup/SetupLogReviewer.ps1 b/Setup/SetupLogReviewer.ps1 index 984279b938..e3b588b97d 100644 --- a/Setup/SetupLogReviewer.ps1 +++ b/Setup/SetupLogReviewer.ps1 @@ -263,7 +263,7 @@ Function Test-KnownOrganizationPreparationErrors { Write-ErrorContext -WriteInfo $errorLine.Line [string]$ap = "Option 1: Restore the objects that were deleted." - [string]$ap += "`r`n`tOption 2: Run the SetupAssist.ps1 script with '-OtherWellKnownObjectsContainer `"$($errorLine.Matches.Groups[2].Value)`"' to be able address deleted objects type" + [string]$ap += "`r`n`tOption 2: Run the SetupAssist.ps1 script with '-OtherWellKnownObjects' to be able address deleted objects type" Write-ActionPlan $ap return $true } From 5e77d46da9addb325e694b8c2e65c68c0dd52bb0 Mon Sep 17 00:00:00 2001 From: Anshul Dube Date: Wed, 10 Mar 2021 13:08:23 -0800 Subject: [PATCH 05/10] fix --- Security/CompareExchangeHashes.ps1 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Security/CompareExchangeHashes.ps1 b/Security/CompareExchangeHashes.ps1 index c28f2509b9..ec8c79aae4 100644 --- a/Security/CompareExchangeHashes.ps1 +++ b/Security/CompareExchangeHashes.ps1 @@ -171,7 +171,13 @@ $VALID_VERSIONS = @{ ` '15.1.1847.3' = $true; ` '15.1.1779.2' = $true; ` '15.1.1713.5' = $true; ` + '15.1.1591.17' = $true; ` + '15.1.1591.16' = $true; ` '15.1.1591.10' = $true; ` + '15.1.1591.8' = $true; ` + '15.1.1531.7' = $true; ` + '15.1.1531.6' = $true; ` + '15.1.1531.4' = $true; ` '15.1.1531.3' = $true; ` '15.1.1466.8' = $true; ` '15.1.1466.3' = $true; ` @@ -211,11 +217,14 @@ $VALID_VERSIONS = @{ ` '15.0.1365.7' = $true; ` '15.0.1365.1' = $true; ` '15.0.1347.5' = $true; ` + '15.0.1347.4' = $true; ` '15.0.1347.3' = $true; ` '15.0.1347.2' = $true; ` + '15.0.1347.0' = $true; ` '15.0.1320.4' = $true; ` '15.0.1293.2' = $true; ` '15.0.1263.5' = $true; ` + '15.0.1236.6' = $true; ` '15.0.1236.3' = $true; ` '15.0.1210.3' = $true; ` '15.0.1178.4' = $true; ` @@ -225,11 +234,14 @@ $VALID_VERSIONS = @{ ` '15.0.1104.5' = $true; ` '15.0.1076.9' = $true; ` '15.0.1044.25' = $true; ` + '15.0.995.32' = $true; ` '15.0.995.29' = $true; ` + '15.0.995.28' = $true; ` '15.0.913.22' = $true; ` '15.0.847.32' = $true; ` '15.0.775.38' = $true; ` '15.0.712.24' = $true; ` + '15.0.712.23' = $true; ` '15.0.620.29' = $true; ` '15.0.516.32' = $true; ` '15.0.516.30' = $true; ` From ca9d58c14b79fda2bc7219690a91c60a41e74a75 Mon Sep 17 00:00:00 2001 From: Anshul Dube Date: Wed, 10 Mar 2021 13:11:52 -0800 Subject: [PATCH 06/10] fix --- Security/CompareExchangeHashes.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Security/CompareExchangeHashes.ps1 b/Security/CompareExchangeHashes.ps1 index ec8c79aae4..743d2d6083 100644 --- a/Security/CompareExchangeHashes.ps1 +++ b/Security/CompareExchangeHashes.ps1 @@ -455,7 +455,7 @@ function PerformComparison { continue; } - if ($baselines[$key] -and $null -ne $baselines[$key][$hash]) { + if ($baselines[$key] -and [string]::IsNullOrEmpty($baselines[$key][$hash]) -ne $true) { $found = $true break; } From 2fc3a7d753e5746e86c024b9ab330bf87d225215 Mon Sep 17 00:00:00 2001 From: Anshul Dube Date: Wed, 10 Mar 2021 13:39:50 -0800 Subject: [PATCH 07/10] fix --- Security/CompareExchangeHashes.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/Security/CompareExchangeHashes.ps1 b/Security/CompareExchangeHashes.ps1 index 743d2d6083..95d303597b 100644 --- a/Security/CompareExchangeHashes.ps1 +++ b/Security/CompareExchangeHashes.ps1 @@ -175,6 +175,7 @@ $VALID_VERSIONS = @{ ` '15.1.1591.16' = $true; ` '15.1.1591.10' = $true; ` '15.1.1591.8' = $true; ` + '15.1.1531.10' = $true; ` '15.1.1531.7' = $true; ` '15.1.1531.6' = $true; ` '15.1.1531.4' = $true; ` From 4ec395fd5e8bb2f2dac6c53a56ff962c7f1a7d53 Mon Sep 17 00:00:00 2001 From: Anshul Dube Date: Wed, 10 Mar 2021 14:29:47 -0800 Subject: [PATCH 08/10] fix --- Security/CompareExchangeHashes.ps1 | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Security/CompareExchangeHashes.ps1 b/Security/CompareExchangeHashes.ps1 index 95d303597b..7448bbd439 100644 --- a/Security/CompareExchangeHashes.ps1 +++ b/Security/CompareExchangeHashes.ps1 @@ -527,29 +527,23 @@ function Main() { } function LoadFromGitHub($url, $filename, $installed_versions) { - - $loaded = $false Write-Host "Downloading baseline file from GitHub to $filename" try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 # this file is only used for network connectivity test - Invoke-WebRequest -Uri "https://github.com/microsoft/CSS-Exchange/releases/latest/download/baseline_15.0.1044.25.checksum.txt" + Invoke-WebRequest -Uri "https://github.com/microsoft/CSS-Exchange/releases/latest/download/baseline_15.0.1044.25.checksum.txt" | Out-Null } catch { Write-Error "Cannot reach out to https://github.com/microsoft/CSS-Exchange/releases/latest, please download baseline files for $installed_versions from https://github.com/microsoft/CSS-Exchange/releases/latest manually to $(GetCurrDir), then rerun this script from $(GetCurrDir)." } try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - Invoke-WebRequest -Uri $url -OutFile $filename - $loaded = $true + Invoke-WebRequest -Uri $url -OutFile $filename | Out-Null } catch { - $loaded = $false Write-Error "$filename not found... please open issue on https://github.com/microsoft/CSS-Exchange/issues, we will work on it" } - - return $loaded } function PreProcessBaseline($baselines) { From a714be51c1b088f6a4085c44bab4b4a2fca5c0f7 Mon Sep 17 00:00:00 2001 From: Anshul Dube Date: Wed, 10 Mar 2021 14:42:22 -0800 Subject: [PATCH 09/10] fix --- Security/CompareExchangeHashes.ps1 | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Security/CompareExchangeHashes.ps1 b/Security/CompareExchangeHashes.ps1 index 7448bbd439..d71781746e 100644 --- a/Security/CompareExchangeHashes.ps1 +++ b/Security/CompareExchangeHashes.ps1 @@ -285,7 +285,7 @@ $VALID_VERSIONS = @{ ` } -$MARK_AS_SUSPICIOUS_FROM = (Get-Date -Date "12/01/2020" -Format "MM/dd/yyyy HH:mm:ss") +$MARK_AS_SUSPICIOUS_FROM = (Get-Date -Date "12/01/2020") function PerformComparison { [CmdletBinding()] @@ -346,9 +346,9 @@ function PerformComparison { $hash = GetFileHash $f.FullName - $creation_time = Get-Date ($f.CreationTimeUtc) -Format $datetime_format - $lastwrite_time = Get-Date ($f.LastWriteTimeUtc) -Format $datetime_format - $lastaccess_time = Get-Date ($f.LastAccessTimeUtc) -Format $datetime_format + $creation_time = Get-Date ($f.CreationTimeUtc) + $lastwrite_time = Get-Date ($f.LastWriteTimeUtc) + $lastaccess_time = Get-Date ($f.LastAccessTimeUtc) if ([string]::IsNullOrEmpty($hash)) { $newError = New-Object PSObject -Property @{ @@ -365,7 +365,7 @@ function PerformComparison { $fErrors += $newError; $errHappend = $true } else { - if ($mark_as_suspicious_from -le $f.LastWriteTime) { + if ($mark_as_suspicious_from -le $lastwrite_time) { $newError = New-Object PSObject -Property @{ VDir = $vdir PDir = $pdir @@ -389,9 +389,9 @@ function PerformComparison { continue; } - $creation_time = Get-Date ($f.CreationTimeUtc) -Format $datetime_format - $lastwrite_time = Get-Date ($f.LastWriteTimeUtc) -Format $datetime_format - $lastaccess_time = Get-Date ($f.LastAccessTimeUtc) -Format $datetime_format + $creation_time = Get-Date ($f.CreationTimeUtc) + $lastwrite_time = Get-Date ($f.LastWriteTimeUtc) + $lastaccess_time = Get-Date ($f.LastAccessTimeUtc) $hash = GetFileHash $f.FullName @@ -412,7 +412,7 @@ function PerformComparison { } if ($pdir.StartsWith("$env:SystemDrive\inetpub\wwwroot")) { - if ($mark_as_suspicious_from -le $f.LastWriteTime) { + if ($mark_as_suspicious_from -le $lastwrite_time) { $newError = New-Object PSObject -Property @{ VDir = $vdir PDir = $pdir From 398253305757aad00bb64c6903c8b09c1a331017 Mon Sep 17 00:00:00 2001 From: Anshul Dube Date: Wed, 10 Mar 2021 14:50:15 -0800 Subject: [PATCH 10/10] fix datetimes --- Security/CompareExchangeHashes.ps1 | 52 ++++++++++++------------------ 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/Security/CompareExchangeHashes.ps1 b/Security/CompareExchangeHashes.ps1 index d71781746e..ae0f24c517 100644 --- a/Security/CompareExchangeHashes.ps1 +++ b/Security/CompareExchangeHashes.ps1 @@ -285,7 +285,7 @@ $VALID_VERSIONS = @{ ` } -$MARK_AS_SUSPICIOUS_FROM = (Get-Date -Date "12/01/2020") +$MARK_AS_SUSPICIOUS_FROM = (Get-Date -Year "2020" -Month "12" -Day "01") function PerformComparison { [CmdletBinding()] @@ -345,11 +345,6 @@ function PerformComparison { foreach ($f in $inetpub_files) { $hash = GetFileHash $f.FullName - - $creation_time = Get-Date ($f.CreationTimeUtc) - $lastwrite_time = Get-Date ($f.LastWriteTimeUtc) - $lastaccess_time = Get-Date ($f.LastAccessTimeUtc) - if ([string]::IsNullOrEmpty($hash)) { $newError = New-Object PSObject -Property @{ VDir = $vdir @@ -357,24 +352,24 @@ function PerformComparison { FileName = $f.Name FilePath = $f.FullName FileHash = "" - CreationTimeUtc = $creation_time - LastWriteTimeUtc = $lastwrite_time - LastAccessTimeUtc = $lastaccess_time + CreationTimeUtc = $f.CreationTimeUtc + LastWriteTimeUtc = $f.LastWriteTimeUtc + LastAccessTimeUtc = $f.LastAccessTimeUtc Error = "ReadError" } $fErrors += $newError; $errHappend = $true } else { - if ($mark_as_suspicious_from -le $lastwrite_time) { + if ($mark_as_suspicious_from -le $f.LastWriteTimeUtc) { $newError = New-Object PSObject -Property @{ VDir = $vdir PDir = $pdir FileName = $f.Name FilePath = $f.FullName FileHash = $hash - CreationTimeUtc = $creation_time - LastWriteTimeUtc = $lastwrite_time - LastAccessTimeUtc = $lastaccess_time + CreationTimeUtc = $f.CreationTimeUtc + LastWriteTimeUtc = $f.LastWriteTimeUtc + LastAccessTimeUtc = $f.LastAccessTimeUtc Error = "Suspicious" } $fErrors += $newError; @@ -389,12 +384,7 @@ function PerformComparison { continue; } - $creation_time = Get-Date ($f.CreationTimeUtc) - $lastwrite_time = Get-Date ($f.LastWriteTimeUtc) - $lastaccess_time = Get-Date ($f.LastAccessTimeUtc) - $hash = GetFileHash $f.FullName - if ([string]::IsNullOrEmpty($hash)) { $newError = New-Object PSObject -Property @{ VDir = $vdir @@ -402,9 +392,9 @@ function PerformComparison { FileName = $f.Name FilePath = $f.FullName FileHash = "" - CreationTimeUtc = $creation_time - LastWriteTimeUtc = $lastwrite_time - LastAccessTimeUtc = $lastaccess_time + CreationTimeUtc = $f.CreationTimeUtc + LastWriteTimeUtc = $f.LastWriteTimeUtc + LastAccessTimeUtc = $f.LastAccessTimeUtc Error = "ReadError" } $fErrors += $newError; @@ -412,16 +402,16 @@ function PerformComparison { } if ($pdir.StartsWith("$env:SystemDrive\inetpub\wwwroot")) { - if ($mark_as_suspicious_from -le $lastwrite_time) { + if ($mark_as_suspicious_from -le $f.LastWriteTimeUtc) { $newError = New-Object PSObject -Property @{ VDir = $vdir PDir = $pdir FileName = $f.Name FilePath = $f.FullName FileHash = $hash - CreationTimeUtc = $creation_time - LastWriteTimeUtc = $lastwrite_time - LastAccessTimeUtc = $lastaccess_time + CreationTimeUtc = $f.CreationTimeUtc + LastWriteTimeUtc = $f.LastWriteTimeUtc + LastAccessTimeUtc = $f.LastAccessTimeUtc Error = "Suspicious" } $fErrors += $newError; @@ -441,9 +431,9 @@ function PerformComparison { FileName = $f.Name FilePath = $f.FullName FileHash = $hash - CreationTimeUtc = $creation_time - LastWriteTimeUtc = $lastwrite_time - LastAccessTimeUtc = $lastaccess_time + CreationTimeUtc = $f.CreationTimeUtc + LastWriteTimeUtc = $f.LastWriteTimeUtc + LastAccessTimeUtc = $f.LastAccessTimeUtc Error = "KnownBadHash" } $fErrors += $newError; @@ -469,9 +459,9 @@ function PerformComparison { FileName = $f.Name FilePath = $f.FullName FileHash = $hash - CreationTimeUtc = $creation_time - LastWriteTimeUtc = $lastwrite_time - LastAccessTimeUtc = $lastaccess_time + CreationTimeUtc = $f.CreationTimeUtc + LastWriteTimeUtc = $f.LastWriteTimeUtc + LastAccessTimeUtc = $f.LastAccessTimeUtc Error = "NoHashMatch" } $fErrors += $newError;