Private/Wissen/C_Advance/C11_Sicherheit.ps1

<#
 
# Sicherheit
 
PowerShell absichern und mit Sicherheit in der PowerShell umgehen
 
- **Hashtags** Sicherheit Security PKI NTFS Security Crypt Decrypt Signature Credentials ActiveDirectory GPO ScriptBlockLogging Transcript CLM PSRC PSSC
- **Version** 2020.05.13
 
#>


# ! EINLEITUNG: Die PowerShell ist im System verwurzelt. Daher sorgt ein generelles Blockieren nur für schein­bare Sicherheit. Den besten Schutz versprechen letztlich die **Schutz­mechanismen von PowerShell** selbst. Neben Mitteln, die den Missbrauch von PowerShell unterbinden, stehen auch solche zur Verfügung, um verdächtigen und unerwünschten Aktivitäten zu **protokollieren** und diese auf die Spur zu kommen.

#region TEIL A :: Sicherheit IN der PowerShell

#region Strings

# ! Grundsätzlich immer die einfachen Hochkommas ' verwenden, es seiden man verwendet Variablen. Erst dann die doppelten Hochkommas " verwenden.
$ort = 'Würzburg'
$s = 'Hallo $ort!'
$s = "Hallo $ort!"

#endregion

#region Für Initial-Passwörter (z.B. beim Zurücksetzen / Neu-Anlage) generieren

# ! Möglichkeit 1:

[System.IO.Path]::GetRandomFileName() -replace "\.", [String]::Empty | Write-Warning

# ! Möglichkeit 2:

(1..100 | Get-Random -Count 3 | ForEach-Object { "{0:00}" -f $_ }) -join [String]::Empty | Write-Warning

# ! Möglichkeit 3:

[Convert]::ToBase64String((0..255 | Get-Random -Count 9)) | Write-Warning

# TODO z.B. praktische Anwendung:

'p.mueller', 'i.schmitt', 'k.bauer', 'e.baecker' | ConvertFrom-Csv -Header 'Username' | ForEach-Object -Process {
    $_ | Add-Member -Name 'PlainTextPassword' -Value ([Convert]::ToBase64String((0..255 | Get-Random -Count 9))) -MemberType NoteProperty
    $_ | Add-Member -Name 'Password' -Value ($_.PlainTextPassword | ConvertTo-SecureString -AsPlainText -Force) -MemberType NoteProperty
    return $_
}

#endregion

#region SecureString

# ? Ein Passwort verschlüsselt aufbewarben und sicher wieder einlesen:

# ! Muss jedoch für jeden PC und Benutzter erneut generiert werden.

# ? 1. Passwort sicher abfragen und verschlüsselt auf C:\ speichern:

Read-Host -Prompt "Passwort eingeben" -AsSecureString | ConvertFrom-SecureString | Out-File -FilePath "C:\temp\SecureString.txt" -Force

Get-ChildItem -Path "C:\Temp\SecureString.txt" | Get-Content | Write-Warning

# ! 2. Verschlüsselt Passwort von C:\ wieder einlesen:

Get-Content -Path "C:\Temp\SecureString.txt" | ConvertTo-SecureString | Write-Warning

# TODO Aufräumen:

Remove-Item -Path "C:\Temp\SecureString.txt" -Force


#endregion

#region Credentials

# ! Unterschiedliche Cmdlets erwarten über einen Parameter Authentifizierungsinformationen (Benutzname & Passwort) in Form eines [PSCredential]-Objektes. Dieses [PSCredential]-Objekt kann per Cmdlet Get-Credential oder manuell erzeugt werden. Der Fokus liegt hierbei auf das Password das als SecureString-Objekt verschlüsselt im Arbeitsspeicher abgelegt ist:

$cred = Get-Credential -Message "Zugangsdaten für Administrator auf 127.0.0.1" -UserName "Administrator"

Enter-PSSession -ComputerName "127.0.0.1" -Credential $cred

# ! Credentials können auch verschlüsselt im Dateisystem gespeichert werden um diese später wieder einzulesen:

# ! 1. verschlüsselt speichern:

$username = Read-Host -Prompt "Benutzername eingaben"
$password = Read-Host -Prompt "Passwort für $username eingeben" -AsSecureString
[PSCustomObject]@{
    Username = $username
    Password = $password | ConvertFrom-SecureString
} | ConvertTo-Json | Set-Content -Path "C:\Temp\Credential_Admin.json" -Force

# ! 2. Verschlüsselte Daten laden, und daraus ein Credential-Objekt Instanziieren:

