PowerShell Sicherheit in 6 Schritten produktiv absichern
PowerShell Sicherheit - Die Keywords
PowerShell Sicherheit in 6 Schritten produktiv absichern

Überblick

Die PowerShell ist ein Bestandteil des Betriebssystems. Daher sorgt ein generelles Blockieren nur für scheinbare PowerShell Sicherheit. Den besten Schutz versprechen die Schutz- und Protokollierungs-Mechanismen von PowerShell selbst.

In diesem Artikel beleuchte wir das Thema PowerShell Sicherheit aus dem Blickwinkel eines Administrators. Wie PowerShell produktiv abgesichert wird, um trotzdem administrative Aufgaben an Nicht-Administratoren zu delegieren (Just Enough Administration). Auch geht es darum verdächtige und unerwünschte Aktivitäten zu protokollieren (ScriptBlockLogging).

TIPP - Neben der administrativen Absicherung, um die PowerShell Sicherheit zu erhöhen, sollten Sie auch mit sensiblen Daten wie Zugangsdaten, Geheimnisse und Zugangsbeschränkungen entsprechend umgehen und schützen. Wie Sie das in PowerShell realisieren, finden Sie im Artikel "Mit Sicherheit in der PowerShell umgehen".
INTERESSANT - Eine gründliche Analyse der Sicherheittechnologie zwischen verfügbaren Shells und Skriptsprachen finden Sie in dem Artikel Ein Vergleich der Sicherheit von Shell und Skriptsprache.

Unbedingt sollten Sie PowerShell 2.0 deinstallieren. 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.

Statische Strings erhöht die Sicherheit! Grundsätzlich immer die einfachen Hochkommas ' verwenden. Es seiden man verwendet Variable im String, erst dann die doppelten Hochkommas " verwenden. Das erhöht die Sicherheit da die Gefahr von String-Injection minimiert wird.

# ! FALSCH
$Ort = "Würzburg"

# RICHTIG
$Ort = 'Würzburg'

$s = 'Hallo $Ort!'
# vs.
$s = "Hallo $Ort!"

Ausführungsrichtlinien korrekt konfigurieren

Die Ausführungsrichtlinie (ExecutionPolicy) ist ein PowerShell Sicherheit-Funktion, die steuert unter welchen Bedingungen Konfigurations- und Skript-Dateien ausgeführt werden. Die Ausführungsrichtlinien schränken Benutzeraktionen nicht ein. Stattdessen helfen die Ausführungsrichtlinien Benutzern, dass sie nicht unbeabsichtigt gegen diese Regeln verstoßen.

WICHTIG - 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: Alle ausführbaren Dateien müssen mit einem x.509-Zertifikat signiert sein.

Set-ExecutionPolicy -ExecutionPolicy 'AllSigned' -Force

Empfohlene Einstellung für Test- und Entwicklungs-Systeme: Alle ausführbaren Dateien die Remote (z.B. per Download oder UNC-Pfad, etc.) auf den PC gelangt sind, müssen mit einem X.509-Zertifikat signiert sein. Diese Dateien werden an einem 'Download-Marker' identifiziert. Dieser kann per Cmdlet Unblock-File wieder entfernt werden.

Set-ExecutionPolicy -ExecutionPolicy 'RemoteSigned' -Force

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!
  2. Das X.509-Zertifikat muss im Zertifikatspeicher für "Vertrauenswürdige Herausgeber" (TrustPublisher) enthalten ist.

Ausführungsrichtlinien per GPO steuern: 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'

Skript benötigt erhöhte Admin-Rechte

Im administrativen Umfeld ist nötig, dass Skripte mit erhöhte Admin-Rechte laufen. Zum Skript-Beginn kann man überprüfen ob die Admin-Rechte vorhanden sind. Sollte dies nicht der Fall sein so könnte das Skript zu beginn abbrechen, anstatt fehlerhaft in der Mitte auszusteigen.

$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
}

PowerShell PKI-Handling


# 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 aus dem Dateisystem 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

# ! Zertifikat aus dem Cert-Speicher lesen und auf Verwendungszweck testen:

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

PowerShell Script-Dateien signieren

Warum sollte man signieren:

  1. Eine Signatur schützt ein Skript (.PS1, .PSM1, .PS1XML, ...) vor Manipulation und erhöht so die PowerShell Sicherheit!
  2. Der Signierer bestätigt mit seiner Signatur den endgültigen Status des Skriptes!
  3. Dritte können erkennen wer dieses Skript signiert / erstellt hat.

