From 9bb41acbdaef736acea4c10cd352c9a63047d9da Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 21 May 2021 10:04:40 -0400 Subject: [PATCH 1/8] Initial import of admin script --- Admin/Get-SimpleAuditLogReport.ps1 | 227 +++++++++++++++++++++++++++++ Admin/README.md | 186 +++++++++++++++++++++++ 2 files changed, 413 insertions(+) create mode 100644 Admin/Get-SimpleAuditLogReport.ps1 create mode 100644 Admin/README.md diff --git a/Admin/Get-SimpleAuditLogReport.ps1 b/Admin/Get-SimpleAuditLogReport.ps1 new file mode 100644 index 0000000000..711063d74c --- /dev/null +++ b/Admin/Get-SimpleAuditLogReport.ps1 @@ -0,0 +1,227 @@ +################################################################################# +# +# The sample scripts are not supported under any Microsoft standard support +# program or service. The sample scripts are provided AS IS without warranty +# of any kind. Microsoft further disclaims all implied warranties including, without +# limitation, any implied warranties of merchantability or of fitness for a particular +# purpose. The entire risk arising out of the use or performance of the sample scripts +# and documentation remains with you. In no event shall Microsoft, its authors, or +# anyone else involved in the creation, production, or delivery of the scripts be liable +# for any damages whatsoever (including, without limitation, damages for loss of business +# profits, business interruption, loss of business information, or other pecuniary loss) +# arising out of the use of or inability to use the sample scripts or documentation, +# even if Microsoft has been advised of the possibility of such damages. +# +################################################################################# +# +# Author matbyrd@microsoft.com +# +################################################################################# + +<# + +.SYNOPSIS +Parses the output of Search-AdminAuditlog to produce more readable results. + +.DESCRIPTION +Takes the output of the Search-AdminAuditlog as an input and reconstructs the +results into a more easily read structure. + +Results can be stored in a variable and sent to the script with -searchresults +or taken directly off of a pipeline and converted. + +Output should generally contain commands that can be copied and pasted into +an Exchange/Exchange Online Shell and run directly with little to no +Modification. + +.PARAMETER SeachResults +Output of the Search-AdminAuditLog. Either stored in a variable or pipelined +into the script. + +.PARAMETER ResolveCaller +Attempts to resolve the alias of the person who ran the command into the +primary SMTP address. + +.PARAMETER Agree +Verifies you have read and agree to the disclaimer at the top of the script file + +.OUTPUTS +Creates an output that contains the following information: + +Caller : Person who ran the command +Cmdlet : Cmdlet that was run +FullCommand : Reconstructed full command that was run +RunDate : Date and Time command was run +ObjectModified : Object that was modified by the command + +.EXAMPLE +$Search = Search-AdminAuditLog +$search | C:\Scripts\Get-SimpleAuditLogReport.ps1 -agree + +Converts the results of Search-AdminAuditLog and sends the output to the screen + +.EXAMPLE +Search-AdminAuditLog | C:\Scripts\Get-SimpleAuditlogReport.ps1 -agree | Export-CSV -path C:\temp\auditlog.csv + +Converts the restuls of Search-AdminAuditLog and sends the output to a CSV file + +.EXAMPLE +$MySearch = Search-AdminAuditLog -cmdlet set-mailbox +C:\Script\C:\Scripts\Get-SimpleAuditLogReport.ps1 -agree -searchresults $MySearch + +Finds all instances of set-mailbox +Converts them by passing in the results to the switch searchresults +Outputs to the screen + +#> + +Param ( + [Parameter( + Position = 0, + Mandatory = $true, + ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $true) + ] + $SearchResults, + [switch]$ResolveCaller, + [switch]$Agree +) + +# Setup to process incomming results +Begin { + + # Statement to ensure that you have looked at the disclaimer or that you have removed this line so you don't have too + if ($Agree -ne $true) { Write-Error "Please run the script with -Agree to indicate that you have read and agreed to the sample script disclaimer at the top of the script file" -ErrorAction Stop } + + # Make sure the array is null + [array]$ResultSet = $null + + # Set the counter to 1 + $i = 1 + + # If resolveCaller is called it can take much longer to run so notify the user of that + if ($ResolveCaller) { Write-Warning "ResolveCaller specified; Script will take significantly longer to run as it attemps to resolve the primary SMTP address of each calling user. Progress updates will be provided every 25 entries." } +} + +# Process thru what ever is comming into the script +Process { + + # Deal with each object in the input + $searchresults | ForEach-Object { + + # Reset the result object + $Result = New-Object PSObject + + # Get the alias of the User that ran the command + $user = ($_.caller.split("/"))[-1] + + # If we used resolve caller then try to resolve the primary SMTP address of the calling user + if ($ResolveCaller) { + + # attempt to resolve the recipient + [string]$Recipient = (get-recipient $user -ErrorAction silentlycontinue).primarysmtpaddress + + # if we get a result then put that in the output otherwise do nothing + If (!([string]::IsNullOrEmpty($Recipient))) { $user = $Recipient } + + # Since this is going to take longer to run provide status every 25 entries + if ($i % 25 -eq 0) { Write-Host "Processed 25 Results" } + $i++ + } + + # Build the command that was run + $switches = $_.cmdletparameters + [string]$FullCommand = $_.cmdletname + + # Get all of the switchs and add them in "human" form to the output + foreach ($parameter in $switches) { + + # Format our values depending on what they are so that they are as close + # a match as possible for what would have been entered + switch -regex ($parameter.value) { + + # If we have a multi value array put in then we need to break it out and add quotes as needed + '[;]' { + + # Reset the formatted value string + $FormattedValue = $null + + # Split it into an array + $valuearray = $switch.current.split(";") + + # For each entry in the array add quotes if needed and add it to the formatted value string + $valuearray | ForEach-Object { + if ($_ -match "[ \t]") { $FormattedValue = $FormattedValue + "`"" + $_ + "`";" } + else { $FormattedValue = $FormattedValue + $_ + ";" } + } + + # Clean up the trailing ; + $FormattedValue = $FormattedValue.trimend(";") + + # Add our switch + cleaned up value to the command string + $FullCommand = $FullCommand + " -" + $parameter.name + " " + $FormattedValue + } + + # If we have a value with spaces add quotes + '[ \t]' { $FullCommand = $FullCommand + " -" + $parameter.name + " `"" + $switch.current + "`"" } + + # If we have a true or false format them with :$ in front ( -allow:$true ) + '^True$|^False$' { $FullCommand = $FullCommand + " -" + $parameter.name + ":`$" + $switch.current } + + # Otherwise just put the switch and the value + default { $FullCommand = $FullCommand + " -" + $parameter.name + " " + $switch.current } + + } + } + + # Pull out the Modified properties + $ModifiedProperties = $_.modifiedproperties + + # Make sure our holding variable are nulled out + $Property = $null + $Oldvalue = $null + $NewValue = $null + + # Push each property set into a seperate string + $ModifiedProperties | ForEach-Object { + [string]$Property = $Property + $_.name + ";" + [string]$OldValue = $OldValue + $_.oldvalue + ";" + [string]$NewValue = $NewValue + $_.newvalue + ";" + } + + # Trim off the last ; + $Property = $Property.TrimEnd(";") + $Oldvalue = $Oldvalue.TrimEnd(";") + $NewValue = $NewValue.TrimEnd(";") + + # Format our modified object + if ([string]::IsNullOrEmpty($_.objectModified)) { $ObjModified = "" } + else { + $ObjModified = ($_.objectmodified.split("/"))[-1] + $ObjModified = ($ObjModified.split("\"))[-1] + } + + # Get just the name of the cmdlet that was run + [string]$cmdlet = $_.CmdletName + + # Build the result object to return our values + $Result | Add-Member -MemberType NoteProperty -Value $user -Name Caller + $Result | Add-Member -MemberType NoteProperty -Value $cmdlet -Name Cmdlet + $Result | Add-Member -MemberType NoteProperty -Value $FullCommand -Name FullCommand + $Result | Add-Member -MemberType NoteProperty -Value $_.rundate -Name RunDate + $Result | Add-Member -MemberType NoteProperty -Value $ObjModified -Name ObjectModified + $Result | Add-Member -MemberType NoteProperty -Value $Property -Name ModifiedProperties + $Result | Add-Member -MemberType NoteProperty -Value $Oldvalue -Name OldValue + $Result | Add-Member -MemberType NoteProperty -Value $NewValue -Name NewValue + + # Add the object to the array to be returned + $ResultSet = $ResultSet + $Result + + } +} + +# Final steps +End { + # Return the array set + Return $ResultSet +} \ No newline at end of file diff --git a/Admin/README.md b/Admin/README.md new file mode 100644 index 0000000000..830af59dae --- /dev/null +++ b/Admin/README.md @@ -0,0 +1,186 @@ +Script|More Info|Download +-|-|- +BackendCookieMitigation.ps1 | [More Info](https://github.com/microsoft/CSS-Exchange/tree/main/Security#backendcookiemitigationps1) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/BackendCookieMitigation.ps1) +CompareExchangeHashes.ps1 | [More Info](https://github.com/microsoft/CSS-Exchange/tree/main/Security#compareexchangehashesps1) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/CompareExchangeHashes.ps1) +ExchangeMitigations.ps1 | [More Info](https://github.com/microsoft/CSS-Exchange/tree/main/Security#exchangemitigationsps1) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/ExchangeMitigations.ps1) +http-vuln-cve2021-26855.nse | [More Info](https://github.com/microsoft/CSS-Exchange/tree/main/Security#http-vuln-cve2021-26855nse) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/http-vuln-cve2021-26855.nse) +Test-ProxyLogon.ps1 | [More Info](https://github.com/microsoft/CSS-Exchange/tree/main/Security#test-proxylogonps1) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/Test-ProxyLogon.ps1) + +# Security scripts +## [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. + +Download the latest release here: + +[Download Test-ProxyLogon.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/Test-ProxyLogon.ps1) + +### Usage + +The most typical usage of this script is to check all Exchange servers and save the reports, +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 check the local server and copy the identified logs and files to the OutPath: + +`.\Test-ProxyLogon.ps1 -OutPath $home\desktop\logs -CollectFiles` + +To display the results without saving them, pass -DisplayOnly: + +`.\Test-ProxyLogon.ps1 -DisplayOnly` + +### Frequently Asked Questions + +**The script says it found suspicious files, and it lists a bunch of zip files. What does this mean?** + +The script will flag any zip/7x/rar files that it finds in ProgramData. As noted in +[this blog post](https://www.microsoft.com/security/blog/2021/03/02/hafnium-targeting-exchange-servers/), web +shells have been observed using such files for exfiltration. An administrator should review the files to +determine if they are valid. Determining if a zip file is a valid part of an installed +product is outside the scope of this script, and whitelisting files by name would only encourage +the use of those specific names by attackers. + +**I'm having trouble running the script on Exchange 2010.** + +If PowerShell 3 is present, the script can be run on Exchange 2010. It will not run on PowerShell 2. One can +also enable PS Remoting and run the script remotely against Exchange 2010. However, +the script has minimal functionality in these scenarios, as Exchange 2010 is only affected by one of the +four announced exploits - CVE-2021-26857. Further, this exploit is only available if the Unified Messaging role +is present. As a result, it is often easier to simply run the Get-EventLog command from the +[blog post](https://www.microsoft.com/security/blog/2021/03/02/hafnium-targeting-exchange-servers/), +rather than using Test-ProxyLogon. + +## [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 +* CVE-2021-26857 +* CVE-2021-27065 +* CVE-2021-26858 + +For more information on each mitigation please visit https://aka.ms/exchangevulns + +**This should only be used as a temporary mitigation until your Exchange Servers can be fully patched, recommended guidance is to apply all of the mitigations at once.** + +For this script to work you must have the IIS URL Rewrite Module installed which can be done via this script using the -FullPathToMSI parameter. + +For IIS 10 and higher URL Rewrite Module 2.1 must be installed, you can download version 2.1 here: + +* x86 & x64 -https://www.iis.net/downloads/microsoft/url-rewrite + +For IIS 8.5 and lower Rewrite Module 2.0 must be installed, you can download version 2.0 here: + +* x86 - https://www.microsoft.com/en-us/download/details.aspx?id=5747 + +* x64 - https://www.microsoft.com/en-us/download/details.aspx?id=7435 + +Installing URL Rewrite version 2.1 on IIS versions 8.5 and lower may cause IIS and Exchange to become unstable. If there is a mismatch between the URL Rewrite module and IIS version, ExchangeMitigations.ps1 will not apply the mitigation for CVE-2021-26855. You must uninstall the URL Rewrite module and reinstall the correct version. + +Script requires PowerShell 3.0 and later and must be executed from an elevated PowerShell Session. + +Download the latest release here: + +[Download ExchangeMitigations.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/ExchangeMitigations.ps1) + +To apply all mitigations with MSI install + +`.\ExchangeMitigations.ps1 -FullPathToMSI "FullPathToMSI" -WebSiteNames "Default Web Site" -ApplyAllMitigations` + +To apply all mitigations without MSI install + +`.\ExchangeMitigations.ps1 -WebSiteNames "Default Web Site" -ApplyAllMitigations -Verbose` + +To rollback all mitigations + +`.\ExchangeMitigations.ps1 -WebSiteNames "Default Web Site" -RollbackAllMitigation` + +To apply multiple or specific mitigations (out of the 4) + +`.\ExchangeMitigations.ps1 -WebSiteNames "Default Web Site" -ApplyECPAppPoolMitigation -ApplyOABAppPoolMitigation` + +To rollback multiple or specific mitigations + +`.\ExchangeMitigations.ps1 -WebSiteNames "Default Web Site" -RollbackECPAppPoolMitigation -RollbackOABAppPoolMitigation` + +## [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). + +[Download CompareExchangeHashes.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/CompareExchangeHashes.ps1) + +`.\CompareExchangeHashes.ps1` + +This script takes the following actions: +* Checks file hashes in exchange vdirs against known good baseline of hashes. +* Any file under IIS root which is edited after Dec 1st 2020 is marked as suspicious. + +**This script needs to be run as administrator on all the exchange servers separately**. + +The script determines the version of exchange installed on the server and then downloads the hashes for known exchange files from the [published known good hashes of exchange files](https://github.com/microsoft/CSS-Exchange/releases/latest). + +The result generated is stored in a file locally with the following format: _result.csv +If potential malicious files are found during comparision there is an error generated on the cmdline. +* Note: If the result file contains huge number of rows, it is potentially due to missing baseline hashes, please find the exchange versions found on the machine and leave a comment on issue [313](https://github.com/microsoft/CSS-Exchange/issues/313) + +To read the output, open the result csv file in excel or in powershell: + +`$result = Import-Csv ` + +Note: If the server does not have internet connectivity, run the script which would output the exchange versions discovered on the server. The baselines can be downloaded from [published known good hashes of exchange files](https://github.com/microsoft/CSS-Exchange/releases/latest) and re-run the script. + +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](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. + +This will help with defense against the known patterns observed but not the SSRF as a whole. For more information please visit https://aka.ms/exchangevulns. + +**For this script to work you must have the IIS URL Rewrite Module installed which can be done via this script using the -FullPathToMSI parameter.** + +For IIS 10 and higher URL Rewrite Module 2.1 must be installed, you can download version 2.1 here: + +* x86 & x64 -https://www.iis.net/downloads/microsoft/url-rewrite + +For IIS 8.5 and lower Rewrite Module 2.0 must be installed, you can download version 2.0 here: + +* x86 - https://www.microsoft.com/en-us/download/details.aspx?id=5747 + +* x64 - https://www.microsoft.com/en-us/download/details.aspx?id=7435 + +Installing URL Rewrite version 2.1 on IIS versions 8.5 and lower may cause IIS and Exchange to become unstable. If there is a mismatch between the URL Rewrite module and IIS version, BackendCookieMitigation.ps1 will not apply the mitigation for CVE-2021-26855. You must uninstall the URL Rewrite module and reinstall the correct version. + +**Script requires PowerShell 3.0 and later and must be executed from an elevated PowerShell Session.** + +Download the latest release here: + +[Download BackendCookieMitigation.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/BackendCookieMitigation.ps1) + +To apply with MSI install of the URL Rewrite module - Note: version may vary depending on system info + +`PS C:\> BackendCookieMitigation.ps1 -FullPathToMSI "C:\temp\rewrite_amd64_en-US.msi" -WebSiteNames "Default Web Site" -Verbose ` + +To apply without MSI install + +`PS C:\> BackendCookieMitigation.ps1 -WebSiteNames "Default Web Site" -Verbose` + +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](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. + +Download the latest release here: + +[Download http-vuln-cve2021-26855.nse](https://github.com/microsoft/CSS-Exchange/releases/latest/download/http-vuln-cve2021-26855.nse) From da35e607e15389085b94a080cf3337f471c1556f Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 21 May 2021 10:26:10 -0400 Subject: [PATCH 2/8] Basic formatting updates --- Admin/Get-SimpleAuditLogReport.ps1 | 95 ++++++++++++++---------------- 1 file changed, 44 insertions(+), 51 deletions(-) diff --git a/Admin/Get-SimpleAuditLogReport.ps1 b/Admin/Get-SimpleAuditLogReport.ps1 index 711063d74c..266ac156e2 100644 --- a/Admin/Get-SimpleAuditLogReport.ps1 +++ b/Admin/Get-SimpleAuditLogReport.ps1 @@ -76,12 +76,7 @@ Outputs to the screen #> Param ( - [Parameter( - Position = 0, - Mandatory = $true, - ValueFromPipeline = $true, - ValueFromPipelineByPropertyName = $true) - ] + [Parameter(Position = 0, Mandatory = $true, ValueFromPipelinepeline = $true, ValueFromPipelineByPropertyName = $true)] $SearchResults, [switch]$ResolveCaller, [switch]$Agree @@ -91,7 +86,7 @@ Param ( Begin { # Statement to ensure that you have looked at the disclaimer or that you have removed this line so you don't have too - if ($Agree -ne $true) { Write-Error "Please run the script with -Agree to indicate that you have read and agreed to the sample script disclaimer at the top of the script file" -ErrorAction Stop } + If ($Agree -ne $true) { Write-Error "Please run the script with -Agree to indicate that you have read and agreed to the sample script disclaimer at the top of the script file" -ErrorAction Stop } # Make sure the array is null [array]$ResultSet = $null @@ -141,7 +136,7 @@ Process { switch -regex ($parameter.value) { # If we have a multi value array put in then we need to break it out and add quotes as needed - '[;]' { + '[;]' { # Reset the formatted value string $FormattedValue = $null @@ -166,60 +161,58 @@ Process { '[ \t]' { $FullCommand = $FullCommand + " -" + $parameter.name + " `"" + $switch.current + "`"" } # If we have a true or false format them with :$ in front ( -allow:$true ) - '^True$|^False$' { $FullCommand = $FullCommand + " -" + $parameter.name + ":`$" + $switch.current } + '^True$|^False$' { $FullCommand = $FullCommand + " -" + $parameter.name + ":`$" + $switch.current } # Otherwise just put the switch and the value default { $FullCommand = $FullCommand + " -" + $parameter.name + " " + $switch.current } - } } + } - # Pull out the Modified properties - $ModifiedProperties = $_.modifiedproperties - - # Make sure our holding variable are nulled out - $Property = $null - $Oldvalue = $null - $NewValue = $null - - # Push each property set into a seperate string - $ModifiedProperties | ForEach-Object { - [string]$Property = $Property + $_.name + ";" - [string]$OldValue = $OldValue + $_.oldvalue + ";" - [string]$NewValue = $NewValue + $_.newvalue + ";" - } - - # Trim off the last ; - $Property = $Property.TrimEnd(";") - $Oldvalue = $Oldvalue.TrimEnd(";") - $NewValue = $NewValue.TrimEnd(";") - - # Format our modified object - if ([string]::IsNullOrEmpty($_.objectModified)) { $ObjModified = "" } - else { - $ObjModified = ($_.objectmodified.split("/"))[-1] - $ObjModified = ($ObjModified.split("\"))[-1] - } - - # Get just the name of the cmdlet that was run - [string]$cmdlet = $_.CmdletName + # Pull out the Modified properties + $ModifiedProperties = $_.modifiedproperties - # Build the result object to return our values - $Result | Add-Member -MemberType NoteProperty -Value $user -Name Caller - $Result | Add-Member -MemberType NoteProperty -Value $cmdlet -Name Cmdlet - $Result | Add-Member -MemberType NoteProperty -Value $FullCommand -Name FullCommand - $Result | Add-Member -MemberType NoteProperty -Value $_.rundate -Name RunDate - $Result | Add-Member -MemberType NoteProperty -Value $ObjModified -Name ObjectModified - $Result | Add-Member -MemberType NoteProperty -Value $Property -Name ModifiedProperties - $Result | Add-Member -MemberType NoteProperty -Value $Oldvalue -Name OldValue - $Result | Add-Member -MemberType NoteProperty -Value $NewValue -Name NewValue + # Make sure our holding variable are nulled out + $Property = $null + $Oldvalue = $null + $NewValue = $null - # Add the object to the array to be returned - $ResultSet = $ResultSet + $Result + # Push each property set into a seperate string + $ModifiedProperties | ForEach-Object { + [string]$Property = $Property + $_.name + ";" + [string]$OldValue = $OldValue + $_.oldvalue + ";" + [string]$NewValue = $NewValue + $_.newvalue + ";" + } + # Trim off the last ; + $Property = $Property.TrimEnd(";") + $Oldvalue = $Oldvalue.TrimEnd(";") + $NewValue = $NewValue.TrimEnd(";") + + # Format our modified object + if ([string]::IsNullOrEmpty($_.objectModified)) { + $ObjModified = "" + } else { + $ObjModified = ($_.objectmodified.split("/"))[-1] + $ObjModified = ($ObjModified.split("\"))[-1] } -} + # Get just the name of the cmdlet that was run + [string]$cmdlet = $_.CmdletName + + # Build the result object to return our values + $Result | Add-Member -MemberType NoteProperty -Value $user -Name Caller + $Result | Add-Member -MemberType NoteProperty -Value $cmdlet -Name Cmdlet + $ResultResult | Add-Member -MemberType NoteProperty -Value $FullCommand -Name FullCommand + $Result | Add-Member -MemberType NoteProperty -Value $_.rundate -Name RunDate + $Result | Add-Member -MemberType NoteProperty -Value $ObjModified -Name ObjectModified + $Result | Add-Member -MemberType NoteProperty -Value $Property -Name ModifiedProperties + $Result | Add-Member -MemberType NoteProperty -Value $Oldvalue -Name OldValue + $Result | Add-Member -MemberType NoteProperty -Value $NewValue -Name NewValue + + # Add the object to the array to be returned + $ResultSet = $ResultSet + $Result +} # Final steps End { # Return the array set From 8dc3ca18060e4e61d0932f5d66cea973bb34a746 Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 21 May 2021 10:35:48 -0400 Subject: [PATCH 3/8] Initial Readme --- .vscode/settings.json | 5 +- Admin/README.md | 192 +++--------------------------------------- 2 files changed, 14 insertions(+), 183 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index b42ca74c15..4e33d5627d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,5 +20,8 @@ "files.encoding": "utf8", "[powershell]": { "files.encoding": "utf8bom" - } + }, + "cSpell.words": [ + "cmdlet" + ] } \ No newline at end of file diff --git a/Admin/README.md b/Admin/README.md index 830af59dae..ae62cf0ccf 100644 --- a/Admin/README.md +++ b/Admin/README.md @@ -1,186 +1,14 @@ -Script|More Info|Download --|-|- -BackendCookieMitigation.ps1 | [More Info](https://github.com/microsoft/CSS-Exchange/tree/main/Security#backendcookiemitigationps1) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/BackendCookieMitigation.ps1) -CompareExchangeHashes.ps1 | [More Info](https://github.com/microsoft/CSS-Exchange/tree/main/Security#compareexchangehashesps1) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/CompareExchangeHashes.ps1) -ExchangeMitigations.ps1 | [More Info](https://github.com/microsoft/CSS-Exchange/tree/main/Security#exchangemitigationsps1) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/ExchangeMitigations.ps1) -http-vuln-cve2021-26855.nse | [More Info](https://github.com/microsoft/CSS-Exchange/tree/main/Security#http-vuln-cve2021-26855nse) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/http-vuln-cve2021-26855.nse) -Test-ProxyLogon.ps1 | [More Info](https://github.com/microsoft/CSS-Exchange/tree/main/Security#test-proxylogonps1) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/Test-ProxyLogon.ps1) +# About Get-SimpleAdminAuditLog +Exchange admin audit logs are not readily human readable. All of the data needed to understand what Cmdlet has been run is in the data but it is not very easy to read. Get-SimpleAdminAuditLog will take the results of an audit log search and provide a significantly more human readable version of the data. -# Security scripts -## [Test-ProxyLogon.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/Test-ProxyLogon.ps1) +It will parse the audit log and attempt to reconstruct the actual Cmdlet that was run. -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. +# Common Usage +`$Search = Search-AdminAuditLog` -Download the latest release here: +`$search | C:\Scripts\Get-SimpleAuditLogReport.ps1 -agree` -[Download Test-ProxyLogon.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/Test-ProxyLogon.ps1) - -### Usage - -The most typical usage of this script is to check all Exchange servers and save the reports, -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 check the local server and copy the identified logs and files to the OutPath: - -`.\Test-ProxyLogon.ps1 -OutPath $home\desktop\logs -CollectFiles` - -To display the results without saving them, pass -DisplayOnly: - -`.\Test-ProxyLogon.ps1 -DisplayOnly` - -### Frequently Asked Questions - -**The script says it found suspicious files, and it lists a bunch of zip files. What does this mean?** - -The script will flag any zip/7x/rar files that it finds in ProgramData. As noted in -[this blog post](https://www.microsoft.com/security/blog/2021/03/02/hafnium-targeting-exchange-servers/), web -shells have been observed using such files for exfiltration. An administrator should review the files to -determine if they are valid. Determining if a zip file is a valid part of an installed -product is outside the scope of this script, and whitelisting files by name would only encourage -the use of those specific names by attackers. - -**I'm having trouble running the script on Exchange 2010.** - -If PowerShell 3 is present, the script can be run on Exchange 2010. It will not run on PowerShell 2. One can -also enable PS Remoting and run the script remotely against Exchange 2010. However, -the script has minimal functionality in these scenarios, as Exchange 2010 is only affected by one of the -four announced exploits - CVE-2021-26857. Further, this exploit is only available if the Unified Messaging role -is present. As a result, it is often easier to simply run the Get-EventLog command from the -[blog post](https://www.microsoft.com/security/blog/2021/03/02/hafnium-targeting-exchange-servers/), -rather than using Test-ProxyLogon. - -## [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 -* CVE-2021-26857 -* CVE-2021-27065 -* CVE-2021-26858 - -For more information on each mitigation please visit https://aka.ms/exchangevulns - -**This should only be used as a temporary mitigation until your Exchange Servers can be fully patched, recommended guidance is to apply all of the mitigations at once.** - -For this script to work you must have the IIS URL Rewrite Module installed which can be done via this script using the -FullPathToMSI parameter. - -For IIS 10 and higher URL Rewrite Module 2.1 must be installed, you can download version 2.1 here: - -* x86 & x64 -https://www.iis.net/downloads/microsoft/url-rewrite - -For IIS 8.5 and lower Rewrite Module 2.0 must be installed, you can download version 2.0 here: - -* x86 - https://www.microsoft.com/en-us/download/details.aspx?id=5747 - -* x64 - https://www.microsoft.com/en-us/download/details.aspx?id=7435 - -Installing URL Rewrite version 2.1 on IIS versions 8.5 and lower may cause IIS and Exchange to become unstable. If there is a mismatch between the URL Rewrite module and IIS version, ExchangeMitigations.ps1 will not apply the mitigation for CVE-2021-26855. You must uninstall the URL Rewrite module and reinstall the correct version. - -Script requires PowerShell 3.0 and later and must be executed from an elevated PowerShell Session. - -Download the latest release here: - -[Download ExchangeMitigations.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/ExchangeMitigations.ps1) - -To apply all mitigations with MSI install - -`.\ExchangeMitigations.ps1 -FullPathToMSI "FullPathToMSI" -WebSiteNames "Default Web Site" -ApplyAllMitigations` - -To apply all mitigations without MSI install - -`.\ExchangeMitigations.ps1 -WebSiteNames "Default Web Site" -ApplyAllMitigations -Verbose` - -To rollback all mitigations - -`.\ExchangeMitigations.ps1 -WebSiteNames "Default Web Site" -RollbackAllMitigation` - -To apply multiple or specific mitigations (out of the 4) - -`.\ExchangeMitigations.ps1 -WebSiteNames "Default Web Site" -ApplyECPAppPoolMitigation -ApplyOABAppPoolMitigation` - -To rollback multiple or specific mitigations - -`.\ExchangeMitigations.ps1 -WebSiteNames "Default Web Site" -RollbackECPAppPoolMitigation -RollbackOABAppPoolMitigation` - -## [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). - -[Download CompareExchangeHashes.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/CompareExchangeHashes.ps1) - -`.\CompareExchangeHashes.ps1` - -This script takes the following actions: -* Checks file hashes in exchange vdirs against known good baseline of hashes. -* Any file under IIS root which is edited after Dec 1st 2020 is marked as suspicious. - -**This script needs to be run as administrator on all the exchange servers separately**. - -The script determines the version of exchange installed on the server and then downloads the hashes for known exchange files from the [published known good hashes of exchange files](https://github.com/microsoft/CSS-Exchange/releases/latest). - -The result generated is stored in a file locally with the following format: _result.csv -If potential malicious files are found during comparision there is an error generated on the cmdline. -* Note: If the result file contains huge number of rows, it is potentially due to missing baseline hashes, please find the exchange versions found on the machine and leave a comment on issue [313](https://github.com/microsoft/CSS-Exchange/issues/313) - -To read the output, open the result csv file in excel or in powershell: - -`$result = Import-Csv ` - -Note: If the server does not have internet connectivity, run the script which would output the exchange versions discovered on the server. The baselines can be downloaded from [published known good hashes of exchange files](https://github.com/microsoft/CSS-Exchange/releases/latest) and re-run the script. - -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](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. - -This will help with defense against the known patterns observed but not the SSRF as a whole. For more information please visit https://aka.ms/exchangevulns. - -**For this script to work you must have the IIS URL Rewrite Module installed which can be done via this script using the -FullPathToMSI parameter.** - -For IIS 10 and higher URL Rewrite Module 2.1 must be installed, you can download version 2.1 here: - -* x86 & x64 -https://www.iis.net/downloads/microsoft/url-rewrite - -For IIS 8.5 and lower Rewrite Module 2.0 must be installed, you can download version 2.0 here: - -* x86 - https://www.microsoft.com/en-us/download/details.aspx?id=5747 - -* x64 - https://www.microsoft.com/en-us/download/details.aspx?id=7435 - -Installing URL Rewrite version 2.1 on IIS versions 8.5 and lower may cause IIS and Exchange to become unstable. If there is a mismatch between the URL Rewrite module and IIS version, BackendCookieMitigation.ps1 will not apply the mitigation for CVE-2021-26855. You must uninstall the URL Rewrite module and reinstall the correct version. - -**Script requires PowerShell 3.0 and later and must be executed from an elevated PowerShell Session.** - -Download the latest release here: - -[Download BackendCookieMitigation.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/BackendCookieMitigation.ps1) - -To apply with MSI install of the URL Rewrite module - Note: version may vary depending on system info - -`PS C:\> BackendCookieMitigation.ps1 -FullPathToMSI "C:\temp\rewrite_amd64_en-US.msi" -WebSiteNames "Default Web Site" -Verbose ` - -To apply without MSI install - -`PS C:\> BackendCookieMitigation.ps1 -WebSiteNames "Default Web Site" -Verbose` - -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](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. - -Download the latest release here: - -[Download http-vuln-cve2021-26855.nse](https://github.com/microsoft/CSS-Exchange/releases/latest/download/http-vuln-cve2021-26855.nse) +# How to use +1. Gather admin audit log results using [Search-AdminAuditLog](https://docs.microsoft.com/en-us/powershell/module/exchange/search-adminauditlog?view=exchange-ps). +2. Pipe the results into the Get-SimpleAuditLogReport script. +3. Open the CSV file created in the same directory with the script. \ No newline at end of file From 84929441dc30afe8f78d99523d69e407368d2739 Mon Sep 17 00:00:00 2001 From: Bill Long Date: Fri, 21 May 2021 10:02:03 -0500 Subject: [PATCH 4/8] Move readme to docs --- .../Admin/Get-SimpleAdminAuditLogReport.md | 12 ++++++++++-- docs/Admin/index.md | 4 ++++ docs/index.md | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) rename Admin/README.md => docs/Admin/Get-SimpleAdminAuditLogReport.md (75%) create mode 100644 docs/Admin/index.md diff --git a/Admin/README.md b/docs/Admin/Get-SimpleAdminAuditLogReport.md similarity index 75% rename from Admin/README.md rename to docs/Admin/Get-SimpleAdminAuditLogReport.md index ae62cf0ccf..022e112a5f 100644 --- a/Admin/README.md +++ b/docs/Admin/Get-SimpleAdminAuditLogReport.md @@ -1,4 +1,12 @@ -# About Get-SimpleAdminAuditLog +--- +title: Get-SimpleAdminAuditLogReport.ps1 +parent: Admin +--- + +## Get-SimpleAdminAuditLog + +Download the latest release: [Get-SimpleAdminAuditLogReport.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/Get-SimpleAdminAuditLogReport.ps1) + Exchange admin audit logs are not readily human readable. All of the data needed to understand what Cmdlet has been run is in the data but it is not very easy to read. Get-SimpleAdminAuditLog will take the results of an audit log search and provide a significantly more human readable version of the data. It will parse the audit log and attempt to reconstruct the actual Cmdlet that was run. @@ -11,4 +19,4 @@ It will parse the audit log and attempt to reconstruct the actual Cmdlet that wa # How to use 1. Gather admin audit log results using [Search-AdminAuditLog](https://docs.microsoft.com/en-us/powershell/module/exchange/search-adminauditlog?view=exchange-ps). 2. Pipe the results into the Get-SimpleAuditLogReport script. -3. Open the CSV file created in the same directory with the script. \ No newline at end of file +3. Open the CSV file created in the same directory with the script. diff --git a/docs/Admin/index.md b/docs/Admin/index.md new file mode 100644 index 0000000000..61db31d335 --- /dev/null +++ b/docs/Admin/index.md @@ -0,0 +1,4 @@ +--- +title: Admin +has_children: true +--- diff --git a/docs/index.md b/docs/index.md index 845b3971a9..6e312b4b18 100644 --- a/docs/index.md +++ b/docs/index.md @@ -17,6 +17,7 @@ ExchangeMitigations.ps1 | [Docs](Security/ExchangeMitigations) | [Download](http ExPerfAnalyzer.ps1 | [Docs](Performance/ExPerfAnalyzer) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/ExPerfAnalyzer.ps1) FixInstallerCache | [Docs](Setup/FixInstallerCache) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/FixInstallerCache.ps1) Get-MRMDetails.ps1 | [Docs](Retention/Get-MRMDetails) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/Get-MRMDetails.ps1) +Get-SimpleAdminAuditLogReport.ps1 | [Docs](Admin/Get-SimpleAdminAuditLogReport) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/Get-SimpleAdminAuditLogReport.ps1) HealthChecker.ps1 | [Docs](Diagnostics/HealthChecker) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/HealthChecker.ps1) http-vuln-cve2021-26855.nse | [Docs](Security/http-vuln-cve2021-26855) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/http-vuln-cve2021-26855.nse) SetupAssist.ps1 | [Docs](Setup/SetupAssist) | [Download](https://github.com/microsoft/CSS-Exchange/releases/latest/download/SetupAssist.ps1) From 261b4b520cc2fa1fe7d863ae04dc560c796ebcdd Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 25 May 2021 10:20:37 -0400 Subject: [PATCH 5/8] Removed disclaimer and author note --- Admin/Get-SimpleAuditLogReport.ps1 | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/Admin/Get-SimpleAuditLogReport.ps1 b/Admin/Get-SimpleAuditLogReport.ps1 index 266ac156e2..a392c1f039 100644 --- a/Admin/Get-SimpleAuditLogReport.ps1 +++ b/Admin/Get-SimpleAuditLogReport.ps1 @@ -1,24 +1,4 @@ -################################################################################# -# -# The sample scripts are not supported under any Microsoft standard support -# program or service. The sample scripts are provided AS IS without warranty -# of any kind. Microsoft further disclaims all implied warranties including, without -# limitation, any implied warranties of merchantability or of fitness for a particular -# purpose. The entire risk arising out of the use or performance of the sample scripts -# and documentation remains with you. In no event shall Microsoft, its authors, or -# anyone else involved in the creation, production, or delivery of the scripts be liable -# for any damages whatsoever (including, without limitation, damages for loss of business -# profits, business interruption, loss of business information, or other pecuniary loss) -# arising out of the use of or inability to use the sample scripts or documentation, -# even if Microsoft has been advised of the possibility of such damages. -# -################################################################################# -# -# Author matbyrd@microsoft.com -# -################################################################################# - -<# +<# .SYNOPSIS Parses the output of Search-AdminAuditlog to produce more readable results. From eae710938df73dd3403902ac8f6090bee1e9e7e4 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 25 May 2021 10:26:09 -0400 Subject: [PATCH 6/8] Corrected indent issues --- Admin/Get-SimpleAuditLogReport.ps1 | 244 ++++++++++++++--------------- 1 file changed, 122 insertions(+), 122 deletions(-) diff --git a/Admin/Get-SimpleAuditLogReport.ps1 b/Admin/Get-SimpleAuditLogReport.ps1 index a392c1f039..dced04d751 100644 --- a/Admin/Get-SimpleAuditLogReport.ps1 +++ b/Admin/Get-SimpleAuditLogReport.ps1 @@ -56,145 +56,145 @@ Outputs to the screen #> Param ( - [Parameter(Position = 0, Mandatory = $true, ValueFromPipelinepeline = $true, ValueFromPipelineByPropertyName = $true)] - $SearchResults, - [switch]$ResolveCaller, - [switch]$Agree + [Parameter(Position = 0, Mandatory = $true, ValueFromPipelinepeline = $true, ValueFromPipelineByPropertyName = $true)] + $SearchResults, + [switch]$ResolveCaller, + [switch]$Agree ) # Setup to process incomming results Begin { - # Statement to ensure that you have looked at the disclaimer or that you have removed this line so you don't have too - If ($Agree -ne $true) { Write-Error "Please run the script with -Agree to indicate that you have read and agreed to the sample script disclaimer at the top of the script file" -ErrorAction Stop } + # Statement to ensure that you have looked at the disclaimer or that you have removed this line so you don't have too + If ($Agree -ne $true) { Write-Error "Please run the script with -Agree to indicate that you have read and agreed to the sample script disclaimer at the top of the script file" -ErrorAction Stop } - # Make sure the array is null - [array]$ResultSet = $null + # Make sure the array is null + [array]$ResultSet = $null - # Set the counter to 1 - $i = 1 + # Set the counter to 1 + $i = 1 - # If resolveCaller is called it can take much longer to run so notify the user of that - if ($ResolveCaller) { Write-Warning "ResolveCaller specified; Script will take significantly longer to run as it attemps to resolve the primary SMTP address of each calling user. Progress updates will be provided every 25 entries." } + # If resolveCaller is called it can take much longer to run so notify the user of that + if ($ResolveCaller) { Write-Warning "ResolveCaller specified; Script will take significantly longer to run as it attemps to resolve the primary SMTP address of each calling user. Progress updates will be provided every 25 entries." } } # Process thru what ever is comming into the script Process { - # Deal with each object in the input - $searchresults | ForEach-Object { + # Deal with each object in the input + $searchresults | ForEach-Object { - # Reset the result object - $Result = New-Object PSObject + # Reset the result object + $Result = New-Object PSObject - # Get the alias of the User that ran the command - $user = ($_.caller.split("/"))[-1] + # Get the alias of the User that ran the command + $user = ($_.caller.split("/"))[-1] - # If we used resolve caller then try to resolve the primary SMTP address of the calling user - if ($ResolveCaller) { - - # attempt to resolve the recipient - [string]$Recipient = (get-recipient $user -ErrorAction silentlycontinue).primarysmtpaddress - - # if we get a result then put that in the output otherwise do nothing - If (!([string]::IsNullOrEmpty($Recipient))) { $user = $Recipient } - - # Since this is going to take longer to run provide status every 25 entries - if ($i % 25 -eq 0) { Write-Host "Processed 25 Results" } - $i++ - } - - # Build the command that was run - $switches = $_.cmdletparameters - [string]$FullCommand = $_.cmdletname - - # Get all of the switchs and add them in "human" form to the output - foreach ($parameter in $switches) { - - # Format our values depending on what they are so that they are as close - # a match as possible for what would have been entered - switch -regex ($parameter.value) { - - # If we have a multi value array put in then we need to break it out and add quotes as needed - '[;]' { - - # Reset the formatted value string - $FormattedValue = $null - - # Split it into an array - $valuearray = $switch.current.split(";") - - # For each entry in the array add quotes if needed and add it to the formatted value string - $valuearray | ForEach-Object { - if ($_ -match "[ \t]") { $FormattedValue = $FormattedValue + "`"" + $_ + "`";" } - else { $FormattedValue = $FormattedValue + $_ + ";" } - } - - # Clean up the trailing ; - $FormattedValue = $FormattedValue.trimend(";") - - # Add our switch + cleaned up value to the command string - $FullCommand = $FullCommand + " -" + $parameter.name + " " + $FormattedValue - } - - # If we have a value with spaces add quotes - '[ \t]' { $FullCommand = $FullCommand + " -" + $parameter.name + " `"" + $switch.current + "`"" } - - # If we have a true or false format them with :$ in front ( -allow:$true ) - '^True$|^False$' { $FullCommand = $FullCommand + " -" + $parameter.name + ":`$" + $switch.current } - - # Otherwise just put the switch and the value - default { $FullCommand = $FullCommand + " -" + $parameter.name + " " + $switch.current } - } - } - } - - # Pull out the Modified properties - $ModifiedProperties = $_.modifiedproperties - - # Make sure our holding variable are nulled out - $Property = $null - $Oldvalue = $null - $NewValue = $null - - # Push each property set into a seperate string - $ModifiedProperties | ForEach-Object { - [string]$Property = $Property + $_.name + ";" - [string]$OldValue = $OldValue + $_.oldvalue + ";" - [string]$NewValue = $NewValue + $_.newvalue + ";" - } - - # Trim off the last ; - $Property = $Property.TrimEnd(";") - $Oldvalue = $Oldvalue.TrimEnd(";") - $NewValue = $NewValue.TrimEnd(";") - - # Format our modified object - if ([string]::IsNullOrEmpty($_.objectModified)) { - $ObjModified = "" - } else { - $ObjModified = ($_.objectmodified.split("/"))[-1] - $ObjModified = ($ObjModified.split("\"))[-1] - } - - # Get just the name of the cmdlet that was run - [string]$cmdlet = $_.CmdletName - - # Build the result object to return our values - $Result | Add-Member -MemberType NoteProperty -Value $user -Name Caller - $Result | Add-Member -MemberType NoteProperty -Value $cmdlet -Name Cmdlet - $ResultResult | Add-Member -MemberType NoteProperty -Value $FullCommand -Name FullCommand - $Result | Add-Member -MemberType NoteProperty -Value $_.rundate -Name RunDate - $Result | Add-Member -MemberType NoteProperty -Value $ObjModified -Name ObjectModified - $Result | Add-Member -MemberType NoteProperty -Value $Property -Name ModifiedProperties - $Result | Add-Member -MemberType NoteProperty -Value $Oldvalue -Name OldValue - $Result | Add-Member -MemberType NoteProperty -Value $NewValue -Name NewValue - - # Add the object to the array to be returned - $ResultSet = $ResultSet + $Result + # If we used resolve caller then try to resolve the primary SMTP address of the calling user + if ($ResolveCaller) { + + # attempt to resolve the recipient + [string]$Recipient = (get-recipient $user -ErrorAction silentlycontinue).primarysmtpaddress + + # if we get a result then put that in the output otherwise do nothing + If (!([string]::IsNullOrEmpty($Recipient))) { $user = $Recipient } + + # Since this is going to take longer to run provide status every 25 entries + if ($i % 25 -eq 0) { Write-Host "Processed 25 Results" } + $i++ + } + + # Build the command that was run + $switches = $_.cmdletparameters + [string]$FullCommand = $_.cmdletname + + # Get all of the switchs and add them in "human" form to the output + foreach ($parameter in $switches) { + + # Format our values depending on what they are so that they are as close + # a match as possible for what would have been entered + switch -regex ($parameter.value) { + + # If we have a multi value array put in then we need to break it out and add quotes as needed + '[;]' { + + # Reset the formatted value string + $FormattedValue = $null + + # Split it into an array + $valuearray = $switch.current.split(";") + + # For each entry in the array add quotes if needed and add it to the formatted value string + $valuearray | ForEach-Object { + if ($_ -match "[ \t]") { $FormattedValue = $FormattedValue + "`"" + $_ + "`";" } + else { $FormattedValue = $FormattedValue + $_ + ";" } + } + + # Clean up the trailing ; + $FormattedValue = $FormattedValue.trimend(";") + + # Add our switch + cleaned up value to the command string + $FullCommand = $FullCommand + " -" + $parameter.name + " " + $FormattedValue + } + + # If we have a value with spaces add quotes + '[ \t]' { $FullCommand = $FullCommand + " -" + $parameter.name + " `"" + $switch.current + "`"" } + + # If we have a true or false format them with :$ in front ( -allow:$true ) + '^True$|^False$' { $FullCommand = $FullCommand + " -" + $parameter.name + ":`$" + $switch.current } + + # Otherwise just put the switch and the value + default { $FullCommand = $FullCommand + " -" + $parameter.name + " " + $switch.current } + } + } + } + + # Pull out the Modified properties + $ModifiedProperties = $_.modifiedproperties + + # Make sure our holding variable are nulled out + $Property = $null + $Oldvalue = $null + $NewValue = $null + + # Push each property set into a seperate string + $ModifiedProperties | ForEach-Object { + [string]$Property = $Property + $_.name + ";" + [string]$OldValue = $OldValue + $_.oldvalue + ";" + [string]$NewValue = $NewValue + $_.newvalue + ";" + } + + # Trim off the last ; + $Property = $Property.TrimEnd(";") + $Oldvalue = $Oldvalue.TrimEnd(";") + $NewValue = $NewValue.TrimEnd(";") + + # Format our modified object + if ([string]::IsNullOrEmpty($_.objectModified)) { + $ObjModified = "" + } else { + $ObjModified = ($_.objectmodified.split("/"))[-1] + $ObjModified = ($ObjModified.split("\"))[-1] + } + + # Get just the name of the cmdlet that was run + [string]$cmdlet = $_.CmdletName + + # Build the result object to return our values + $Result | Add-Member -MemberType NoteProperty -Value $user -Name Caller + $Result | Add-Member -MemberType NoteProperty -Value $cmdlet -Name Cmdlet + $ResultResult | Add-Member -MemberType NoteProperty -Value $FullCommand -Name FullCommand + $Result | Add-Member -MemberType NoteProperty -Value $_.rundate -Name RunDate + $Result | Add-Member -MemberType NoteProperty -Value $ObjModified -Name ObjectModified + $Result | Add-Member -MemberType NoteProperty -Value $Property -Name ModifiedProperties + $Result | Add-Member -MemberType NoteProperty -Value $Oldvalue -Name OldValue + $Result | Add-Member -MemberType NoteProperty -Value $NewValue -Name NewValue + + # Add the object to the array to be returned + $ResultSet = $ResultSet + $Result } # Final steps End { - # Return the array set - Return $ResultSet + # Return the array set + Return $ResultSet } \ No newline at end of file From ec0891624c97b5d725bfd032ead5ba9f74152e96 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 25 May 2021 10:27:23 -0400 Subject: [PATCH 7/8] Fixed output object typo --- Admin/Get-SimpleAuditLogReport.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Admin/Get-SimpleAuditLogReport.ps1 b/Admin/Get-SimpleAuditLogReport.ps1 index dced04d751..b6e988eba3 100644 --- a/Admin/Get-SimpleAuditLogReport.ps1 +++ b/Admin/Get-SimpleAuditLogReport.ps1 @@ -183,7 +183,7 @@ Process { # Build the result object to return our values $Result | Add-Member -MemberType NoteProperty -Value $user -Name Caller $Result | Add-Member -MemberType NoteProperty -Value $cmdlet -Name Cmdlet - $ResultResult | Add-Member -MemberType NoteProperty -Value $FullCommand -Name FullCommand + $Result | Add-Member -MemberType NoteProperty -Value $FullCommand -Name FullCommand $Result | Add-Member -MemberType NoteProperty -Value $_.rundate -Name RunDate $Result | Add-Member -MemberType NoteProperty -Value $ObjModified -Name ObjectModified $Result | Add-Member -MemberType NoteProperty -Value $Property -Name ModifiedProperties From f8d15a33eca946235c21c072e9824c62ec55b32a Mon Sep 17 00:00:00 2001 From: Canthv0 Date: Tue, 25 May 2021 11:00:17 -0400 Subject: [PATCH 8/8] Adding empty line --- Admin/Get-SimpleAuditLogReport.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Admin/Get-SimpleAuditLogReport.ps1 b/Admin/Get-SimpleAuditLogReport.ps1 index 266ac156e2..04e33862b5 100644 --- a/Admin/Get-SimpleAuditLogReport.ps1 +++ b/Admin/Get-SimpleAuditLogReport.ps1 @@ -217,4 +217,4 @@ Process { End { # Return the array set Return $ResultSet -} \ No newline at end of file +}