$admin = Get-Content -Path C:\Temp\Credential_Admin.json | ConvertFrom-Json | Select-Object Username, @{Label="Password"; Expression={$_.Password | ConvertTo-SecureString}}
$cred = [System.Management.Automation.PSCredential]::new($admin.Username, $admin.Password)

$cred

Enter-PSSession -ComputerName "127.0.0.1" -Credential $cred

# TODO Aufräumen:

Remove-Item -Path "C:\Temp\Credential_Admin.json" -Force

#endregion

#region Zeichenketten verschlüsseln

# READ betroffene Cmdlets:

Get-Command -Noun "CmsMessage" -Module "Microsoft.PowerShell.Security"

# READ CipherNet Lite => Funktionsbibliothek zum Thema Verschlüsselung

# ! 1. Vorbereitung:

using namespace "Microsoft.CertificateServices.Commands"

$params = @{
    FriendlyName      = "Superman"
    Subject           = "CN=_Superman (Doctor S. Man), E=s.man@krypton.universe"
    HashAlgorithm     = "SHA512"
    KeyExportPolicy   = [KeyExportPolicy]::ExportableEncrypted
    KeyLength         = 4096
    KeySpec           = [KeySpec]::KeyExchange
    CertStoreLocation = "Cert:\CurrentUser\My"
    Type              = [CertificateType]::DocumentEncryptionCert
    NotAfter          = (Get-Date).AddMinutes(60)
}
$myPfxCert = New-SelfSignedCertificate @params

# ! 2. Verschlüsseln:

$cryptedText = "Hallo Würzburg!" | Protect-CmsMessage -To $myPfxCert.Thumbprint

# ! 3. Chiffre checken:

$cryptedText | Get-CmsMessage

# ! 4. Entschlüsseln

$cryptedText | Unprotect-CmsMessage -To $myPfxCert.Thumbprint | Write-Warning

# TODO Aufräumen:

Get-ChildItem -Path "Cert:\CurrentUser\My" | Where-Object -Property "Subject" -CLike -Value "CN=_Superman (Doctor S. Man), E=s.man@krypton.universe" | Remove-Item -Force

#endregion

#region Credentials sicher persistent speichern

# ! 'Microsoft.PowerShell.SecretManagement'-Module installieren:
Find-Module -Name 'Microsoft.PowerShell.SecretManagement' -Repository "PSGallery-Alternative" -AllowPrerelease | Install-Module -Scope "CurrentUser" -SkipPublisherCheck -AllowPrerelease -Force

# ! Credential persistent speichern:
Set-Secret -Name "Administrator" -Secret (Get-Credential) -Vault "BuiltInLocalVault"

# ! Credential abrufen und verwenden:
$Credential = Get-Secret -Name "Administrator" -Vault "BuiltInLocalVault"

Start-Process -FilePath 'notepad.exe' -Credential $Credential

Get-Process 'notepad' -IncludeUserName

# TODO Aufräumen:

Get-Process 'notepad' | Stop-Process -Force
Remove-Secret -Name "Administrator" -Vault "BuiltInLocalVault"
Remove-Module -Name "Microsoft.PowerShell.SecretManagement" -Force
Uninstall-Module -Name "Microsoft.PowerShell.SecretManagement" -AllVersions -AllowPrerelease -Force

#endregion

#region Skripte die Admin-Berechtigungen benötigen

# ! WICHTIG :: Skripte die erhöhte Admin-Berechtigungen benötigen, immer zu beginn prüfen ob diese vorliegen. Wenn nicht is es immer besser das Skript bricht zu beginn ab, als mitten drin.

$IsElevatedAdminRights = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]"Administrator")
if(-not $IsElevatedAdminRights) {
    throw "Vorgang abgebrochen! Es werden erhöhte administrative Berechtigungen benötigt!"
} else {
    "Dieses Skript läuft mit erhöhten Admin-Berechtigungen!" | Write-Warning
}

#endregion

#region PKI

# READ betroffene Cmdlets:

Get-Command -Name '*' -Module 'PKI'

# ! Zertifikat erstellen:

using namespace "Microsoft.CertificateServices.Commands"