PowerShell kann über die Ausführungsrichtlinien so konfiguriert werden (AllSigned), dass nur Skripte ausgeführt werden, wenn folgende Voraussetzungen erfüllt sind:

  1. Die Skriptdatei wurde mit einem X.509-Zertifikat signiert!
  2. Die signierte Datei wurde anschließend nicht manipuliert!
  3. Das Signierer-Zertifikat stamm von einer "Vertrauenswürdigen Stammzertifizierungsstelle" (Root) ab ODER es ist selbst in Root abgelegt worden!
  4. Signierer-Zertifikat wurde zusätzlich im Zertifikatspeicher für "Vertrauenswürdige Herausgeber" (TrustPublisher) abgelegt!

Zum Beispiel an einer einfachen Umsetzung über ein selbstsigniertes Zertifikat:

# ! 1. Testumgebung vorbereiten

# Ausführungsrichtlinien auf AllSigned setzen
Set-ExecutionPolicy -ExecutionPolicy 'AllSigned' -Scope 'Process'      -Force
Set-ExecutionPolicy -ExecutionPolicy 'AllSigned' -Scope 'CurrentUser'  -Force
Set-ExecutionPolicy -ExecutionPolicy 'AllSigned' -Scope 'LocalMachine' -Force

# PKI Modul importieren
Import-Module -Name 'PKI' # Windows PowerShell
Import-WinModule -Name 'PKI' # bzw. PowerShell 7

# Test-Ordner C:\Temp erstellen
New-Item -Path 'C:\Temp' -ItemType 'Directory' -Force | Set-Location

# Test-Script erzeugen
'"> > > Execute Code from .PS1-Script! at $(Get-Date) < < <" | Write-Warning' | Set-Content -Path '.\Test.ps1' -Force

# ! 2. Aktuell wird das Ausführen des Skriptes mit einer Fehlermeldung abgebrochen
. '.\Test.ps1'

# ! 3. Signierer-Zertifikate erstellen:
$params = @{
    Subject           = 'CN=_FirstName_LastName (PS Developer), E=f.lastname@abc.local'
    HashAlgorithm     = 'SHA512'
    KeyExportPolicy   = [Microsoft.CertificateServices.Commands.KeyExportPolicy]::ExportableEncrypted
    CertStoreLocation = 'Cert:\CurrentUser\My'
    Type              = [ Microsoft.CertificateServices.Commands.CertificateType]::CodeSigningCert
    NotAfter          = (Get-Date).AddYears(5)
}
$myPfxCert = New-SelfSignedCertificate @params

# ! 4. Aus dem Signierer-Zertifikat ein öffentliches Zertifikat erstellen:
$myPfxCert | Export-Certificate -FilePath '.\PublicSignerCertificate.cer' -Type 'CERT' -Force

# ! 5. Vertrauensstellung einrichten:

# 1. im Zertifikatsspeicher Root abgelegt werden, um dem Zertifikat generell zu vertrauen
Import-Certificate -FilePath '.\PublicSignerCertificate.cer' -CertStoreLocation 'Cert:\LocalMachine\Root'

# 2. im Zertifikatsspeicher TrustedPublisher abgelegt werden, um den signierten .PS1-Dateien im Besonderen zu vertrauen
Import-Certificate -FilePath '.\PublicSignerCertificate.cer' -CertStoreLocation 'Cert:\LocalMachine\TrustedPublisher'

# ! 6. Das eigentliche Script signieren:
$params = @{
    Certificate     = $myPfxCert
    HashAlgorithm   = 'SHA512'
    TimestampServer = 'http://timestamp.globalsign.com/scripts/timstamp.dll'
    FilePath        = '.\Test.ps1'
}
Set-AuthenticodeSignature @params

# ! 7. Signiertes Script testen:

# ? Die digitale Signatur sollte GÜLTIG sein?
Get-AuthenticodeSignature -FilePath '.\Test.ps1' | Format-List -Property *

# ? Wird .PS1-Datei nun problemlos ausgeführt?
. '.\Test.ps1'

# ! 8. Aufräumen
Set-ExecutionPolicy -ExecutionPolicy 'RemoteSigned' -Scope 'Process'      -Force
Set-ExecutionPolicy -ExecutionPolicy 'RemoteSigned' -Scope 'CurrentUser'  -Force
Set-ExecutionPolicy -ExecutionPolicy 'RemoteSigned' -Scope 'LocalMachine' -Force
Get-ChildItem -Path 'Cert:\CurrentUser\My', 'Cert:\LocalMachine\Root', 'Cert:\LocalMachine\TrustedPublisher' | Where-Object Thumbprint -CEQ $myPfxCert.Thumbprint | Remove-Item
Get-ChildItem -Path '.\Test.ps1', '.\PublicSignerCertificate.cer' | Remove-Item

