DefenderEval-Report.psm1
#Requires -Version 5.1 <# .SYNOPSIS Verify configuration are aligning with recommended settings when performing an evaluation of Microsoft Defender Antivirus and Microsoft Defender for Endpoint .DESCRIPTION .NOTES Jonathan Devere-Ellery Cloud Solution Architect - Microsoft ############################################################################################## #This sample script is not supported under any Microsoft standard support program or service. #This sample script is 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 script 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 script or #documentation, even if Microsoft has been advised of the possibility of such damages. ############################################################################################## #> Function Invoke-ModuleVersionCheck { # Determines if the module is up to date $GalleryVersion = Find-Module DefenderEval $InstalledVersion = Get-Module DefenderEval If($GalleryVersion.Version -gt $InstalledVersion.Version) { Write-Host "$(Get-Date) The loaded version of the DefenderEval module ($($InstalledVersion.Version)) is older than the latest version in the PSGallery ($($GalleryVersion.Version)). Attempting to upgrade to the latest version." $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) if ($currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { $InstallArguments = @{ Scope = "AllUsers" } } else { $InstallArguments = @{ Scope = "CurrentUser" } } Try { Install-Module DefenderEval -Force -AllowClobber @InstallArguments } Catch { Write-Error "Error while trying to upgrade the module. Try running Update-Module DefenderEval" } # Uninstall old versions $Modules = (Get-Module DefenderEval -ListAvailable | Sort-Object Version -Descending) $Latest = $Modules[0] If($Modules.Count -gt 1) { ForEach($Module in $Modules) { If($Module.Version -ne $Latest.Version) { # Not the latest version, remove it. Write-Host "$(Get-Date) Uninstalling $($Module.Name) Version $($Module.Version)" Try { Uninstall-Module $Module.Name -RequiredVersion $($Module.Version) -ErrorAction:Stop } Catch {} } } } } } Function Invoke-CheckDefenderRecommendations { param ( ) # Prechecks Invoke-ModuleVersionCheck $Results = @() $MpPref = Get-MpPreference $MpComputerStatus = Get-MpComputerStatus # Evaluate Settings # Tamper Protection switch ($MpComputerStatus.IsTamperProtected) { $true {$Tamper = "Enabled"} $false {$Tamper = "Disabled"} } if ($Tamper -eq "Enabled") {$Result="Yes"} else {$Result="No"} $Results += New-Object -TypeName psobject -Property @{ Topic="Global" Check="IsTamperProtected" Result=$Result Config=$Tamper Description= "Prevents certain security settings, such as virus and threat protection, from being disabled or changed" } # Collect details of configured Exclusions $Exclusions = @{ 'Excluded Paths' = @($MpPref.ExclusionPath) 'Excluded Processes' = @($MpPref.ExclusionExtension) 'Excluded Extensions' = @($MpPref.ExclusionExtension) 'Excluded IPs' = @($MpPref.ExclusionIpAddress) } # Cloud Protection - https://learn.microsoft.com/en-us/defender-endpoint/microsoft-defender-antivirus-using-powershell#cloud-protection-features switch ($MpPref.MAPSReporting) { {1 -or 2} {$MAPSReporting = "Advanced"} default {$MAPSReporting = "Disabled"} } if ($MAPSReporting -eq "Advanced") {$Result="Yes"} else {$Result="No"} $Results += New-Object -TypeName psobject -Property @{ Topic="Cloud Protection" Check="MAPSReporting" Result=$Result Config=$MAPSReporting Description= "Enable the Microsoft Defender Cloud for near-instant protection and increased protection" } switch ($MpPref.SubmitSamplesConsent) { 1 {$SubmitType = "SafeSamples"} 2 {$SubmitType = "NeverSend"} 3 {$SubmitType = "AllSamples"} 4 {$SubmitType = "AlwaysPrompt"} } if ($SubmitType -eq "AllSamples") {$Result="Yes"} else {$Result="No"} $Results += New-Object -TypeName psobject -Property @{ Topic="Cloud Protection" Check="SubmitSamplesConsent" Result=$Result Config=$SubmitType Description= "Automatically submit samples to increase group protection" } switch ($MpPref.DisableBlockAtFirstSeen) { 1 {$BAFS = "Disabled"} default {$BAFS = "Enabled"} } if ($BAFS -eq "Enabled") {$Result="Yes"} else {$Result="No"} $Results += New-Object -TypeName psobject -Property @{ Topic="Cloud Protection" Check="BlockAtFirstSeen" Result=$Result Config=$BAFS Description= "Always Use the cloud to block new malware within seconds" } switch ($MpPref.DisableIOAVProtection) { 1 {$IOAV = "Disabled"} default {$IOAV = "Enabled"} } if ($IOAV -eq "Enabled") {$Result="Yes"} else {$Result="No"} $Results += New-Object -TypeName psobject -Property @{ Topic="Cloud Protection" Check="IOAVProtection" Result=$Result Config=$IOAV Description= "Scan all downloaded files and attachments" } switch ($MpPref.CloudBlockLevel) { 0 {$CloudBlockLevel = "Default"} 1 {$CloudBlockLevel = "Moderate"} 2 {$CloudBlockLevel = "High"} 4 {$CloudBlockLevel = "HighPlus"} 6 {$CloudBlockLevel = "ZeroTolerance"} default {$CloudBlockLevel = "Default"} } if ($CloudBlockLevel -eq "High" -or $CloudBlockLevel -eq "HighPlus" -or $CloudBlockLevel -eq "ZeroTolerance") { $Result="Yes" } else {$Result="No"} $Results += New-Object -TypeName psobject -Property @{ Topic="Cloud Protection" Check="CloudBlockLevel" Result=$Result Config=$CloudBlockLevel Description= "Set cloud block level to at least 'High'" } if ($MpPref.CloudExtendedTimeout -ge 50) {$Result="Yes"} else {$Result="No"} $Results += New-Object -TypeName psobject -Property @{ Topic="Cloud Protection" Check="CloudExtendedTimeout" Result=$Result Config=$MpPref.CloudExtendedTimeout Description= "Set cloud block time-out to 1 minute" } # Real-time Scanning - https://learn.microsoft.com/en-us/defender-endpoint/microsoft-defender-antivirus-using-powershell#always-on-protection-real-time-scanning switch ($MpPref.DisableRealtimeMonitoring) { 1 {$RTPMonitoring = "Disabled"} default {$RTPMonitoring = "Enabled"} } if ($RTPMonitoring -eq "Enabled") {$Result="Yes"} else {$Result="No"} $Results += New-Object -TypeName psobject -Property @{ Topic="Real-time Scanning" Check="RealtimeMonitoring" Result=$Result Config=$RTPMonitoring Description= "Constantly monitor files and processes for known malware modifications" } switch ($MpPref.DisableBehaviorMonitoring) { 1 {$BehaviorMonitoring = "Disabled"} default {$BehaviorMonitoring = "Enabled"} } if ($BehaviorMonitoring -eq "Enabled") {$Result="Yes"} else {$Result="No"} $Results += New-Object -TypeName psobject -Property @{ Topic="Real-time Scanning" Check="BehaviorMonitoring" Result=$Result Config=$BehaviorMonitoring Description= "Constantly monitor for known malware behaviors - even in 'clean' files and running programs" } switch ($MpPref.DisableScriptScanning) { 1 {$ScriptScanning = "Disabled"} default {$ScriptScanning = "Enabled"} } if ($ScriptScanning -eq "Enabled") {$Result="Yes"} else {$Result="No"} $Results += New-Object -TypeName psobject -Property @{ Topic="Real-time Scanning" Check="ScriptScanning" Result=$Result Config=$ScriptScanning Description= "Scan scripts as soon as they're seen or run" } switch ($MpPref.DisableRemovableDriveScanning) { 1 {$RemovableDriveScanning = "Disabled"} default {$RemovableDriveScanning = "Enabled"} } if ($RemovableDriveScanning -eq "Enabled") {$Result="Yes"} else {$Result="No"} $Results += New-Object -TypeName psobject -Property @{ Topic="Real-time Scanning" Check="RemovableDriveScanning" Result=$Result Config=$RemovableDriveScanning Description= "Scan removable drives as soon as they're inserted or mounted" } # Potentially Unwanted Application protection - https://learn.microsoft.com/en-us/defender-endpoint/microsoft-defender-antivirus-using-powershell#potentially-unwanted-application-protection switch ($MpPref.PUAProtection) { 0 {$PUA = "Disabled"} 1 {$PUA = "Enabled"} 2 {$PUA = "Audit"} } if ($PUA -eq "Enabled") {$Result="Yes"} else {$Result="No"} $Results += New-Object -TypeName psobject -Property @{ Topic="Potentially Unwanted Application protection" Check="PUAProtection" Result=$Result Config=$PUA Description= "Prevent grayware, adware, and other potentially unwanted apps from installing" } # Email and archive scanning - https://learn.microsoft.com/en-us/defender-endpoint/microsoft-defender-antivirus-using-powershell#email-and-archive-scanning switch ($MpPref.DisableArchiveScanning) { 1 {$ArchiveScan = "Disabled"} default {$ArchiveScan = "Enabled"} } if ($ArchiveScan -eq "Enabled") {$Result="Yes"} else {$Result="No"} $Results += New-Object -TypeName psobject -Property @{ Topic="Email and archive scanning" Check="ArchiveScanning" Result=$Result Config=$ArchiveScan Description= "Scan files contained within archives" } switch ($MpPref.DisableEmailScanning) { 0 {$EmailScan = "Disabled"} default {$EmailScan = "Disabled"} } if ($EmailScan -eq "Enabled") {$Result="Yes"} else {$Result="No"} $Results += New-Object -TypeName psobject -Property @{ Topic="Email and archive scanning" Check="EmailScanning" Result=$Result Config=$EmailScan Description= "Scan email stored within files (e.g. .PST)" } # Exploit protection - https://learn.microsoft.com/en-us/defender-endpoint/microsoft-defender-antivirus-using-powershell#advanced-threat-and-exploit-mitigation-and-prevention-controlled-folder-access switch ($MpPref.EnableControlledFolderAccess) { 0 {$CFA = "Disabled"} 1 {$CFA = "Enabled"} 2 {$CFA = "Audit"} 3 {$CFA = "BlockDiskOnly"} 4 {$CFA = "AuditDiskOnly"} } if ($CFA -eq "Enabled") {$Result="Yes"} else {$Result="No"} $Results += New-Object -TypeName psobject -Property @{ Topic="Exploit protection" Check="EnableControlledFolderAccess" Result=$Result Config=$CFA Description= "Prevent malicious and suspicious apps (such as ransomware) from making changes to protected folders with Controlled folder access" } switch ($MpPref.EnableNetworkProtection) { 0 {$NetworkProtection = "Disabled"} 1 {$NetworkProtection = "Enabled"} 2 {$NetworkProtection = "Audit"} } if ($NetworkProtection -eq "Enabled") {$Result="Yes"} else {$Result="No"} $Results += New-Object -TypeName psobject -Property @{ Topic="Exploit protection" Check="EnableNetworkProtection" Result=$Result Config=$NetworkProtection Description= "Block connections to known bad IP addresses and other network connections with Network protection" } # https://learn.microsoft.com/en-us/defender-endpoint/attack-surface-reduction-rules-reference $ASRDefinitions = @{ "56a863a9-875e-4185-98a7-b882c64b5ce5" = "Block abuse of exploited vulnerable signed drivers"; "7674ba52-37eb-4a4f-a9a1-f0f9a1619a2c" = "Block Adobe Reader from creating child processes"; "d4f940ab-401b-4efc-aadc-ad5f3c50688a" = "Block all Office applications from creating child processes"; "9e6c4e1f-7d60-472f-ba1a-a39ef669e4b2" = "Block credential stealing from the Windows local security authority subsystem (lsass.exe)"; "be9ba2d9-53ea-4cdc-84e5-9b1eeee46550" = "Block executable content from email client and webmail"; "01443614-cd74-433a-b99e-2ecdc07bfc25" = "Block executable files from running unless they meet a prevalence, age, or trusted list criterion"; "5beb7efe-fd9a-4556-801d-275e5ffc04cc" = "Block execution of potentially obfuscated scripts"; "d3e037e1-3eb8-44c8-a917-57927947596d" = "Block JavaScript or VBScript from launching downloaded executable content"; "3b576869-a4ec-4529-8536-b80a7769e899" = "Block Office applications from creating executable content"; "75668c1f-73b5-4cf0-bb93-3ecf5cb7cc84" = "Block Office applications from injecting code into other processes"; "26190899-1602-49e8-8b27-eb1d0a1ce869" = "Block Office communication application from creating child processes"; "e6db77e5-3df2-4cf1-b95a-636979351e5b" = "Block persistence through WMI event subscription"; "d1e49aac-8f56-4280-b9ba-993a6d77406c" = "Block process creations originating from PSExec and WMI commands"; "33ddedf1-c6e0-47cb-833e-de6133960387" = "Block rebooting machine in Safe Mode"; "b2b3f03d-6a65-4f7b-a9c7-1c7ef74a9ba4" = "Block untrusted and unsigned processes that run from USB"; "c0033c00-d16d-4114-a5a0-dc9b3a7d2ceb" = "Block use of copied or impersonated system tools"; "a8f5898e-1dc8-49a9-9878-85004b8a61e6" = "Block Webshell creation for Servers"; "92e97fa1-2edf-4476-bdd6-9dd0b4dddc7b" = "Block Win32 API calls from Office macros"; "c1db55ab-c21a-4637-bb3f-a12568109d35" = "Use advanced protection against ransomware"; } $ASRIds = $MpPref.AttackSurfaceReductionRules_Ids $ASRActions = $MpPref.AttackSurfaceReductionRules_Actions $MappedASR = @() $i = 0 # Map both the ASR ID and Action within the same array foreach ($ASRId in $ASRIds) { $MappedASR += New-Object -TypeName psobject -Property @{ ID=$ASRId Action=$ASRActions[$i] } $i++ } ForEach ($ASR in $MappedASR) { # ASR Rule modes switch ($ASR.Action) { 0 {$ASRState = "Disabled"} 1 {$ASRState = "Block"} 2 {$ASRState = "Audit"} 6 {$ASRState = "Warn"} } if ($ASRState -eq "Block") {$Result="Yes"} else {$Result="No"} $ASRName = $ASRDefinitions[$ASR.ID] $Results += New-Object -TypeName psobject -Property @{ Topic="Exploit protection" Check="ASR Rule ($($ASR.ID))" Result=$Result Config=$ASRState Description=$ASRName } } # Return the results Invoke-GenerateReport -Results $Results } function Invoke-GenerateReport { param ( $Results ) $ReportTitle = "Defender Evaluation report" $ReportHeading = "Defender Evaluation report" $IntroText = "Verify configuration are aligning with recommended settings when performing an evaluation of Microsoft Defender Antivirus and Microsoft Defender for Endpoint." $ModuleInfo = (Get-Module -Name DefenderEval).Version # Output start $output += "<!doctype html> <html lang='en'> <head> <!-- Required meta tags --> <meta charset='utf-8'> <meta name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no'> <link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css' rel='stylesheet' integrity='sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH' crossorigin='anonymous'> <title>$ReportTitle</title> </head> <body> <div class='container my-5'> <div class='position-relative p-5 text-center text-muted bg-dark-subtle border border-dashed rounded-5'> <h1 class='text-body-emphasis'>$ReportHeading</h1> <p class='col-lg-10 mx-auto mb-4'>$IntroText</p> <a class='btn btn-primary px-4 mb-4' href='https://learn.microsoft.com/en-us/defender-endpoint/evaluate-microsoft-defender-antivirus' role='button' target='_blank'>Learn more</a> <div class='text-right'>Report generated: $((get-date).ToString("dd MMMM yyyy - HH:mm:ss"))</div> </div> </div> <script src='https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js' integrity='sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r' crossorigin='anonymous'></script> <script src='https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js' integrity='sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy' crossorigin='anonymous'></script> " # Loop each Topic foreach ($Topic in ($Results | Group-Object Topic)){ $output += "<div class='card m-3'> <h5 class='card-header bg-dark-subtle'>$($Topic.Name)</h5> <div class='card-body'> <table class='table table-hover table-striped'> <thead class='table-light'><tr> <th scope='col'></th> <th scope='col'>Feature</th> <th scope='col'>Current Value</th> <th scope='col'>Follows Recommendation?</th> <th scope='col'>Description</th> </tr></thead> <tbody> " # Loop each Result foreach ($Result in ($Results | Where-Object {$_.Topic -eq $Topic.Name})) { $output += "<tr><th scope='row'></th> <td>$($Result.Check)</td> <td>$($Result.Config)</td> <td";if($($Result.Result -eq "Yes")) {$output += " class='table-success'"} else {$output += " class='table-danger'"};$output+=">$($Result.Result)</td> <td>$($Result.Description)</td> </tr>" } $output += "</tbody></table></div></div>" } # Add details of Exclusions configured foreach ($Ex in $Exclusions.keys){ # Add one table for each exclusion type $output += "<div class='card m-3'> <h5 class='card-header bg-dark-subtle'>$($Ex)</h5> <table class='table table-hover table-striped'> <tbody>" # Define how to add a new row to the Exclusions tables $Row = "<tr> <td scope='row'><ReplaceMe></td></tr> " if ($($Exclusions.$Ex).Count -eq 0) { # Add a single row indicating there are no exclusions configured $newRow = ($Row -replace ("<ReplaceMe>","None")) $newRow = ($newRow -replace ("<td ","<td class='table-success'")) $output += $newRow } else { # Add a row for each configured exclusion foreach ($obj in ($Exclusions.$Ex)) { $output += ($Row -replace ("<ReplaceMe>",$Obj)) } } $output += " </tbody></table> </div></div>" } # End of the report $output += " <div class='card m-3 card-body text-center border-light text-body-secondary'> <p>Version: $ModuleInfo</p> </div> </body> </html> " # Export the generated HTML file $Folder = (Get-Item .).FullName $OutFile = "DefenderEval_$(Get-Date -Format ("yyyymmdd-HHmmss")).html" $FilePath = Join-Path -Path $Folder -ChildPath $OutFile $output | Out-File -FilePath $FilePath Invoke-Expression "&'$FilePath'" } |