$params = @{
    FriendlyName                = "Superman"
    Subject                     = "CN=_Superman (Doctor S. Man), E=s.man@krypton.universe"
    HashAlgorithm               = "SHA512"
    KeyAlgorithm                = "RSA"
    KeyLength                   = 4096
    KeyExportPolicy             = [KeyExportPolicy]::ExportableEncrypted
    KeySpec                     = [KeySpec]::KeyExchange
    CertStoreLocation           = "Cert:\CurrentUser\My"
    Type                        = [CertificateType]::Custom
    TextExtension               = @("2.5.29.37={text}1.3.6.1.5.5.7.3.1,1.3.6.1.5.5.7.3.2,1.3.6.1.5.5.7.3.3,1.3.6.1.5.5.7.3.4,1.3.6.1.5.5.7.3.5,1.3.6.1.5.5.7.3.6,1.3.6.1.5.5.7.3.7,1.3.6.1.5.5.7.3.8,1.3.6.1.5.5.7.3.9,1.3.6.1.4.1.311.80.1,1.3.6.1.4.1.311.10.3.4,1.3.6.1.4.1.311.10.3.4.1", `
                                    "2.5.29.17={text}email=s.man@krypton.universe&upn=s.man@krypton.universe")
    NotAfter                    = (Get-Date).AddMinutes(60)
    AlternateSignatureAlgorithm = $true
}
$myPfxCert = New-SelfSignedCertificate @params

# ! Zertifikat exportieren:

$myPfxCert | Export-PfxCertificate -Password (Read-Host -Prompt "Set PFX-Password for Superman" -AsSecureString) -FilePath "C:\Temp\Superman.pfx"

$myPfxCert | Export-Certificate -Type "CERT" -Force -FilePath "C:\Temp\Superman.cer"

# ! Zertifikat im Zertifikats-Speicher löschen:

Get-ChildItem -Path "Cert:\CurrentUser" -Recurse | Where-Object -Property "Thumbprint" -CEQ $myPfxCert.Thumbprint | Remove-Item -Force -Recurse

# ! Zertifikat importieren:

Import-Certificate -FilePath "C:\Temp\Superman.cer" -CertStoreLocation "Cert:\LocalMachine\Root"

Import-PfxCertificate -FilePath "C:\Temp\Superman.pfx" -CertStoreLocation "Cert:\CurrentUser\My" -Password (Read-Host -Prompt "PFX-Password for Superman" -AsSecureString)

# ! Zertifikat lesen und auf Verwendungszweck testen:

Get-PfxData -Password (Read-Host -Prompt "PFX-Password for Superman" -AsSecureString) -FilePath "C:\Temp\Superman.pfx" | Format-List -Property "*"

$test = Get-PfxCertificate -FilePath "C:\Temp\Superman.pfx"

Test-Certificate -Cert $test -Policy "SSL" | Write-Warning

Test-Certificate -Cert $test -EKU "1.3.6.1.5.5.7.3.1" | Write-Warning # = Server Authentication

# ! ODER über den Cert-Speicher:

Get-ChildItem -Path 'Cert:\CurrentUser\My' | Where-Object -Property "Subject" -CLike -Value "CN=_Superman (Doctor S. Man), E=s.man@krypton.universe" | Test-Certificate -Policy "SSL" | Write-Warning

Get-ChildItem -Path 'Cert:\LocalMachine\Root' | Where-Object -Property "Subject" -CLike -Value "CN=_Superman (Doctor S. Man), E=s.man@krypton.universe" | Test-Certificate -EKU "1.3.6.1.5.5.7.3.1" | Write-Warning

# TODO Aufräumen:

Remove-Item -Path "C:\Temp\Superman.*" -Force

Get-ChildItem -Path "Cert:\LocalMachine\", "Cert:\CurrentUser\" -Recurse | Where-Object -Property "Subject" -CLike -Value "CN=_Superman (Doctor S. Man), E=s.man@krypton.universe" | Remove-Item -Force

#endregion

#region Umgang mit Zugriffsberechtigungen

# ! Die PowerShell-Board-Mittel sind leider noch überschaubar:

Get-Command -Name "Get-Acl", "Set-Acl", "ConvertFrom-SddlString"

# ? Berechtigungen von Ordner-A auf Ordner-B übetragen:

New-Item -Path "C:\" -Name "TempTest" -ItemType "Directory" -Force
Get-Acl -Path "C:\Temp" | Select-Object -Property "*"
Get-Acl -Path "C:\Temp" | Set-Acl -Path "C:\TempTest"

# ? Berechtigungen von ACL-Objekte auswerten:

Get-Acl -Path $env:USERPROFILE | Select-Object -ExpandProperty "Sddl" | ConvertFrom-SddlString

# ! ... Umfassende/aufwendige Möglichkeiten ergeben sich mit .NET oder Sie schauen Sich das Module NtfsSecurity aus der PowerShallGallery einmal genauer an:

Install-Module -Name "NTFSSecurity" -Scope "CurrentUser" -SkipPublisherCheck -Force -PassThru -Verbose
Get-Command -Module "NTFSSecurity"
Get-NTFSAccess -Path "C:\Temp"

# TODO Aufräumen:

Remove-Module -Name "NTFSSecurity" -Force
Uninstall-Module -Name "NTFSSecurity" -AllVersions -Force
Remove-Item -Path "C:\TempTest" -Recurse -Force

#endregion

#endregion

#region TEIL B :: Sicherheit AN der PowerShell

# READ Vergleich Sicherheitsmechanismen div. Script-Sprachen https://devblogs.microsoft.com/powershell/a-comparison-of-shell-and-scripting-language-security/

#region Windows PowerShell 2

# !!! UNBEDINGT sollten Sie PowerShell 2.0 ENTFERNEN. Mit dieser alten Shell-Version lassen sich alle wesentlichen Restriktionen für PowerShell unterlaufen. Das optionale Feature ist vorinstalliert und kann aber ab Windows 8.1 und Server 2012 deinstalliert werden. !!!

#endregion

#region Ausführungsrichtlinien konfigurieren

# ! ACHTUNG :: Das Verändern der Ausführungsrichtlinien stellt NUR einen Schutz vor dem unbedarften Ausführen von Skripts dar - MEHR NICHT!

# READ betroffene about-Seiten:

Get-Help -Name "about_Execution_Policy" -ShowWindow

# ? Wie ist der aktuelle Status der Ausführungsrichtlinien:

Get-ExecutionPolicy -List

# ? Wie ist die Ausführungsrichtlinie für den aktuellen Script-Host eingestellt:

$env:PSExecutionPolicyPreference

# ? Empfohlene Einstellung für Client und Server:

Set-ExecutionPolicy -ExecutionPolicy "AllSigned" -Force
# ! D.h.: Alle ausführbare Dateien müssen mit einem x.509-Zertifikat signiert sein!

# ? Empfohlene Einstellung für Test-/Entwicklungszwecke:

Set-ExecutionPolicy -ExecutionPolicy 'RemoteSigned' -Force
# ! D.h.: Alle ausführbare Dateien müssen mit einem x.509-Zertifikat signiert sein, die Remote (Download, UNC, etc.) auf den PC gelangt sind. Diese werden Dateien an einem 'Download-Marker' identifiziert der mit dem Cmdlet 'Unblock-File' wieder entfernt werden kann.

# TODO :: Für das automatische Ausführen von signierten Skripten müssen folgende Voraussetzungen erfüllt sein: 1. Das X.509-Zertifikat muss von einer "Vertrauenswürdigen Stammzertifizierungsstelle" (Root) abstammt ODER es ist selbst in Root abgelegt! Und 2. das X.509-Zertifikat muss im Zertifikatspeicher für "Vertrauenswürdige Herausgeber" (TrustPublisher) enthalten ist.

# TODO :: Die Ausführungsrichtlinien können komfortable per GPO eingestellt werden, Diese wiederum manipulieren folgende Registry-Schlüssel:

Get-ItemPropertyValue -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell" -Name "ExecutionPolicy"
Get-ItemPropertyValue -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\ScriptedDiagnostics"  -Name "ExecutionPolicy"
Get-ItemPropertyValue -Path "Registry::HKEY_CURRENT_USER\Software\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell"  -Name "ExecutionPolicy"

#endregion

#region PowerShell-Scripte (.PS1, .PSM1, ...) signieren

# TODO :: Für das automatische Ausführen von signierten Skripten müssen folgende Voraussetzungen erfüllt sein: 1. Das X.509-Zertifikat muss von einer "Vertrauenswürdigen Stammzertifizierungsstelle" (Root) abstammt ODER es ist selbst in Root abgelegt! Und 2. das X.509-Zertifikat muss im Zertifikatspeicher für "Vertrauenswürdige Herausgeber" (TrustPublisher) enthalten ist.

Import-WinModule -Name 'PKI'

Import-Certificate -FilePath '.\Z_Superman.cer' -CertStoreLocation "Cert:\LocalMachine\Root"

Import-Certificate -FilePath '.\Z_Superman.cer' -CertStoreLocation "Cert:\LocalMachine\TrustedPublisher"

# TODO Test-Umgebung erzeugen:

Set-ExecutionPolicy -ExecutionPolicy "AllSigned" -Scope "Process"

"' > > > The Power of PowerShell-Power < < < ' | Write-Warning" | Set-Content -Path "C:\Temp\Superman_Test.ps1" -Force

& 'C:\Temp\Superman_Test.ps1'

# TODO .PS1-Datei signieren:

$params = @{
    Certificate     = (Get-PfxCertificate -FilePath '.\Z_Superman.pfx') # Password: P@ssw0rd
    FilePath        = 'C:\Temp\Superman_Test.ps1'
    Force           = $true
    IncludeChain    = 'all'
    HashAlgorithm   = 'SHA512'
    TimestampServer = 'http://timestamp.globalsign.com/scripts/timstamp.dll'
}
Set-AuthenticodeSignature @params

# TODO Signierte Datei validieren:

'C:\Temp\Superman_Test.ps1' | Get-AuthenticodeSignature | Select-Object -Property *

& 'C:\Temp\Superman_Test.ps1'

# TODO Aufräumen:

Set-ExecutionPolicy -ExecutionPolicy 'RemoteSigned' -Scope 'Process' -Force

Get-ChildItem -Path 'Cert:\LocalMachine\', 'Cert:\CurrentUser\' -Recurse | Where-Object -Property 'Thumbprint' -CEQ -Value '76607F936AB133FEA29630B2843FE3B3EBE5F41F' | Remove-Item -Force

Get-ChildItem -Path 'C:\temp\Superman_Test.ps1' | Remove-Item -Force

#endregion

#region Permanentes und sicheres Protokollieren sämtlicher Host's aktivieren (ScriptBlockLogging)

# TODO Betrifft PowerShell 7 :: Bevor Ereignisse in das Ereignisprotokoll geschrieben werden können, muss im Gegensatz zu Linux oder macOS in Windows der Ereignisanbieter registriert sein. Führen Sie dazu mit erhöhten Berechtigungen folgendes Script aus:

. "$PsHome\RegisterManifest.ps1"
Get-WinEvent -ListProvider *powershell* | Select-Object -Property Name

# TODO per GP aktivieren unter: Computerkonfiguration / Administrative Vorlagen / Windows-Komponenten / Windows PowerShell / Protokollierung von PowerShell-Scriptblöcken aktivieren. ACHTUNG z.Zt. nur für Windows PowerShell, andere Versionen nur per Registry-Key.

# ! Für PowerShell 7 und PowerShell Core:
New-Item -Path 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\PowerShellCore' -Name 'ScriptBlockLogging' -ItemType 'Key' -Force | New-ItemProperty -Name 'EnableScriptBlockLogging' -Value 1 -PropertyType 'DWord' -Force

# ! Für Windows PowerShell:
New-Item -Path 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell' -Name 'ScriptBlockLogging' -ItemType 'Key' -Force | New-ItemProperty -Name 'EnableScriptBlockLogging' -Value 1 -PropertyType 'DWord' -Force

# ! Für Windows PowerShell 32it: Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging

# ! Für PowerShell Core 32bit: Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Policies\Microsoft\Windows\PowerShellCore\ScriptBlockLogging

# TODO Windows Log auswerten:

# Test-Code:
Get-ChildItem -Path 'C:\Temp'

Get-WinEvent -ListLog '*powershell*'

Get-WinEvent -FilterHashtable @{ LogName='PowerShellCore/Operational' ; Id="4104" } | Select-Object -Property 'RecordId', 'ProcessId', 'MachineName', 'UserId', 'TimeCreated', 'Message' | Where-Object -Property 'Message' -IMatch -Value 'Get-ChildItem'

# TODO Um sensible Daten zu schützen sollten diese Log's verschlüsselt werden (s. Protected Event Logging):
Get-Help -Name "about_logging_windows" -ShowWindow

# TODO Aufräumen:

. "$PsHome\RegisterManifest.ps1" -Unregister

Remove-Item -Path 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\PowerShellCore\ScriptBlockLogging' -Recurse -Force

Remove-Item -Path 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging' -Recurse -Force

#endregion

#region Eine PowerShell-Session einschränken (RestrictedLanguage)

# READ siehe auch:
Get-Help -Name "about_RestrictedLanguage" -ShowWindow
Get-Help -Name "about_FullLanguage" -ShowWindow
Get-Help -Name "about_Language_Modes" -ShowWindow

# ! z.B. per Autostart über eine der folgenden Dateien:

$profile.AllUsersAllHosts
$profile.AllUsersCurrentHost

# ! 1. Keine Anwendungen und Skripte ab jetzt erlauben:

$ExecutionContext.SessionState.Applications.Clear()
$ExecutionContext.SessionState.Scripts.Clear()

# ! 2. Erlaubte Cmdlets festlegen und die restlichen Cmdlets unsichtbar machen:

$allowedCommands = @()
$allowedCommands += "ForEach-Object"
$allowedCommands += "Get-ChildItem"
$allowedCommands += "Get-Command"
$allowedCommands += "Get-Member"
$allowedCommands += "Get-Module"
$allowedCommands += "Out-Default"
$allowedCommands += "Select-Object"
$allowedCommands += "Test-NetConnection"
$allowedCommands += "Where-Object"
Get-Command -CommandType "Cmdlet", "Function" |  Where-Object -Property Name -NotIn -Value $allowedCommands | ForEach-Object -Process { $_.Visibility = "Private" }

# ! 3. In den beschränkten Sprachmodus wechseln:

$ExecutionContext.SessionState.LanguageMode = "RestrictedLanguage"

#endregion

#region Aktivieren des eingeschränkten PowerShell-Sprachmodus (Constrained Language Mode (CLM))

# ! Zu den wichtigsten Sicherheits­mechanismen gehört der 'Constrained Language Mode' der mehrere gefährliche PowerShell-Features deaktiviert und so deren Missbrauch minimiert.

# READ
Get-Help -Name "about_ConstrainedLanguage" -ShowWindow

# ? Eine automatische Erkennung einer Ausführungsbeschränkung (Constrained Language Mode) aktivieren:

# Seit PowerShell 5 wird automatisch erkannt ob in den 'Constrained Language Mode' gewechselt werden soll. Die PowerShell legt dazu unter $env:TEMP temporär ein Script an und versucht dieses auszuführen. Wird die PowerShell bei der Ausführung gehindert, startet sie im eingeschränkten Sprach­modus. Dazu müssen Sie lediglich das Ausführen von Skripten im Ordner $env:TEMP verbieten.

# TODO ... Realisieren können Sie das per Softwareeinschränkung (GPO) unter:

# ! Computerkonfiguration / Windows-Einstellungen / Sicherheitseinstellungen / Richtlinien für Softwareeinschränkung / Zusätzliche Regeln

# TODO Test-Umgebung: Neu Pfad-Regel erstellen für C:\Users\Attila\AppData\Local\Temp :
gpedit.msc

# oder per AppLocker unter:

# ! Computerkonfiguration / Windows-Einstellungen / Sicherheitseinstellungen / Anwendungssteuerungsrichtlinien / AppLocker / Skriptregeln

#endregion

#region PowerShell-Remoting absichern und trotzdem Admin-Aufgaben an Nicht-Admins delegieren mit Just Enough Administration (JEA)

# TODO Nötig für PowerShell 7:
Import-WinModule -Name 'NetAdapter'
Import-WinModule -Name 'NetConnection'
Import-WinModule -Name 'Microsoft.PowerShell.LocalAccounts'

#region 1. Voraussetzungen prüfen

# READ Voraussetzungen: https://docs.microsoft.com/de-de/powershell/scripting/learn/remoting/jea/prerequisites?view=powershell-7

# ? PowerShell-Version:

$PSVersionTable.PSVersion -ge [Version]"5.0.0.0"

# ? OS-Version:

Get-ComputerInfo | ForEach-Object {  if(($_.OsName -IMatch 'Windows 10' -and $_.WindowsVersion -ge 1607) -or $_.OsName -IMatch 'Windows Server 2016' -or $_.OsName -imatch 'Windows Server 2019') { $true } else { $false } }

# ? PS Remoting aktiviert:

try { Invoke-Command -ComputerName 'localhost' -ScriptBlock { 1 | Out-Null } -Credential (Get-Credential) -ErrorAction Stop ; $true } catch { $false }

#endregion

#region 2. Vorbereitungen treffen

# TODO Remoting auf Bediener-PC und Wartungs-Server aktivieren:

Get-NetAdapter -Physical | Set-NetConnectionProfile -NetworkCategory 'Private' -PassThru
Enable-PSRemoting -Force
Set-Item -Path 'WSMan:\localhost\Client\TrustedHosts' -Value '*' -Force

# !TODO Auf dem Wartungs-Server bzw. AD-Domäne Nicht-Admin-Benutzer und -Gruppe festlegen / erstellen die Verwaltungsaufgaben erledigen müssen:

$NewUser = @{
    Name                     = 'DruckerHorst'
    AccountExpires           = (Get-Date).AddHours(24)
    Description              = "24h Demo-Drucker-Managers ohne Admin-Rechte."
    Password                 = (Read-Host -Prompt "Passwort für DruckerHorst" -AsSecureString)
    PasswordNeverExpires     = $true
    UserMayNotChangePassword = $true
}
New-LocalUser @NewUser
New-LocalGroup -Name 'LocalPrinterManagerGroup' -Description 'JEA-Demo: DruckerManager-Gruppe für nicht Admins'
Add-LocalGroupMember -Group 'LocalPrinterManagerGroup' -Member 'DruckerHorst'

#endregion

#region 3. Module-Ordner erstellen

# TODO Den JEA-PowerShell-Module-Ordner auf dem Wartungsserver erstellen in dem später die Rollenfunktionsdateien (Role Capabilities) gesucht werden:

# ! WICHTIG :: Den folgenden Module-Ordner muss vor Manipulation geschützt werden (ACL):

# Für PowerShell 7:
$ModulePath = "$env:ProgramFiles\PowerShell\Modules\AbcFirma"

# Für Windows PowerShell:
$ModulePath = "$env:ProgramFiles\WindowsPowerShell\Modules\AbcFirma" 

New-Item -Path $ModulePath -ItemType 'Directory'

New-ModuleManifest -Path "$ModulePath\AbcFirma.psd1" -RootModule 'AbcFirma'

#endregion

#region 4. Rollenfunktions-Datei erstellen

# READ JEA-Rollenfunktionen https://docs.microsoft.com/de-de/powershell/scripting/learn/remoting/jea/role-capabilities?view=powershell-7#create-a-role-capability-file

# TODO In dieser/n Rollenfunktionsdatei(en) (PowerShell Role Capability File (.PSRC)) legen Sie fest welche Rolle was auf dem Wartungs-Server machen darf:

New-Item -Path "$ModulePath\RoleCapabilities" -ItemType 'Directory'
$Parameter = @{
    Path                = "$ModulePath\RoleCapabilities\PrinterManagerRoleCapability.psrc"
    Author              = 'Attila Krick'
    CompanyName         = 'ATTILAKRICK.COM'
    Description         = 'Benötigte Tools für Drucker-Manager.'
    ModulesToImport     = 'Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Core', 'Microsoft.PowerShell.Utility'
    VisibleProviders    = 'FileSystem', 'Environment', 'Registry'
    VisibleCmdlets      = 'Clear-Host', 'Exit-PSSession', 'Get-Command', 'Get-ChildItem', 'Get-FormatData', 'Get-Help', 'Measure-Object', 'Out-Default', 'Out-String', 'Select-Object', `
                          @{ Name = 'Restart-Computer'; Parameters  = @{ Name = 'ComputerName' ; ValidatePattern = 'VDI\d+'         } }, `
                          @{ Name = 'Restart-Service' ; Parameters  = @{ Name = 'Name'         ; ValidateSet     = 'Dns', 'Spooler' } }
    FunctionDefinitions = @{ Name = 'Get-UserInfo'    ; ScriptBlock = { $PSSenderInfo }                                               }
    VisibleExternalCommands = 'C:\Windows\System32\WhoAmI.exe'
}
New-PSRoleCapabilityFile @Parameter 

# TODO Kontrolle:

Start-Process -FilePath "$ModulePath\RoleCapabilities\PrinterManagerRoleCapability.psrc"

#endregion

#region 5. Sitzungskonfigurations-Datei erstellen

# READ: Get-Help -Name "about_Session_Configuration_Files" -ShowWindow
# READ JEA-Sitzungskonfigurationen https://docs.microsoft.com/de-de/powershell/scripting/learn/remoting/jea/session-configurations?view=powershell-7

# TODO In dieser Sitzungskonfigurations-Datei (PowerShell Session Configuration File (.PSSC)) wird die Nicht-Admin-Gruppe(n) mit der entsprechenden Rolle verknüpft:

New-Item -Path "$env:ProgramData\JEASessionConfigurations" -ItemType 'Directory'
$JEAConfigParams = @{
    Path                = "$env:ProgramData\JEASessionConfigurations\JEAPrintServerSession.pssc"
    Author              = 'Attila Krick'
    Description         = 'Verbindet die Rollen (u.a. Drucker-Manager) mit dieser Session (JEAPrintServerSession)'
    CompanyName         = 'ATTILAKRICK.COM'
    SessionType         = "Default" # ! PROBLEM: "RestrictedRemoteServer"
    RunAsVirtualAccount = $true
    TranscriptDirectory = "$env:ProgramData\JEASessionConfigurations\Transcripts"
    RoleDefinitions     = @{ 'LocalPrinterManagerGroup' = @{RoleCapabilities = 'PrinterManagerRoleCapability'}}
    Full                = $true
}
New-PSSessionConfigurationFile @JEAConfigParams

# ! Kontrolle:

Test-PSSessionConfigurationFile -Path "$env:ProgramData\JEASessionConfigurations\JEAPrintServerSession.pssc"
Start-Process -FilePath "$env:ProgramData\JEASessionConfigurations\JEAPrintServerSession.pssc"

#endregion

#region 6. Die Sitzungskonfiguration registrieren und etablieren

# READ Registrieren von JEA-Konfigurationen https://docs.microsoft.com/de-de/powershell/scripting/learn/remoting/jea/register-jea?view=powershell-7

Register-PSSessionConfiguration -Name 'JEAPrintServerSession' -Path "$env:ProgramData\JEASessionConfigurations\JEAPrintServerSession.pssc" -Force
Restart-Service WinRM

# ! Kontrolle:

# Übersicht der registrierten Sitzungskonfigurationen:
Get-PSSessionConfiguration | Select-Object Name, Permission

#endregion

#region 7. JEA als Bediener/Verwalter verwenden

# READ Verwenden von JEA https://docs.microsoft.com/de-de/powershell/scripting/learn/remoting/jea/using-jea?view=powershell-7

# TODO 1. Per JEA sich mit dem zu verwalten Server als Nicht-Admin per Remote verbinden:

$cred = Get-Credential -UserName 'DruckerHorst' -Message 'Credential für localhost'

 # ! Administrator-Remote-Verbindung ist NICHT erlaubt:
Enter-PSSession -ComputerName 'localhost' -Credential $cred

 # * JEA-Remote-Verbindung ist ERLAUBT:
Enter-PSSession -ComputerName 'localhost' -ConfigurationName 'JEAPrintServerSession' -Credential $cred

# ! 2. Eine Übersicht der Rechte für Benutzer X:

Get-PSSessionCapability -ConfigurationName "JEAPrintServerSession" -Username "DruckerHorst" -Full

# ! 3. JEA testen:

C:\Windows\System32\WhoAmI.exe           # * Erlaubt
Get-ChildItem -PAth Hkcu:\               # * Erlaubt
Restart-Service -Name Spooler            # * Erlaubt
Restart-Service -Name AudioSrv           # ! Nicht erlaubt
Restart-Computer -ComputerName localhost # ! Nicht erlaubt
Restart-Computer -ComputerName VDI009    # * Erlaubt
Get-UserInfo                             # * Erlaubt
Get-Command                              # * Erlaubt
exit                                     # * Erlaubt

#endregion

#region 8. Aufgezeigt Konfiguration wieder rückgängig machen und sämtliche Spuren beseitigen

Unregister-PSSessionConfiguration -Name 'JEAPrintServerSession'
Restart-Service -Name 'WinRM'
Remove-Item -Path "$env:ProgramData\JEASessionConfigurations\JEAPrintServerSession.pssc" -Force
Remove-Item -Path "$env:ProgramFiles\PowerShell\Modules\AbcFirma" -Recurse -Force
Remove-Item -Path "$env:ProgramFiles\WindowsPowerShell\Modules\AbcFirma" -Recurse -Force
Remove-LocalGroup -Name 'LocalPrinterManagerGroup'
Remove-LocalUser -Name 'DruckerHorst'
Set-Item -Path "WSMan:\localhost\Client\TrustedHosts" -Value ([String]::Empty) -Force
Disable-PSRemoting -Force
Get-NetAdapter -Physical | Set-NetConnectionProfile -NetworkCategory "Public"

#endregion

#endregion

#region PowerShell per GPO in der ActiveDirectory-Domäne absichern

<#
 
! EdgeUI.admx
    ? Prevent users from replacing the Command Prompt with Windows PowerShell in the menu they see when they right-click the lower-left corner or press the Windows logo key+X
    User - Windows Components\Edge UI
    HKCU\Software\Policies\Microsoft\Windows\EdgeUI!ShowCommandPromptOnWinX
 
! PowerShellExecutionPolicy.admx
    ? Turn on PowerShell Script Block Logging
    Machine - Windows Components\Windows PowerShell
    HKLM\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging!EnableScriptBlockLogging
    HKLM\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging!EnableScriptBlockInvocationLogging
 
    ? Turn on PowerShell Script Block Logging
    User - Windows Components\Windows PowerShell
    HKCU\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging!EnableScriptBlockLogging
    HKCU\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging!EnableScriptBlockInvocationLogging
 
    ? Turn on PowerShell Transcription
    Machine - Windows Components\Windows PowerShell
    HKLM\Software\Policies\Microsoft\Windows\PowerShell\Transcription!EnableTranscripting
    HKLM\Software\Policies\Microsoft\Windows\PowerShell\Transcription!OutputDirectory
    HKLM\Software\Policies\Microsoft\Windows\PowerShell\Transcription!EnableInvocationHeader
 
    ? Turn on PowerShell Transcription
    User - Windows Components\Windows PowerShell
    HKCU\Software\Policies\Microsoft\Windows\PowerShell\Transcription!EnableTranscripting
    HKCU\Software\Policies\Microsoft\Windows\PowerShell\Transcription!OutputDirectory
    HKCU\Software\Policies\Microsoft\Windows\PowerShell\Transcription!EnableInvocationHeader
#>


#endregion

#endregion