TIPP - Vielleicht besitzen Sie ja schon ein entsprechendes Zertifikat zum Signieren. Lassen Sie sich diese wie folgt anzeigen.

Get-ChildItem -Path 'Cert:\CurrentUser\My' -CodeSigningCert

Einfache Host-Ein-/Ausgabe Protokollierung

Eine einfache Variante, um die PowerShell Sicherheit zu erhöhen, ist das Mitschneiden sämtlicher Aktivitäten und deren Resultate auf einem PowerShell Host. Hierzu verwenden Sie die Cmdlets Start-Transcript und Stop-Transcript.

# * 1. Transcript starten:
Start-Transcript -Path "C:\Temp\PsHost.log" -Append -Force

# * 2. Test-Eingabe & -Ausgaben erzeugen:
Get-Process
Read-Host -Prompt "Passwort eingeben" -AsSecureString

 # * 3. Transcript stoppen:
 Stop-Transcript -Verbose

 # 4. Log-Datei Anzeigen:
Start-Process -FilePath "C:\Temp\PsHost.log"

Wo und wann Sie Start-Transcript initiieren obliegt Ihnen. Denkbar wäre z.B. einer der Autostartdateien (profile.ps1).

Aktivitäten mittels ScriptBlockLogging protokollieren

Wenn ScriptBlockLogging aktiviert ist werden sämtliche Befehle zu PowerShell-Vorgängen protokolliert. Durch den lückenlosen Nachweis sämtlicher PowerShell-Aktivität wir die PowerShell Sicherheit weiter erhöht.

WICHTIG - Bzgl. 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 aus.

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

Permanentes und sicheres Protokollieren sämtlicher Host-Aktivitäten mittels ScriptBlockLogging aktivieren.

TIPP - ScriptBlockLogging per GPO 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 folgenden Registry-Keys.

# ! 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

Die so protokollierten Aktivitäten können nun wie folgt ausgewertet werden.

# Test-Code der gleich im Protokoll auftaucht:
Get-ChildItem -Path 'C:\Temp'

# ! Auswertung:
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 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

TIPP - Um die PowerShell Sicherheit weiter zu erhöhen, sollten sensible Daten im Protokoll geschützt werden. Dazu verschlüsseln Sie einfach die Logs (s. Protected Event Logging unter Get-Help -Name 'about_logging_windows' -ShowWindow).

Aktivitäten in der PowerShell-Session einschränken (LanguageMode)

Über $ExecutionContext.SessionState.LanguageMode können Sie weiter einschränken welche Cmdlets, Typen und Interaktionen erlaubt sind, um die PowerShell Sicherheit weiter zu erhöhen.

# ! Zum Beispiel aktivieren 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. Den LanguageMode in den beschränkten Sprachmodus versetzen:
$ExecutionContext.SessionState.LanguageMode = "RestrictedLanguage"

Weitere LanguageMode-Details entnehmen Sie den entsprechenden About-Seiten.

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

Um zu ermitteln in welchem Modi sich die aktuelle Sitzung befindet, können Sie die LanguageMode Eigenschaft abfragen:

$ExecutionContext.SessionState.LanguageMode

Zu den wichtigsten PowerShell Sicherheits-mechanismen gehört der Constrained Language Mode (CLM) die mehrere gefährlichen PowerShell-Features deaktiviert und so deren Missbrauch minimiert und die PowerShell Sicherheit erhöht. Der ConstrainedLanguage-Modus erlaubt alle Cmdlets und alle PowerShell-Sprachelemente, begrenzt jedoch die zulässigen Typen.

Wenn der LanguageMode auf ConstrainedLanguage steht werden folgende wichtige Funktionen geboten:

  • Alle Cmdlets in Windows-Modulen und andere von UMCI genehmigte Cmdlets sind voll funktionsfähig und haben, sofern nicht anders angegeben, vollständigen Zugriff auf Systemressourcen.
  • Alle in Windows enthaltenen Module können importiert und alle Befehle, die die Module exportieren, in der Sitzung ausgeführt werden.
  • Das Cmdlet Add-Type kann signierte Assembly laden, jedoch keinen beliebigen C# -Code oder Win32-APIs.
  • Das Cmdlet New-Object kann nur für zulässige Typen verwendet werden (siehe unten).
  • Die Typkonvertierung ist zulässig, jedoch nur, wenn das Ergebnis ein zulässiger Typ ist.
  • Cmdlet-Parameter, die Zeichenfolgeneingaben in Typen konvertieren, funktionieren nur, wenn der resultierende Typ ein zulässiger Typ ist.
  • Es sind nur folgende COM-Objekte zulässig:
    • Scripting.Dictionary
    • Scripting.FileSystemObject
    • VBScript.RegExp

