Private/Probes.Security.ps1
|
function Invoke-HLPowerShellModuleLoggingProbe { [CmdletBinding()] param() $root = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging' $enabled = Get-HLRegistryValue -Path $root -Name 'EnableModuleLogging' $moduleNamesPath = "$root\ModuleNames" $moduleNames = Get-HLRegistryKeyValueMap -Path $moduleNamesPath $patterns = if ($null -ne $moduleNames) { @($moduleNames.PSObject.Properties | ForEach-Object { [string]$_.Value } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) } else { @() } $evidence = [pscustomobject][ordered]@{ Enabled = if ($enabled.ValueExists) { $enabled.Value } else { $null }; ModulePatterns = $patterns; PolicyPath = $root } if (-not $enabled.ValueExists -or [int]$enabled.Value -ne 1) { return Get-HLProbeResult -Status Fail -Expected 'Enabled with at least one module pattern' -Actual 'Disabled or not configured' -Message 'PowerShell Module Logging is not enabled.' -Evidence $evidence } if ($patterns.Count -eq 0) { return Get-HLProbeResult -Status Fail -Expected 'At least one module pattern' -Actual 'No patterns configured' -Message 'Module Logging is enabled but no module pattern is configured.' -Evidence $evidence } return Get-HLProbeResult -Status Pass -Expected 'Enabled with at least one module pattern' -Actual ($patterns -join ', ') -Message 'PowerShell Module Logging is enabled with configured module patterns.' -Evidence $evidence } function Invoke-HLEventLogProbe { [CmdletBinding()] param( [Parameter(Mandatory)] [object]$Control ) try { $log = Get-WinEvent -ListLog ([string]$Control.parameters.logName) -ErrorAction Stop $minimum = [int64]$Control.parameters.minimumSizeBytes $requireEnabled = (Test-HLProperty -InputObject $Control.parameters -Name 'requireEnabled') -and [bool]$Control.parameters.requireEnabled $evidence = [pscustomobject][ordered]@{ LogName = [string]$log.LogName IsEnabled = [bool]$log.IsEnabled MaximumSizeBytes = [int64]$log.MaximumSizeInBytes LogMode = [string]$log.LogMode RecordCount = $log.RecordCount } if ($requireEnabled -and -not [bool]$log.IsEnabled) { return Get-HLProbeResult -Status Fail -Expected 'Enabled with sufficient capacity' -Actual 'Disabled' -Message 'The event log is disabled.' -Evidence $evidence } if ([int64]$log.MaximumSizeInBytes -lt $minimum) { return Get-HLProbeResult -Status Fail -Expected ("At least $minimum bytes") -Actual ([int64]$log.MaximumSizeInBytes) -Message 'The event log maximum size is below the baseline.' -Evidence $evidence } return Get-HLProbeResult -Status Pass -Expected ("At least $minimum bytes") -Actual ([int64]$log.MaximumSizeInBytes) -Message 'The event log capacity matches the baseline.' -Evidence $evidence } catch { return Get-HLProbeResult -Status Error -Expected 'Event log enabled and sized' -Actual $null -Message "Unable to query event log '$($Control.parameters.logName)': $($_.Exception.Message)" } } function Invoke-HLBitLockerProbe { [CmdletBinding()] param( [Parameter(Mandatory)] [object]$Control, [AllowNull()] [object]$SystemContext ) if ($null -eq (Get-Command -Name Get-BitLockerVolume -ErrorAction SilentlyContinue)) { $productType = if ($null -ne $SystemContext) { [int]$SystemContext.ProductType } else { 0 } if ($productType -in @(2, 3)) { # On server SKUs the BitLocker feature is optional; its absence # proves the volume is unprotected rather than unresolvable. $evidence = [pscustomobject][ordered]@{ BitLockerFeatureInstalled = $false ProductType = $productType } return Get-HLProbeResult -Status Fail -Expected 'BitLocker protection enabled' -Actual 'BitLocker feature not installed' -Message 'The BitLocker feature is not installed on this server, so the operating system volume is not protected by BitLocker.' -Evidence $evidence } return Get-HLProbeResult -Status Unknown -Expected 'BitLocker protection enabled' -Actual $null -Message 'Get-BitLockerVolume is not available.' } $mountPoint = if ([string]$Control.parameters.mountPoint -eq 'SystemDrive') { [string]$env:SystemDrive } else { [string]$Control.parameters.mountPoint } try { $volume = Get-BitLockerVolume -MountPoint $mountPoint -ErrorAction Stop $evidence = [pscustomobject][ordered]@{ MountPoint = [string]$volume.MountPoint VolumeStatus = [string]$volume.VolumeStatus ProtectionStatus = [string]$volume.ProtectionStatus EncryptionMethod = [string]$volume.EncryptionMethod KeyProtectorTypes = @($volume.KeyProtector | ForEach-Object { [string]$_.KeyProtectorType }) } if ([string]$volume.ProtectionStatus -eq 'On' -and [string]$volume.VolumeStatus -eq 'FullyEncrypted') { return Get-HLProbeResult -Status Pass -Expected 'Protection On and FullyEncrypted' -Actual 'Protection On and FullyEncrypted' -Message 'The operating system volume is fully encrypted and protected.' -Evidence $evidence } if ([string]$volume.ProtectionStatus -eq 'On' -and [string]$volume.VolumeStatus -match 'EncryptionInProgress') { return Get-HLProbeResult -Status Warning -Expected 'FullyEncrypted' -Actual ([string]$volume.VolumeStatus) -Message 'BitLocker is enabled but encryption is still in progress.' -Evidence $evidence } return Get-HLProbeResult -Status Fail -Expected 'Protection On and FullyEncrypted' -Actual ("$($volume.ProtectionStatus), $($volume.VolumeStatus)") -Message 'The operating system volume is not fully protected by BitLocker.' -Evidence $evidence } catch { return Get-HLProbeResult -Status Error -Expected 'BitLocker protection enabled' -Actual $null -Message "Unable to query BitLocker on '$mountPoint': $($_.Exception.Message)" } } function Invoke-HLSecureBootProbe { [CmdletBinding()] param() if ($null -eq (Get-Command -Name Confirm-SecureBootUEFI -ErrorAction SilentlyContinue)) { return Get-HLProbeResult -Status Unknown -Expected 'Enabled' -Actual $null -Message 'Confirm-SecureBootUEFI is not available.' } try { $enabled = Confirm-SecureBootUEFI -ErrorAction Stop if ($enabled) { return Get-HLProbeResult -Status Pass -Expected 'Enabled' -Actual 'Enabled' -Message 'Secure Boot is enabled.' } return Get-HLProbeResult -Status Fail -Expected 'Enabled' -Actual 'Disabled' -Message 'Secure Boot is supported but disabled.' } catch { $exception = $_.Exception $message = [string]$exception.Message # Classify by exception type and error code first; provider message # text is localized and matches only on English systems. if ($exception -is [PlatformNotSupportedException] -or (Test-HLErrorMatchesCode -Exception $exception -Code 0xC0000002) -or $message -match '(?i:not supported|unsupported|not UEFI)') { return Get-HLProbeResult -Status Fail -Expected 'Enabled' -Actual 'Unsupported by current firmware or virtual hardware' -Message $message } if ($exception -is [UnauthorizedAccessException] -or (Test-HLErrorMatchesCode -Exception $exception -Code 0x80070005, 1314, 0x80070522)) { return Get-HLProbeResult -Status Unknown -Expected 'Enabled' -Actual $null -Message 'Querying Secure Boot requires an elevated session. Collect with elevation to resolve this control.' } return Get-HLProbeResult -Status Error -Expected 'Enabled' -Actual $null -Message "Unable to query Secure Boot: $message" } } function Invoke-HLAutoRunProbe { [CmdletBinding()] param() $path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer' $driveType = Get-HLRegistryValue -Path $path -Name 'NoDriveTypeAutoRun' $noAutorun = Get-HLRegistryValue -Path $path -Name 'NoAutorun' $evidence = [pscustomobject][ordered]@{ Path = $path NoDriveTypeAutoRun = if ($driveType.ValueExists) { $driveType.Value } else { $null } NoAutorun = if ($noAutorun.ValueExists) { $noAutorun.Value } else { $null } } if ($driveType.ValueExists -and [int]$driveType.Value -eq 255 -and $noAutorun.ValueExists -and [int]$noAutorun.Value -eq 1) { return Get-HLProbeResult -Status Pass -Expected 'NoDriveTypeAutoRun=255 and NoAutorun=1' -Actual 'Configured' -Message 'AutoRun and AutoPlay are disabled through machine policy.' -Evidence $evidence } return Get-HLProbeResult -Status Fail -Expected 'NoDriveTypeAutoRun=255 and NoAutorun=1' -Actual (ConvertTo-HLDisplayString -Value $evidence) -Message 'AutoRun and AutoPlay are not fully disabled through machine policy.' -Evidence $evidence } |