Public/Test-SecurityState.ps1

#Requires -Version 5.1
#Requires -PSEdition Core, Desktop
#Requires -Modules @{ ModuleName="PKI"; ModuleVersion="1.0.0.0" }

using module PKI
using namespace System.Security.Cryptography.X509Certificates

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

class SicherheitInfoCheck {

    [int]    $Id
    [string] $Status
    [string] $Kategorie
    [string] $Test
    [Object] $IST
    [Object] $SOLL

    SicherheitInfoCheck([int]$Id, [string]$Status, [string]$Kategorie, [string]$Test, [Object]$IST, [Object]$SOLL) {
        $this.Id = $Id
        $this.Status = $Status
        $this.Kategorie = $Kategorie
        $this.Test = $Test
        $this.IST = $IST
        $this.SOLL = $SOLL
    }
}

function IstZertifikatExportierbar {
    param([X509Certificate2]$Cert)
    try {
        $Cert.Export(([X509ContentType]::Pfx)) | Out-Null
        return $true
    }
    catch {
        return $false
    }
}

function Test-SecurityState {

    <#
    .SYNOPSIS
    Analysiert das aktuelle System auch Sicherheitsschwachstellen in Verbindung mit PowerShell.
 
    .OUTPUTS
    Analyseergebnis wird per [SicherheitInfoCheck]-Objekt zurückgegeben. Erklärende Informationen erhältst Du per -Verbose-Meldung
 
    .EXAMPLE
    Test-SecurityState
 
    .EXAMPLE
    Test-SecurityState -Verbose
 
    .EXAMPLE
    Test-SecurityState -SkipModuleVersionTest -Verbose
 
    .EXAMPLE
    Test-SecurityState -SkipAlternateDataStreamTest -Verbose
 
    .EXAMPLE
    Test-SecurityState -SkipModuleVersionTest -SkipAlternateDataStreamTest -Verbose
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingEmptyCatchBlock','')]
    [CmdletBinding()]
    param(
        # Überspringt die zeitaufwändige Modul-Versions-Test zu der eine Internet-Verbindung bestehen muss.
        [switch]$SkipModuleVersionTest,

        # Überspringt die zeitaufwändige Alternate-Data-Stream-Test.
        [switch]$SkipAlternateDataStreamTest
    )

    $My = [HashTable]::Synchronized(@{})
    $My.ESC = [char]0x1b
    $My.Status = $null
    $My.Result = $null
    $My.TargetResult = $null
    $My.Id = 1
    $My.Neutral = "Neutral"
    $My.Passed = "$($My.ESC)[92mBestanden$($My.ESC)[0m"
    $My.Failed = "$($My.ESC)[91mDurchgefallen$($My.ESC)[0m"
    $My.Skip = "$($My.ESC)[95mÜbersprungen$($My.ESC)[0m"
    $my.IsAdminRights = (New-Object -TypeName Security.Principal.WindowsPrincipal -ArgumentLIST ([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]::AdminISTrator)

    #region Allgemein

    [SicherheitInfoCheck]::new($My.Id++, $My.Neutral, 'Allgemein', 'PSEdition', $PSVersionTable.PSEdition, '')
    "Zu $($My.Id - 1)) Weitere Details siehe about_PowerShell_Editions" | Write-Verbose

    if ($PSVersionTable.PSVersion -le [Version]"2.0.0.0") { $My.Status = $My.Failed } else { $My.Status = $My.Passed }
    [SicherheitInfoCheck]::new($My.Id++ , $My.Status, 'Allgemein', 'PSVersion', $PSVersionTable.PSVersion, '-gt 2.0')
    "Zu $($My.Id - 1)) Schutzmechaniken können in Windows PowerShell 2.0 umgangen werden, daher diese Versionen unter Windows-Feature-/Optionen deaktivieren." | Write-Verbose

    [SicherheitInfoCheck]::new($My.Id++, $My.Neutral, 'Allgemein', 'Platform', $PSVersionTable.Platform, '')

    [SicherheitInfoCheck]::new($My.Id++, $My.Neutral, 'Allgemein', 'CurrentPSHost', (Get-Host | Select-Object -ExpandProperty Name), '')
    "Zu $($My.Id - 1)) weitere PowerShell-Hosts können sein: ConsoleHost, Visual Studio Code Host, Windows PowerShell ISE Host, etc." | Write-Verbose

    if ($ExecutionContext.SessionState.LanguageMode -ieq "FullLanguage") { $My.Status = $My.Failed } else { $My.Status = $My.Passed }
    [SicherheitInfoCheck]::new($My.Id++, $My.Status, 'Allgemein', 'PSSessionLanguageMode', ($ExecutionContext.SessionState.LanguageMode), 'ConstrainedLanguage')
    "Zu $($My.Id - 1)) Weitere Details siehe about_Language_Modes" | Write-Verbose

    if ((Get-ExecutionPolicy) -ine "AllSigned") { $My.Status = $My.Failed } else { $My.Status = $My.Passed }
    [SicherheitInfoCheck]::new($My.Id++, $My.Status, 'Allgemein', 'ExecutionPolicy', (Get-ExecutionPolicy), 'AllSigned')
    "Zu $($My.Id - 1)) *Policy sollte auf AllSigned stehen. Für PSEntwickler empfiehlt sich RemoteSigned" | Write-Verbose

    if ($my.IsAdminRights) { $My.Status = $My.Failed } else { $My.Status = $My.Passed }
    [SicherheitInfoCheck]::new($My.Id++, $My.Status, 'Allgemein', 'Erhöhte Admin-Rechte', $my.IsAdminRights, $false)

    #endregion

    #region PROFILE

    $my.Result = Test-Path -Path $PROFILE.AllUsersAllHosts -PathType Leaf
    if ($My.Result) { $My.Status = $My.Failed } else { $My.Status = $My.Passed }
    [SicherheitInfoCheck]::new($My.Id++, $My.Status, 'Profile-Datei vorhanden', 'AllUsersAllHosts', $My.Result, $false)

    $my.Result = Test-Path -Path $PROFILE.AllUsersCurrentHost -PathType Leaf
    if ($My.Result) { $My.Status = $My.Failed } else { $My.Status = $My.Passed }
    [SicherheitInfoCheck]::new($My.Id++, $My.Status, 'Profile-Datei vorhanden', 'AllUsersCurrentHost', $My.Result, $false)
    "Zu $($My.Id - 1)) Auch PFade in anderen PowerShell-Hosts (s.o.) testen." | Write-Verbose

    $my.Result = Test-Path -Path $PROFILE.CurrentUserAllHosts -PathType Leaf
    if ($My.Result) { $My.Status = $My.Failed } else { $My.Status = $My.Passed }
    [SicherheitInfoCheck]::new($My.Id++, $My.Status, 'Profile-Datei vorhanden', 'CurrentUserAllHosts', $My.Result, $false)

    $my.Result = Test-Path -Path $PROFILE.CurrentUserCurrentHost -PathType Leaf
    if ($My.Result) { $My.Status = $My.Failed } else { $My.Status = $My.Passed }
    [SicherheitInfoCheck]::new($My.Id++, $My.Status, 'Profile-Datei vorhanden', 'CurrentUserCurrentHost', $My.Result, $false)
    "Zu $($My.Id - 1)) Auch PFade in anderen PowerShell-Hosts (s.o.) testen." | Write-Verbose

    #endregion

    #region Microsoft Defender Antivirus

    $my.Result = Get-MpPreference | Select-Object -ExpandProperty ExclusionPath
    if ($null -ne $My.Result) { $My.Status = $My.Failed } else { $My.Status = $My.Passed }
    [SicherheitInfoCheck]::new($My.Id++, $My.Status , 'Defender', 'ExclusionPath', $My.Result, 'No Exclusion Paths')

    #endregion

    #region Module Version Check

    $My.Status = $my.Skip ; $My.Result = "Test übersprungen" ; $My.TargetResult = "Test übersprungen"
    if (-not $SkipModuleVersionTest) {
        $My.Status = $My.Passed ; $My.Result = "unnötig in PowerShell 7" ; $My.TargetResult = "unnötig in PowerShell 7"
        if (-not $PSVersionTable.PSVersion -ge 6) {
            $My.Result = (Get-PackageProvider -Name NuGet).Version
            $My.TargetResult = (Find-PackageProvider -Name NuGet).Version
            if ($My.Result -le $My.TargetResult) { $My.Status = $My.Failed } else { $My.Status = $My.Passed }
        }
    }
    [SicherheitInfoCheck]::new($My.Id++, $My.Status, 'PackageProvider', 'NuGet', $My.Result, $My.TargetResult)
    "Zu $($My.Id - 1)) Nur in Windows PowerShell 5.1 wichtig." | Write-Verbose

    #endregion

    #region Module Version Check

    $My.Status = $my.Skip ; $My.Result = "Test übersprungen" ; $My.TargetResult = "Test übersprungen"
    if (-not $SkipModuleVersionTest) {
        $My.Result = (Get-Module -Name PackageManagement -LISTAvailable -Verbose:$false).Version | Sort-Object -Descending | Select-Object -First 1
        $My.TargetResult = [Version](Find-Module -Name PackageManagement).Version
        if ($My.Result -lt $My.TargetResult) { $My.Status = $My.Failed } else { $My.Status = $My.Passed }
    }
    [SicherheitInfoCheck]::new($My.Id++, $My.Status, 'Module', 'PackageManagement', $My.Result, $My.TargetResult)

    $My.Status = $my.Skip ; $My.Result = "Test übersprungen" ; $My.TargetResult = "Test übersprungen"
    if (-not $SkipModuleVersionTest) {
        $My.Result = (Get-Module -Name PowerShellGet -LISTAvailable -Verbose:$false).Version | Sort-Object -Descending | Select-Object -First 1
        $My.TargetResult = [Version](Find-Module -Name PowerShellGet).Version
        if ($My.Result -lt $My.TargetResult) { $My.Status = $My.Failed } else { $My.Status = $My.Passed }
    }
    [SicherheitInfoCheck]::new($My.Id++, $My.Status, 'Module', 'PowerShellGet', $My.Result, $My.TargetResult)

    $My.Status = $my.Skip ; $My.Result = "Test übersprungen" ; $My.TargetResult = "Test übersprungen"
    if (-not $SkipModuleVersionTest) {
        $My.Result = (Get-Module -Name PSScriptAnalyzer -LISTAvailable -Verbose:$false).Version | Sort-Object -Descending | Select-Object -First 1
        $My.TargetResult = [Version](Find-Module -Name PSScriptAnalyzer).Version
        if ($My.Result -lt $My.TargetResult) { $My.Status = $My.Failed } else { $My.Status = $My.Passed }
    }
    [SicherheitInfoCheck]::new($My.Id++, $My.Status, 'Module', 'PSScriptAnalyzer', $My.Result, $My.TargetResult)

    $My.Status = $my.Skip ; $My.Result = "Test übersprungen" ; $My.TargetResult = "Test übersprungen"
    if (-not $SkipModuleVersionTest) {
        $My.Result = (Get-Module -Name Pester -LISTAvailable -Verbose:$false).Version | Sort-Object -Descending | Select-Object -First 1
        $My.TargetResult = [Version](Find-Module -Name Pester).Version
        if ($My.Result -lt $My.TargetResult) { $My.Status = $My.Failed } else { $My.Status = $My.Passed }
    }
    [SicherheitInfoCheck]::new($My.Id++, $My.Status, 'Module', 'Pester', $My.Result, $My.TargetResult)

    #endregion

    #region ScriptBlockLogging

    $My.Result = Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty 'EnableScriptBlockLogging' -ErrorAction SilentlyContinue
    if ($My.Result -eq 1) { $My.Status = $My.Passed } else { $My.Status = $My.Failed }
    [SicherheitInfoCheck]::new($My.Id++, $My.Status, 'ScriptBlockLogging', 'Windows PowerShell 64bit', $My.Result, 1)
    "Zu $($My.Id - 1)) ScriptBlockLogging sollte inkl. Verschlüsselung aktiviert werden. Für weitere Details siehe about_Logging_Windows." | Write-Verbose
    
    $My.Result = Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty 'EnableScriptBlockLogging' -ErrorAction SilentlyContinue
    if ($My.Result -eq 1) { $My.Status = $My.Passed } else { $My.Status = $My.Failed }
    [SicherheitInfoCheck]::new($My.Id++, $My.Status, 'ScriptBlockLogging', 'Windows PowerShell 32bit', $My.Result, 1)
    "Zu $($My.Id - 1)) ScriptBlockLogging sollte inkl. Verschlüsselung aktiviert werden. Für weitere Details siehe about_Logging_Windows." | Write-Verbose

    $My.Result = Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\PowerShellCore\ScriptBlockLogging' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty 'EnableScriptBlockLogging' -ErrorAction SilentlyContinue
    if ($My.Result -eq 1) { $My.Status = $My.Passed } else { $My.Status = $My.Failed }
    [SicherheitInfoCheck]::new($My.Id++, $My.Status, 'ScriptBlockLogging', 'PowerShell 6+ 64bit', $My.Result, 1)
    "Zu $($My.Id - 1)) ScriptBlockLogging sollte inkl. Verschlüsselung aktiviert werden. Für weitere Details siehe about_Logging_Windows." | Write-Verbose
    
    #endregion

    #region Just Enough Administration

    if($my.IsAdminRights) {
        $My.Result = Get-PSSessionConfiguration -Verbose:$false | Where-Object -Property Name -NotMatch -Value 'PowerShell' | Select-Object -ExpandProperty Name; $sc -join ', '
    } else {
        "Zu $($My.Id - 1)) ACHTUNG! Keine Admin-Rechte zum testen vorhanden. Führe diesen Test erneut mit Admin-Rechten aus." | Write-Warning
        $My.Result = "n/v"
    }
    [SicherheitInfoCheck]::new($My.Id++, $My.Neutral, 'JEA', 'SessionConfigurations', $my.Result, '')
    "Zu $($My.Id - 1)) Das Einrichten von JEA für Nicht-Administratoren/Konten mit administrativen Aufgaben anstreben." | Write-Verbose

    #endregion

    #region Alternate Data Stream

    $My.Status = $My.Skip; $My.Result = "Test übersprungen"
    if (-not $SkipAlternateDataStreamTest) {
        $My.CountFiles = Get-ChildItem -Path 'C:\' -File -Force -ErrorAction Ignore -Recurse | Measure-Object | Select-Object -ExpandProperty Count
        $My.CurrentCounter = 1
        $My.Status = $My.Neutral
        $My.Result = Get-ChildItem -Path c:\ -File -Force -Recurse -ErrorAction Ignore | ForEach-Object -Process {
            Write-Progress -Activity 'Scanne Dateien nach Alternate Data Stream''s' -Status $My.CurrentCounter -PercentComplete ([System.Math]::Floor(($My.CurrentCounter++) / $My.CountFiles * 100))
            try { Get-Item -Path $_.FullName -Stream * -ErrorAction Ignore | Where-Object -Property Stream -ne ':$DATA' } catch {  }
        } | Group-Object -Property Stream | Sort-Object -Descending Count | Select-Object -Property Count, Name, @{Name = 'Sum'; Expression = { ($_.Group | Measure-Object -Property Length -Sum | Select-Object -ExpandProperty SumLengthKB) / 1KB } }, Group
        Write-Progress -Activity 'Scanne Dateien nach Alternate Data Stream''s' -Completed
    }
    [SicherheitInfoCheck]::new($My.Id++, $My.Status, 'Alternate Data Stream', 'Gruppiert IST', $My.Result, 'Zero ADS Strategy')
    "Zu $($My.Id - 1)) Auffällige / Große ADS-Dateien manuell prüfen. Siehe das gruppierte Ergebnis der IST-Eigenschaft." | Write-Verbose

    #endregion

    #region PKI

    $My.Result = Get-ChildItem -Path 'Cert:\' -Recurse -Force | Where-Object -Property 'HasPrivateKey' | ForEach-Object -Process {
        if ((IstZertifikatExportierbar $_)) {
            return "SUBJECT: $($_.Subject) THUMBPRINT: $($_.Thumbprint)"
        }
    }
    if ($null -ne $My.Result) { $My.Status = $My.Failed } else { $My.Status = $My.Passed }
    [SicherheitInfoCheck]::new($My.Id++, $My.Status, 'PKI', 'ExportablePrivateCertificate', $My.Result, 'Zero Strategie')
    "Zu $($My.Id - 1)) Zertifikate mit privatem Schlüssel sollten mit diesem nicht exportierbar sein." | Write-Verbose

    #endregion

    Remove-Variable -Name My -Force -ErrorAction Ignore
}