Die folgenden Typen sind im ConstrainedLanguage-Modus zulässig. Benutzer können Eigenschaften abrufen, Methoden aufrufen und Objekte in diese Typen konvertieren.

[AliasAttribute], [AllowEmptyCollectionAttribute], [AllowEmptyStringAttribute], [AllowNullAttribute], [Array], [Bool], [byte], [char], [CmdletBindingAttribute], [DateTime], [decimal], [DirectoryEntry], [DirectorySearcher], [double], [float], [Guid], [Hashtable], [int], [Int16], [long], [ManagementClass], [ManagementObject], [ManagementObjectSearcher], [NullString], [OutputTypeAttribute], [ParameterAttribute], [PSCredential], [PSDefaultValueAttribute], [PSListModifier], [PSObject], [PSPrimitiveDictionary], [PSReference], [PSTypeNameAttribute], [Regex], [SByte], [string], [SupportsWildcardsAttribute], [SwitchParameter], [System.Globalization.CultureInfo], [System.Net.IPAddress], [System.Net.Mail.MailAddress], [System.Numerics.BigInteger], [System.Security.SecureString], [TimeSpan], [UInt16], [UInt32], [UInt64]

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.

Realisieren können Sie das z.B. 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.

Umgang mit Zugriffsberechtigungen (ACL)

Die PowerShell-Board-Mittel sind leider noch überschaubar. Im Grunde können wir von einem vorhandenes Windows-Objekt, dessen Access-Control-List klonen und genau diese auf ein anderes Objekt übertragen.

Windows Terminal Screenshot - ACL auswerten und Berechtigungen im Klartext anzeigen

# ! Die PowerShell-Board-Mittel:
Get-Command -Name 'Get-Acl', 'Set-Acl', 'ConvertFrom-SddlString'

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

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'

# TODO Aufräumen:
Remove-Item -Path 'C:\TempTest' -Recurse -Force

ACL-Objekte sind mit unter oft kryptisch zu lesen (gacutil.exe) aber lesen und auswerten bekommen wir über das Cmdlet ConvertFrom-SddlString Unterstützung.

# ? Berechtigungen von ACL-Objekten 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 Modul NtfsSecurity aus der PowerShall-Gallery 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

Admin-Aufgaben an Nicht-Admins delegieren (JEA)

Neben den bereits beschriebenen PowerShell Sicherheit-Mechaniken ist die Königsdisziplin, um die PowerShell Sicherheit auf ein Maximum zu heben das Feature "Just Enough Administration". Dieses vereint die bereits aufgeführten Funktionen und ergänzt diese.

Just Enough Administration (JEA => Gerade genug Administration) ist ein Feature, das ab Windows 10 und Server 2016 zur Verfügung steht. Mit JEA (Just Enough Administration) können Systemverwalter mittels PowerShell granular administrative Aufgaben an Nicht-Administratoren delegieren.

Diese Nicht-Administrator erhalten, unabhängig von der Gruppenmitgliedschaft über eine Remote-Session mittels "Just Enough Administration"-Zugriff auf vorher festgelegte Komponenten (PSProvider, Cmdlets, Cmdlet-Parameter, Ausführbare Dateien, etc.).

Das damit realisierte Prinzip "Least Privilege" sieht vor, nur noch die tatsächlich minimalen Rechte für bestimmte Aufgaben und Dienste an administrative Mitarbeiter zu erteilen.

Die Architektur basiert auf "Just Enough Administration"-Endpoints, mit denen sich der Bediener via Enter-PSSession verbindet und remote Befehle mit höheren Rechten auf Systemen ausführen kann.

Umfassende Details finden Sie in der Microsoft Dokumentation Just Enough Administration.

HINWEIS - Sollten Sie dieses Beispiel unter PowerShell 7 auf Windows-Rechnern ausführen sind folgende Schritte nötig.

Import-WinModule -Name 'NetAdapter'
Import-WinModule -Name 'NetConnection'
Import-WinModule -Name 'Microsoft.PowerShell.LocalAccounts'

1. JEA Voraussetzung und Vorbereitungen

Just Enough Administration ist ein in Feature ab PowerShell 5.0. Dazu gesellen sich noch weitere Voraussetzungen, die wir zuerst einmal abklopfen sollten.

# ? 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 }

In diesem Artikel Just Enough Administration Voraussetzungen werden die Voraussetzungen erläutert, die erfüllt sein müssen, um "Just Enough Administration" verwenden zu können.

Im folgenden Code-Block treffen wir die Vorbereitungen für das Testszenario. Dieses Szenario kann komplett lokal ausgeführt werden ohne dass eine Domänenmitgliedschaft nötig ist.

# 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'

2. Module-Ordner erstellen

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

WICHTIG - Der folgende Modul-Ordner muss vor Manipulation geschützt werden, Stichwort 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'

3. Rollenfunktions-Datei erstellen

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"

Weitere Details finden Sie in der Microsoft Dokumentation Erstellen einer Rollenfunktionsdatei.

4. Sitzungskonfigurations-Datei erstellen

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"

Weitere Details finden Sie in der Microsoft Dokumentation JEA-Sitzungskonfigurationen sowie unter Get-Help -Name 'about_Session_Configuration_Files' -ShowWindow.

5. Die Sitzungskonfiguration registrieren und etablieren

Jetzt können wir Sitzungskonfiguration registrieren und den Endpoint etablieren, um so JEA für den Bediener zu aktivieren.

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

Weitere Details finden Sie in der Microsoft Dokumentation Registrieren von JEA-Konfigurationen.

6. Just Enough Administration als Bediener/Verwalter verwenden

Jetzt zeigt es sich, wie es um die PowerShell Sicherheit gestellt ist.

# TODO 1. Per Just Enough Administration (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. Just Enough Administration 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

Weitere Details finden Sie in der Microsoft Dokumentation [Verwenden von JEA](https://docs.microsoft.com/de-de/powershell/scripting/learn/remoting/jea/register-jea).

7. Aufgezeigt Konfiguration zurücksetzen

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\WindowsPowerShell\Modules\AbcFirma\RoleCapabilities\PrinterManagerRoleCapability.psrc" -Force
Remove-Item -Path "$env:ProgramFiles\WindowsPowerShell\Modules\AbcFirma\AbcFirma.psd1" -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'

PowerShell Sicherheit per GPO verteilen

PowerShell Sicherheit per GPO in der ActiveDirectory-Domäne verteilen.

EdgeUI.admx

  • User / Windows Components / Edge UI / 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
    • HKCU\Software\Policies\Microsoft\Windows\EdgeUI!ShowCommandPromptOnWinX

PowerShellExecutionPolicy.admx

  • Machine / Windows Components / Windows PowerShell / Turn on PowerShell Script Block Logging
    • HKLM\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging!EnableScriptBlockLogging
    • HKLM\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging!EnableScriptBlockInvocationLogging
  • User / Windows Components / Windows PowerShell / Turn on PowerShell Script Block Logging
    • HKCU\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging!EnableScriptBlockLogging
    • HKCU\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging!EnableScriptBlockInvocationLogging
  • Machine / Windows Components / Windows PowerShell / Turn on PowerShell Transcription
    • HKLM\Software\Policies\Microsoft\Windows\PowerShell\Transcription!EnableTranscripting
    • HKLM\Software\Policies\Microsoft\Windows\PowerShell\Transcription!OutputDirectory
    • HKLM\Software\Policies\Microsoft\Windows\PowerShell\Transcription!EnableInvocationHeader
  • User / Windows Components / Windows PowerShell / Turn on PowerShell Transcription
    • HKCU\Software\Policies\Microsoft\Windows\PowerShell\Transcription!EnableTranscripting
    • HKCU\Software\Policies\Microsoft\Windows\PowerShell\Transcription!OutputDirectory
    • HKCU\Software\Policies\Microsoft\Windows\PowerShell\Transcription!EnableInvocationHeader

Epilog

Von mir gibt es noch weitere PowerShell-Fachartikel und Guides. Und jetzt Neu, abonnieren Sie meinen PowerShell-YouTube-Kanal.

Wissen vom Trainer mit praxisbezogenen Übungen, Wissenstests und Frage und Antwort-Runden. Jetzt in meinen PowerShell-Seminaren und -Workshops.

Bitte bewerten Sie diesen Artikel, wenn Sie diesen als hilfreich empfunden haben. Und teilen Sie diesen Artikel in den sozialen Medien. Als Dankeschön erhalten Sie ein von mir erstelltes hochwertiges A1-PowerShell-7-Poster mit den TOP wichtigsten Informationen für die tägliche Arbeit per Post zugesendet. Hierzu schreiben Sie mir an info at attilakrick.com einfache eine E-Mail mit dem Link der Erwähnung sowie Ihre Adresse. Aber nur solange der Vorrat reicht. Ihre Daten (E-Mail, Adresse) werden nur zum Zweck des Poster-Versandes benötigt und anschließend gelöscht.



Dieser Beitrag hat einen Kommentar

Schreibe einen Kommentar