<# KOMPONENTEN TEST
Update-FormatData -PrependPath '.\Modules\PowerShellBuddy\Public\Test-SecurityState.Format.ps1xml'
Test-SecurityState
Test-SecurityState -Verbose
Test-SecurityState -SkipModuleVersionTest -Verbose
Test-SecurityState -SkipAlternateDataStreamTest -Verbose
Test-SecurityState -SkipModuleVersionTest -SkipAlternateDataStreamTest -Verbose
#>



# SIG # Begin signature block
# MIIcZAYJKoZIhvcNAQcCoIIcVTCCHFECAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDhwSEM/AxufL2d
# CNQo9e//ojGwWff2Rv5rh+eZP/Jc+6CCFn8wggNyMIICWqADAgECAhA8emkJ82H0
# pEx+itPRny0WMA0GCSqGSIb3DQEBDQUAMFExIzAhBgkqhkiG9w0BCQEWFGluZm9A
# YXR0aWxha3JpY2suY29tMSowKAYDVQQDDCFBdHRpbGEgS3JpY2sgKFNvZnR3YXJl
# IERldmVsb3BlcikwHhcNMjIxMDE2MTg1MTA3WhcNMzIxMDE2MTkwMTA3WjBRMSMw
# IQYJKoZIhvcNAQkBFhRpbmZvQGF0dGlsYWtyaWNrLmNvbTEqMCgGA1UEAwwhQXR0
# aWxhIEtyaWNrIChTb2Z0d2FyZSBEZXZlbG9wZXIpMIIBIjANBgkqhkiG9w0BAQEF
# AAOCAQ8AMIIBCgKCAQEAvbn3JNw62vDI7mnX6gD+V/MyDkvFSzclaqC3Kfn+wDxj
# xkmgwho343N/JCxAUjHoBjNDpx4W3KTVv3X7PYrPHxoz/134JTIBdV+yIyL/VkCS
# Mnp9exMhMPxa1RvX3p/zphPMosv7xGgRPF3QVKxoBBUtifql80/pDHvOCPtROZ+9
# xSfXJ6P9jXGH4YSlNJxAKv53AXV0avihpa1BWL6ohyS1Bnjdbcw/hq0TIf4as1Dy
# 7IbrU6Fneaqm/XIEEs11I0BrDNwgXnmB8PSojsm/DOtQY5Ps8eGJzwiXP2Vdvk0j
# OsqDaJe3cxiOCXk6dz2zylB6rfP+Kph97bjaQoNI2QIDAQABo0YwRDAOBgNVHQ8B
# Af8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwHQYDVR0OBBYEFKrSiXRUaupI
# 5orXIniIR5sTcWwIMA0GCSqGSIb3DQEBDQUAA4IBAQAQ+6LPLF6Ua7JCC9LuwOCG
# UlWwawgV/D5wK+FU8PQ+/onwBx9ZBqXnZw4f7NQtqaiR1bwm7sRTZeaIcif57zHM
# /WBicYjB9dwZoGylfeCW6LRNQaoBvjuavOTsQw3lYF//ylJV8QyUKloJd9W6CVtm
# tBSJEYopjygAm/9E9CxRFPSXxX/G+Af0+G+VeeBjSpaYKO688dVXa7pqMOm6hq9P
# Ww6jgiCL3aRk0qbWwl2mt1POcfdRk/rdvt66BcO91cLDKhSCOIp7coJZ8Cf2xHDP
# 99NcZ8ct02Cco6qjtlUkrCBRYNsuUAcToTvqQLiSSkywj+jTI4gm0vcjSiVXwnyy
# MIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv21DiCEAYWjANBgkqhkiG9w0BAQwFADBl
# MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
# d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
# b3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcNMzExMTA5MjM1OTU5WjBiMQswCQYDVQQG
# EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
# cnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0G
# CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7J
# IT3yithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxS
# D1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb
# 7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1ef
# VFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoY
# OAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSa
# M0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI
# 8OCiEhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9L
# BADMfRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfm
# Q6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDr
# McXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15Gkv
# mB0t9dmpsh3lGwIDAQABo4IBOjCCATYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
# FgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wHwYDVR0jBBgwFoAUReuir/SSy4IxLVGL
# p6chnfNtyA8wDgYDVR0PAQH/BAQDAgGGMHkGCCsGAQUFBwEBBG0wazAkBggrBgEF
# BQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAChjdodHRw
# Oi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0Eu
# Y3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9E
# aWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwEQYDVR0gBAowCDAGBgRVHSAAMA0G
# CSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXnOF+go3QbPbYW1/e/Vwe9mqyhhyzshV6p
# Grsi+IcaaVQi7aSId229GhT0E0p6Ly23OO/0/4C5+KH38nLeJLxSA8hO0Cre+i1W
# z/n096wwepqLsl7Uz9FDRJtDIeuWcqFItJnLnU+nBgMTdydE1Od/6Fmo8L8vC6bp
# 8jQ87PcDx4eo0kxAGTVGamlUsLihVo7spNU96LHc/RzY9HdaXFSMb++hUD38dglo
# hJ9vytsgjTVgHAIDyyCwrFigDkBjxZgiwbJZ9VVrzyerbHbObyMt9H5xaiNrIv8S
# uFQtJ37YOtnwtoeW/VvRXKwYw02fc7cBqZ9Xql4o4rmUMIIGrjCCBJagAwIBAgIQ
# BzY3tyRUfNhHrP0oZipeWzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEV
# MBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t
# MSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAw
# MDAwWhcNMzcwMzIyMjM1OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln
# aUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5
# NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
# MIICCgKCAgEAxoY1BkmzwT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYR
# oUQVQl+kiPNo+n3znIkLf50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CE
# iiIY3+vaPcQXf6sZKz5C3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCH
# RgB720RBidx8ald68Dd5n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5K
# fc71ORJn7w6lY2zkpsUdzTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDni
# pUjW8LAxE6lXKZYnLvWHpo9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2
# nuY7W+yB3iIU2YIqx5K/oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp
# 88qqlnNCaJ+2RrOdOqPVA+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1C
# vwWcZklSUPRR8zZJTYsg0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+
# 0wOI/rOP015LdhJRk8mMDDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl2
# 7KtdRnXiYKNYCQEoAA6EVO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOC
# AV0wggFZMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaa
# L3WMaiCPnshvMB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1Ud
# DwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkw
# JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcw
# AoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJv
# b3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQu
# Y29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwB
# BAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+
# ZtbYIULhsBguEE0TzzBTzr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvX
# bYf6hCAlNDFnzbYSlm/EUExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tP
# iix6q4XNQ1/tYLaqT5Fmniye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCy
# Xen/KFSJ8NWKcXZl2szwcqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpF
# yd/EjaDnmPv7pp1yr8THwcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3
# fpNTrDsdCEkPlM05et3/JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t
# 5TRxktcma+Q4c6umAU+9Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejx
# mF/7K9h+8kaddSweJywm228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxah
# ZrrdVcA6KYawmKAr7ZVBtzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAA
# zV3C+dAjfwAL5HYCJtnwZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vup
# L0QVSucTDh3bNzgaoSv27dZ8/DCCBsIwggSqoAMCAQICEAVEr/OUnQg5pr/bP1/l
# YRYwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
# ZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYg
# U0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yMzA3MTQwMDAwMDBaFw0zNDEwMTMy
# MzU5NTlaMEgxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjEg
# MB4GA1UEAxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjMwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQCjU0WHHYOOW6w+VLMj4M+f1+XS512hDgncL0ijl3o7
# Kpxn3GIVWMGpkxGnzaqyat0QKYoeYmNp01icNXG/OpfrlFCPHCDqx5o7L5Zm42nn
# af5bw9YrIBzBl5S0pVCB8s/LB6YwaMqDQtr8fwkklKSCGtpqutg7yl3eGRiF+0Xq
# DWFsnf5xXsQGmjzwxS55DxtmUuPI1j5f2kPThPXQx/ZILV5FdZZ1/t0QoRuDwbjm
# UpW1R9d4KTlr4HhZl+NEK0rVlc7vCBfqgmRN/yPjyobutKQhZHDr1eWg2mOzLukF
# 7qr2JPUdvJscsrdf3/Dudn0xmWVHVZ1KJC+sK5e+n+T9e3M+Mu5SNPvUu+vUoCw0
# m+PebmQZBzcBkQ8ctVHNqkxmg4hoYru8QRt4GW3k2Q/gWEH72LEs4VGvtK0VBhTq
# YggT02kefGRNnQ/fztFejKqrUBXJs8q818Q7aESjpTtC/XN97t0K/3k0EH6mXApY
# TAA+hWl1x4Nk1nXNjxJ2VqUk+tfEayG66B80mC866msBsPf7Kobse1I4qZgJoXGy
# bHGvPrhvltXhEBP+YUcKjP7wtsfVx95sJPC/QoLKoHE9nJKTBLRpcCcNT7e1NtHJ
# XwikcKPsCvERLmTgyyIryvEoEyFJUX4GZtM7vvrrkTjYUQfKlLfiUKHzOtOKg8tA
# ewIDAQABo4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYD
# VR0lAQH/BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZI
# AYb9bAcBMB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQW
# BBSltu8T5+/N0GSh1VapZTGj3tXjSTBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8v
# Y3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2
# VGltZVN0YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcw
# AYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8v
# Y2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hB
# MjU2VGltZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCBGtbeoKm1
# mBe8cI1PijxonNgl/8ss5M3qXSKS7IwiAqm4z4Co2efjxe0mgopxLxjdTrbebNfh
# YJwr7e09SI64a7p8Xb3CYTdoSXej65CqEtcnhfOOHpLawkA4n13IoC4leCWdKgV6
# hCmYtld5j9smViuw86e9NwzYmHZPVrlSwradOKmB521BXIxp0bkrxMZ7z5z6eOKT
# GnaiaXXTUOREEr4gDZ6pRND45Ul3CFohxbTPmJUaVLq5vMFpGbrPFvKDNzRusEEm
# 3d5al08zjdSNd311RaGlWCZqA0Xe2VC1UIyvVr1MxeFGxSjTredDAHDezJieGYkD
# 6tSRN+9NUvPJYCHEVkft2hFLjDLDiOZY4rbbPvlfsELWj+MXkdGqwFXjhr+sJyxB
# 0JozSqg21Llyln6XeThIX8rC3D0y33XWNmdaifj2p8flTzU8AL2+nCpseQHc2kTm
# Ot44OwdeOVj0fHMxVaCAEcsUDH6uvP6k63llqmjWIso765qCNVcoFstp8jKastLY
# OrixRoZruhf9xHdsFWyuq69zOuhJRrfVf8y2OMDY7Bz1tqG4QyzfTkx9HmhwwHcK
# 1ALgXGC7KP845VJa1qwXIiNO9OzTF/tQa/8Hdx9xl0RBybhG02wyfFgvZ0dl5Rtz
# tpn5aywGRu9BHvDwX+Db2a2QgESvgBBBijGCBTswggU3AgEBMGUwUTEjMCEGCSqG
# SIb3DQEJARYUaW5mb0BhdHRpbGFrcmljay5jb20xKjAoBgNVBAMMIUF0dGlsYSBL
# cmljayAoU29mdHdhcmUgRGV2ZWxvcGVyKQIQPHppCfNh9KRMforT0Z8tFjANBglg
# hkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3
# DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEV
# MC8GCSqGSIb3DQEJBDEiBCAVe0GCmQI3HprydSLeoyPgcl+Y4AMci3jMraGdP0UU
# vjANBgkqhkiG9w0BAQEFAASCAQAdqBDt/lV3KghPEmR2oNNtXnZwBW9r/bNkev3U
# YPCzfo/Fe9Dla84sM6hCh/PG2Gycd2nok3DaYrdyLoMxc919wf6YemmEqnkAszZJ
# RJyhRxfqXypPkJch1y48pYRTeWoAWD8Pcu2/QVvZG+opyqQIpVH+G6sBYjRtg4gt
# oBZCy3BMU2vcdv6E/UIYgw6bpAiC5HytswHsfjfjRbvO2Nyk5WjjTD5W7a2Rcw0Q
# kazp4lvVrOQtKDbZquFJ27cbHoARroFiy0hJ5Ay7ioqXQyRSOIKOvqHPL3SpZhjP
# JimRDJFvWAkjjpceqXfJDqtlIO6GhmOlm6NxA2jcPahVeNTEoYIDIDCCAxwGCSqG
# SIb3DQEJBjGCAw0wggMJAgEBMHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRp
# Z2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQw
# OTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQQIQBUSv85SdCDmmv9s/X+VhFjANBglg
# hkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcN
# AQkFMQ8XDTI0MDMwNTIwNDU0MFowLwYJKoZIhvcNAQkEMSIEIMnAsc3mJTEBQ36g
# qPsz1NLr9VtAxsEzRhWziuudCl8lMA0GCSqGSIb3DQEBAQUABIICAGyqPJYYXEmy
# 8qDxVaa9Jw6ZIk4KI9d2cOEXH1zjlMgWGpaDFEZ9H3390scqixhNL56AN+ZcGT/t
# vsBGX+tGdh0hHD5MGZ+GIB1kIqpsADkEqnaoXKODCMiC042TTq8pJelers+M45oT
# XahK9hQ+il/CvzUPDVssQuig5AxEGRoRukBg7ahA7Q6o20FFGIlwrU4xybAbzTh4
# BYIsCggMytKNRK9fiEDR+XAprkgvq6SoJzIZ1F5J3+3Y0fM3sHknpNXpm6eckqUF
# R53uJdwY46846E5HVutwMpKATz8CBuAVCvZG7CTF3J2+44WQf8MEhLkGNQ0vTMUo
# Rpvh6Q0gj6vLGyHTOwklCs61lQG56DZNynoEBK7CF38mLaKqOTa/gk0RpoEPco4U
# 51bkU3jZBoCoEZ+NkyNOmxidc2K7zXACtBNeS1d5dEpD+wC+6iVNjD2xqoA022mx
# E0Dk2jGRUOf40Ao0R/nfQrYFyM8rO2pPzMFoWPsNyBhY87JuPFSwykuBzcUi+W1X
# auTGS2o+WBpIzA4buT/a7CXsyJzyMeanl2B+X8XRlxp02pppwyZVHBFQ+syKDQEk
# O8QFuYvr0WfJGCEdAX+2vgde0fWltqiXd/fKCygN+I3OCV53p3uLBJeTPpfa5AQS
# ZpvIfpu0oZVa9PtPT/lHKiuvKodqrGZ/
# SIG # End signature block