PowerShell absichern — Protokollierung, AMSI, Sprachmodi und JEA

· · PowerShell, Sicherheit, Windows, Protokollierung, Auditierung, Unternehmens-IT, Berechtigungsverwaltung, Betriebsoptimierung

Nach einer Reihe von Berichten, dass „PowerShell beim Eindringen missbraucht wurde“, versuchen manche Organisationen, die Nutzung von PowerShell im gesamten Unternehmen komplett zu verbieten. Das zahlt sich jedoch weder in der Wirksamkeit noch bei den geschäftlichen Auswirkungen aus. PowerShell ist selbst die Verwaltungsgrundlage von Windows, und es zu stoppen stoppt auch Ihre betriebliche Automatisierung. Ein Angreifer kann währenddessen dasselbe über andere Wege erreichen.

Die realistische Strategie lautet nicht Verbot, sondern Sichtbarkeit und Einschränkung. Glücklicherweise bringt PowerShell 5.0 und höher Funktionen für die verteidigende Seite mit: Script Block Logging, das selbst verschleierten Code in expandierter Form aufzeichnet; AMSI, das den Inhalt vor der Ausführung an Ihr Antivirenprodukt übergibt; Sprachmodi, die die ausführbare Syntax selbst einschränken; und JEA, das „nur die tatsächlich benötigten Vorgänge“ delegiert. Kombinieren Sie diese, erreichen Sie einen prüfbaren Zustand, ohne den Betrieb zu stoppen.

Dieser Artikel fasst die Einstellungen zusammen, die Sie anwenden sollten, um PowerShell in einer internen Windows-Umgebung sicher weiterzunutzen, geordnet nach Wirksamkeit. Ausführungsrichtlinie und Signierung werden in „PowerShell-Ausführungsrichtlinie und Skriptsignierung“ behandelt; dieser Artikel knüpft daran an.

1. Das Wichtigste zuerst

  • Oberste Priorität hat die Aktivierung von Script Block Logging. Ausgeführter Code wird im Ereignisprotokoll aufgezeichnet, und verschleierter Code bleibt in expandierter Form erhalten (Ereignis-ID 4104).1
  • Aktivieren Sie zugleich die Transkription. Sitzungsaufzeichnungen einschließlich Ein- und Ausgabe lassen sich auf einer nur beschreibbaren Freigabe zusammenführen.1
  • Prüfen Sie nach dem Aktivieren der Protokollierung auch Ihre Protokollgrößen-Einstellungen. Bei den Standardwerten läuft es in kurzer Zeit über.1
  • AMSI übergibt das Skript vor der Ausführung an Ihr Antivirenprodukt. Es ist ab PowerShell 5.0, ab Windows 10, aktiv.2
  • Deaktivieren Sie die alte PowerShell-2.0-Engine. Solange sie verbleibt, besteht die Möglichkeit, in eine alte Engine zu wechseln, in der weder Protokollierung noch AMSI greifen.3
  • Die Ausführungsrichtlinie ist keine Sicherheitsgrenze. Microsoft stellt dies ausdrücklich klar. Machen Sie sie nicht zum Kern Ihrer Verteidigung.4
  • Verwenden Sie Sprachmodi als Folge von WDAC/AppLocker. Manuell gesetzt lassen sie sich umgehen und wirken nicht als Sicherheitsfunktion.5
  • Verwenden Sie JEA zur Delegation von Berechtigungen. Erlauben Sie „nur diesen Parameter, dieses Befehls“, und halten Sie den Betrieb aufrecht, ohne Administratorrechte zu vergeben.6

2. Was schützen Sie? — Erst Sichtbarkeit, dann Einschränkung

Legen Sie vor der Auflistung von Maßnahmen Prioritäten fest.

Stufe Was zu tun ist Wirkung
1. Sichtbarkeit Script Block Logging, Transkription Sie erkennen, was passiert ist. Eine nachträgliche Untersuchung wird möglich
2. Grundlegende Entschärfung PowerShell 2.0 deaktivieren, aktuell halten, AMSI verifizieren Wege schließen, die die Prüfung umgehen
3. Berechtigungen einschränken Delegation über JEA, Reduzierung von Administratorrechten Den Schadensradius begrenzen
4. Ausführung einschränken WDAC/AppLocker + eingeschränkter Sprachmodus Nicht genehmigten Code von vornherein an der Ausführung hindern

In vielen Organisationen verbessert allein die Umsetzung von 1 und 3 die Lage erheblich. Punkt 4 hat hohe Einführungskosten und ist ein Bereich, den Sie voranbringen sollten, während Sie die Auswirkungen auf den Betrieb überprüfen.

3. Protokollierung aktivieren — 4104 ist am wichtigsten

Die Protokollierung von PowerShell umfasst drei Ebenen.1

Art Was aufgezeichnet wird Ereignis-ID
Modulprotokollierung Details der Pipeline-Ausführung für angegebene Module 4103
Script Block Logging Der Text des ausgeführten Codes (nach Entschleierung) 4104
Zielprotokoll 5.1 schreibt nach Microsoft-Windows-PowerShell/Operational, 7 nach PowerShellCore/Operational
Transkription Zeichnet Ein- und Ausgabe der Sitzung in eine Textdatei auf — (Dateiausgabe)

All dies lässt sich über Gruppenrichtlinie unter „Computerkonfiguration > Administrative Vorlagen > Windows-Komponenten > Windows PowerShell“ konfigurieren. Sie können die Werte auch direkt in der Registrierung setzen.1

# Script Block Logging aktivieren (erfordert Administratorrechte; normalerweise per GPO ausgerollt)
$key = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging'
New-Item -Path $key -Force | Out-Null
# -Type ist ein vom Registrierungsanbieter hinzugefügter dynamischer Parameter. Damit lässt sich der Werttyp (DWord) explizit angeben
Set-ItemProperty -Path $key -Name 'EnableScriptBlockLogging' -Value 1 -Type DWord

# PowerShell 7 (pwsh) sieht auf einen anderen Richtlinienschlüssel. Beide setzen
$key = 'HKLM:\SOFTWARE\Policies\Microsoft\PowerShellCore\ScriptBlockLogging'
New-Item -Path $key -Force | Out-Null
Set-ItemProperty -Path $key -Name 'EnableScriptBlockLogging' -Value 1 -Type DWord

# Transkription aktivieren und auf einer nur beschreibbaren Freigabe zusammenführen
$key = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription'
New-Item -Path $key -Force | Out-Null
Set-ItemProperty -Path $key -Name 'EnableTranscripting'     -Value 1 -Type DWord
Set-ItemProperty -Path $key -Name 'OutputDirectory'         -Value '\\logsrv\pstranscripts$'
Set-ItemProperty -Path $key -Name 'EnableInvocationHeader'  -Value 1 -Type DWord

# Die Transkription hat auf der PowerShell-7-Seite einen eigenen Schlüssel. Dieselben Werte schreiben
$key = 'HKLM:\SOFTWARE\Policies\Microsoft\PowerShellCore\Transcription'
New-Item -Path $key -Force | Out-Null
Set-ItemProperty -Path $key -Name 'EnableTranscripting'     -Value 1 -Type DWord
Set-ItemProperty -Path $key -Name 'OutputDirectory'         -Value '\\logsrv\pstranscripts$'
Set-ItemProperty -Path $key -Name 'EnableInvocationHeader'  -Value 1 -Type DWord

PowerShell 7 liest die Richtlinie unter PowerShellCore, nicht unter Windows\PowerShell. Konfigurieren Sie nur die 5.1-Seite und halten das für erledigt, fehlen Aufzeichnungen von unter pwsh ausgeführten Skripten vollständig. Schreiben Sie sowohl für Script Block Logging als auch für Transkription dieselben Werte in beide Schlüssel (rollen Sie es per GPO aus, konfigurieren Sie jeweils die entsprechende Vorlage).

Der Wert von Script Block Logging liegt in seiner Widerstandsfähigkeit gegen Verschleierung. Ob der Befehl Base64-kodiert oder durch Zeichenkettenverkettung zusammengesetzt war — aufgezeichnet wird der zum Ausführungszeitpunkt expandierte Inhalt.1 Ob Sie bei einer Vorfalluntersuchung rekonstruieren können, „was ausgeführt wurde“, entscheidet sich daran, ob diese Einstellung vorhanden ist.

Zur Überprüfung der Aufzeichnungen verwenden Sie Get-WinEvent (siehe „Ereignisprotokolle mit Get-WinEvent praxisnah untersuchen“).

# Script-Block-Protokolle des letzten Tages prüfen.
# Windows PowerShell (5.1) und PowerShell 7 schreiben in unterschiedliche Protokolle,
# deshalb beide abdecken
Get-WinEvent -FilterHashtable @{
    LogName   = 'Microsoft-Windows-PowerShell/Operational', 'PowerShellCore/Operational'
    ID        = 4104
    StartTime = (Get-Date).AddDays(-1)
} -ErrorAction SilentlyContinue |
    Select-Object TimeCreated, LogName, @{ n='Script'; e={ $_.Message } } -First 20

Prüfen Sie nach dem Aktivieren unbedingt die Protokollgröße. Bei der Standardgröße läuft es innerhalb von Stunden bis Tagen über, und im Bedarfsfall ist nichts mehr vorhanden.

Get-WinEvent -ListLog 'Microsoft-Windows-PowerShell/Operational', 'PowerShellCore/Operational' |
    Select-Object LogName, MaximumSizeInBytes, RecordCount
wevtutil sl Microsoft-Windows-PowerShell/Operational /ms:1073741824   # Beispiel: auf 1 GB erweitern
wevtutil sl PowerShellCore/Operational               /ms:1073741824   # die PowerShell-7-Seite nicht vergessen

Der entscheidende Punkt beim Transkript-Ausgabeort ist, dass es sich um eine Freigabe handeln sollte, auf die Benutzer nur schreiben, aber nicht löschen oder überschreiben können. Legen Sie es lokal ab, wird es bei einem kompromittierten Rechner schlicht gelöscht.

4. Umgehungswege schließen — PowerShell 2.0 und AMSI

Über AMSI (Antimalware Scan Interface) übergibt PowerShell 5.0 und höher den Inhalt eines Skripts unmittelbar vor der Ausführung an das Antivirenprodukt, sodass er in entschleierter Form geprüft werden kann.2 Verwenden Sie ein unterstützendes Produkt, einschließlich Microsoft Defender, funktioniert dies ohne zusätzliche Konfiguration.

Das Problem ist, dass die alte Engine über keinen solchen Mechanismus verfügt. Bleibt die Engine Windows PowerShell 2.0 aktiviert, besteht mit powershell.exe -Version 2 weiterhin die Möglichkeit, in eine Umgebung zu wechseln, in der weder Protokollierung noch AMSI greifen. Diese Funktion ist veraltet, und ihre Deaktivierung wird empfohlen.3

# Status der PowerShell-2.0-Engine prüfen und deaktivieren (erfordert Administratorrechte; ein Neustart kann nötig sein)
Get-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2* |
    Select-Object FeatureName, State

Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root -NoRestart

Beachten Sie: Haben Sie PowerShell 7 (pwsh) ausgerollt, unterscheiden sich sowohl die Konfigurationsschlüssel als auch das Zielprotokoll von 5.1 (die Richtlinie liegt unter PowerShellCore, die Protokolle gehen nach PowerShellCore/Operational). In einer Umgebung, in der beide genutzt werden, wenden Sie Konfiguration, Protokollgrößen-Anpassung und Untersuchungsabfragen auf beide an. Zur Koexistenz der Versionen siehe „Die Unterschiede zwischen Windows PowerShell 5.1 und PowerShell 7“.

5. Die Ausführungsrichtlinie nicht falsch einordnen

Stellen wir es noch einmal ausdrücklich klar: Die Ausführungsrichtlinie ist keine Sicherheitsgrenze. Die offizielle Dokumentation besagt, dass sie eine Sicherheitsfunktion ist, um Benutzer davon abzuhalten, unbeabsichtigt ein Skript auszuführen, und keine Maßnahme, die böswilliges Handeln verhindert.4

Das heißt nicht, dass sie wertlos ist. Signierung hat einen eigenständigen Wert: „Sie können bestätigen, dass ein verteiltes Modul nicht manipuliert wurde“ (siehe „PowerShell-Module intern verteilen und aktualisieren“). Entscheidend ist, ihre Rolle korrekt zu verstehen und entsprechend einzusetzen; die gefährlichste Annahme ist „wir haben AllSigned gesetzt, also sind wir sicher“.

6. Sprachmodi — gemeinsam mit Anwendungssteuerung einsetzen

Eine PowerShell-Sitzung besitzt einen Sprachmodus, der die verfügbaren Sprachelemente einschränkt.5

Modus Was verfügbar ist
FullLanguage Alles (Standard)
ConstrainedLanguage Nur genehmigte Typen. Aufrufe beliebiger .NET-Typen und Ähnliches sind eingeschränkt
RestrictedLanguage Befehle können ausgeführt werden, Skriptblöcke jedoch nicht
NoLanguage Nur Befehlsausführung (wird von APIs verwendet)

Den aktuellen Modus prüfen Sie wie folgt.

$ExecutionContext.SessionState.LanguageMode

Entscheidend ist, wie er gesetzt wird. Der eingeschränkte Sprachmodus funktioniert dadurch, dass PowerShell automatisch dorthin wechselt, wenn Sie allowlist-basierte Anwendungssteuerung mit WDAC (Windows Defender Application Control) oder AppLocker konfiguriert haben.5 Eine manuelle Einstellung über Umgebungsvariablen und Ähnliches lässt sich leicht umgehen und wirkt nicht als Sicherheitsfunktion. Machen Sie sich klar, dass allein die Einschränkung des Sprachmodus ohne Einführung von Anwendungssteuerung kaum etwas für den Aufwand zurückgibt.

7. JEA — „Nur die tatsächlich benötigten Vorgänge“ delegieren

Was den realen Schaden in den meisten Fällen bestimmt, ist die Breite der Berechtigungen. Wo „der Helpdesk Administratorrechte besitzt“ oder „alle im Betrieb in Domain Admins sind“, wird die Kompromittierung eines einzelnen Rechners zur Kompromittierung des gesamten Unternehmens.

JEA (Just Enough Administration) ist ein Mechanismus, um bestimmte Vorgänge zu delegieren, ohne Administratorrechte zu übergeben.6 Es passt exakt zu Anforderungen wie „lasst den Helpdesk den Anwendungsdienst neu starten, und nichts weiter“.

Schritt 1: Die erlaubten Vorgänge in einer Rollenfähigkeitsdatei (.psrc) definieren

Eine Rollenfähigkeitsdatei muss im Ordner RoleCapabilities eines PowerShell-Moduls abgelegt werden. Das bloße Anlegen des Ordners macht ihn noch nicht als Modul erkennbar, und der Name lässt sich nicht über die weiter unten beschriebenen RoleDefinitions auflösen. Legen Sie deshalb zuerst das umschließende Modul an (einen Ordner mit einem Manifest).6

# Das Modul anlegen, das die Rollenfähigkeit aufnehmen wird (ein Manifest ist erforderlich)
$moduleRoot = 'C:\Program Files\WindowsPowerShell\Modules\KsJea'
$null = New-Item -Path "$moduleRoot\RoleCapabilities" -ItemType Directory -Force
# Ein Modulordner benötigt mindestens eine Datei mit demselben Namen wie der Ordner
$null = New-Item -Path "$moduleRoot\KsJea.psm1" -ItemType File -Force
New-ModuleManifest -Path "$moduleRoot\KsJea.psd1" -RootModule 'KsJea.psm1'

# Eine Grundgerüst-Rollenfähigkeitsdatei anlegen (der Dateiname wird zum Rollennamen).
# Rollennamen werden allein nach dem Namen über alle Module auf PSModulePath aufgelöst,
# ein generischer Name wie 'HelpDesk' kann daher mit der .psrc eines anderen Moduls kollidieren.
# Bei einer Kollision ist nicht garantiert, welche gewählt wird, und es können ungewollte Berechtigungen entstehen.
# Verwenden Sie einen eindeutigen Namen mit dem Präfix Ihrer Organisation
New-PSRoleCapabilityFile -Path "$moduleRoot\RoleCapabilities\KsHelpDesk.psrc"

# Prüfen, ob es als Modul sichtbar ist
Get-Module -Name KsJea -ListAvailable
# KsHelpDesk.psrc (Auszug) — deklarieren, was erlaubt ist, und wie weit
@{
    GUID           = '....'
    # Nur lesende Befehle können unverändert freigegeben werden
    VisibleCmdlets = @(
        'Get-Service',
        'Get-EventLog'
    )
    # Restart-Service ändert den Zustand, deshalb bewusst nicht in VisibleCmdlets aufnehmen (siehe unten)
    # VisibleFunctions grenzt nur ein, „welche bereits in die Sitzung geladenen Funktionen“ sichtbar sind;
    # es definiert keine Funktionen. Für eigene Funktionen den Rumpf in FunctionDefinitions
    # schreiben und den Namen zusätzlich in VisibleFunctions aufführen (beides ist nötig)
    VisibleFunctions = @('Get-KsAppStatus', 'Restart-KsAppService')
    FunctionDefinitions = @(
        @{
            Name        = 'Get-KsAppStatus'
            ScriptBlock = {
                # Der Rumpf einer Funktion läuft im Standard-Sprachmodus und unterliegt daher nicht den Einschränkungen von JEA.
                # Niemals Benutzereingaben ungeprüft an einen gefährlichen Befehl weiterreichen
                Get-Service -Name 'KsAppService' |
                    Microsoft.PowerShell.Utility\Select-Object Name, Status, StartType
            }
        },
        @{
            # Den Neustart als Funktion freigeben, die ihr Ziel nur über ein Argument entgegennehmen kann
            Name        = 'Restart-KsAppService'
            ScriptBlock = {
                param(
                    [Parameter(Mandatory)]
                    [ValidateSet('KsAppService', 'Spooler')]
                    [string] $Name
                )
                Microsoft.PowerShell.Management\Restart-Service -Name $Name
            }
        }
    )
    VisibleExternalCommands = @()
}

Allein ValidateSet bei VisibleCmdlets schränkt einen zustandsändernden Befehl nicht sicher ein. Über Parameters und ValidateSet ausgedrückte Einschränkungen werden nur ausgewertet, wenn dieser Parameter tatsächlich gebunden wird. Restart-Service kann einen ServiceController aus der Pipeline entgegennehmen — schreibt jemand also

Get-Service WinRM | Restart-Service

wird -Name nie gebunden, und ValidateSet wird vollständig umgangen. An einem Endpunkt, von dem Sie glaubten, er könne „nur KsAppService und Spooler neu starten“, lässt sich in Wirklichkeit jeder Dienst neu starten.

Dasselbe passiert mit einem anderen Parametersatz. Auch wenn Sie für LogName bei Get-WinEvent ein ValidateSet anfügen, bleibt bei

Get-WinEvent -ProviderName Microsoft-Windows-Security-Auditing -MaxEvents 1

LogName ungebunden, und die Einschränkung wirkt nicht. Da eine JEA-Sitzung als virtueller Administrator läuft, wird in diesem Fall sogar das Security-Protokoll lesbar.

Deshalb hält das obige Beispiel Restart-Service aus VisibleCmdlets heraus und gibt nur eine Wrapper-Funktion frei, die ihr Ziel über ein Argument entgegennehmen kann (Restart-KsAppService). Ist der einzige Eingabepfad ein Argument, gibt es keine Möglichkeit, das zu umgehen.

Einschränkungen über Parameters und ValidateSet greifen nur, „wenn dieser Parameter gebunden wird“. Bei Befehlen, bei denen sich die Bindung über Pipeline-Eingabe oder einen anderen Parametersatz vermeiden lässt, ist die Einschränkung selbst wirkungslos. Nehmen Sie es als Regel: Befehle, deren Argumente Sie einschränken möchten, sollten in eine Wrapper-Funktion verpackt werden.

Hier gibt es zwei leicht zu übersehende Verhaltensweisen. Erstens erzeugt VisibleFunctions keine Funktionen. Eine Funktion, deren Name nur aufgeführt wird, existiert in der Sitzung nicht, und Benutzer sehen „kein solcher Befehl“. Definieren Sie eigene Funktionen in FunctionDefinitions und führen Sie sie zusätzlich in VisibleFunctions auf.7 Wächst die Anzahl, lässt es sich leichter verwalten, sie in ein Skriptmodul auszulagern und dessen Funktionen über VisibleFunctions freizugeben.

Zweitens unterliegt der Rumpf einer Funktion nicht den Einschränkungen von JEA.7 Möchten Sie einen von JEA ersetzten, eingeschränkten Befehl wie Select-Object mit seinem ursprünglichen Verhalten nutzen, rufen Sie ihn wie oben vollqualifiziert als Microsoft.PowerShell.Utility\Select-Object auf. Umgekehrt bedeutet das, innerhalb einer Funktion ist alles möglich — vermeiden Sie deshalb unbedingt Code, der Benutzereingaben an Invoke-Expression weiterreicht.

Schritt 2: In einer Sitzungskonfigurationsdatei (.pssc) festlegen, wer welche Rolle erhält

# SessionType: eingeschränkter Remoteserver (standardmäßig NoLanguage)
# RunAsVirtualAccount: als virtuelles Administratorkonto ausführen
# TranscriptDirectory: aufzeichnen, was ausgeführt wird
$pssc = @{
    Path                = '.\KsHelpDesk.pssc'
    SessionType         = 'RestrictedRemoteServer'
    RunAsVirtualAccount = $true
    TranscriptDirectory = 'C:\JeaTranscripts'
    RoleDefinitions     = @{ 'EXAMPLE\HelpDesk' = @{ RoleCapabilities = 'KsHelpDesk' } }
}
New-PSSessionConfigurationFile @pssc

Schritt 3: Registrieren

Register-PSSessionConfiguration -Name 'KsHelpDesk' -Path .\KsHelpDesk.pssc -Force

Benutzer verbinden sich wie folgt.

Enter-PSSession -ComputerName 'appsrv01' -ConfigurationName 'KsHelpDesk'
# Nur erlaubte Befehle stehen zur Verfügung. Auch Restart-Service nur für die festgelegten Dienste

Zu JEA gibt es drei zentrale Punkte.6

  • Benutzer besitzen keine Administratorrechte. Die Ausführung erfolgt über die Seite des virtuellen Kontos
  • Die Sitzung ist als eingeschränkter Remoteserver konfiguriert. Der Sprachmodus ist standardmäßig eingeschränkt, sodass sich kein beliebiger Code ausführen lässt
  • Transkripte zeichnen auf, was ausgeführt wurde. So lässt sich prüfen, wer was getan hat

Wie oben erwähnt, muss die Rollenfähigkeitsdatei im RoleCapabilities-Ordner eines Moduls liegen, und dieses Modul muss über $env:PSModulePath auffindbar sein. Lässt sich ein in RoleDefinitions angegebener Rollenname nicht auflösen, prüfen Sie zunächst mit Get-Module -ListAvailable, ob das Modul sichtbar ist.6

Die Bereitstellung setzt voraus, dass Remote-Ausführung konfiguriert ist (siehe „Einstieg in PowerShell Remoting (WinRM)“).

8. Praktische Checkliste

Punkt Priorität Status
Script Block Logging (4104) aktiviert Hoch Unternehmensweit per GPO ausrollen18
Größen der PowerShell-bezogenen Protokolle erweitert Hoch Bei Standardwerten innerhalb von Tagen verschwunden
Transkription aktiviert und auf einer nur beschreibbaren Freigabe zusammengeführt Hoch Nicht lokal auf dem Rechner belassen1
PowerShell-2.0-Engine deaktiviert Hoch Ein Umgehungsweg um Protokollierung und AMSI3
Antivirenprodukt unterstützt AMSI Hoch Standardmäßig aktiviert2
Ausführungsrichtlinie nicht mit einer Sicherheitsgrenze verwechselt Mittel Signierung hat einen anderen Wert4
Umfang der vergebenen Administratorrechte überprüft Hoch Die Breite der Berechtigungen bestimmt den Schadensradius
Routineaufgaben mit JEA delegiert Mittel Reduziert Administratorrechte direkt6
WDAC/AppLocker in Betracht gezogen Mittel Die Einschränkung des Sprachmodus geht damit einher5
Protokollierungskonfiguration auch auf der PowerShell-7-Seite ausgerollt Mittel Die Einstellungen unterscheiden sich zwischen 5.1 und 7

9. Zusammenfassung

  • Ein Verbot von PowerShell hat wenig Wirkung und stoppt den Betrieb. Die Strategie sollte „Sichtbarkeit und Einschränkung“ lauten.
  • Oberste Priorität hat die Aktivierung von Script Block Logging (4104). Selbst verschleierter Code wird in expandierter Form aufgezeichnet. Erledigen Sie dies zusammen mit der Erweiterung der Protokollgröße.
  • Bei der Transkription liegt der entscheidende Punkt in der Zusammenführung auf einer Freigabe, von der Benutzer nicht löschen können.
  • Deaktivieren Sie die PowerShell-2.0-Engine, um keinen Weg offenzulassen, auf dem weder Protokollierung noch AMSI greifen.
  • Die Ausführungsrichtlinie ist keine Sicherheitsgrenze. Signierung hat Wert als Manipulationserkennung, machen Sie sie aber nicht zum Kern Ihrer Verteidigung.
  • Die Breite der Berechtigungen bestimmt die Breite des Schadens. Delegieren Sie mit JEA „nur die tatsächlich benötigten Vorgänge“, und Sie können den Betrieb aufrechterhalten, ohne Administratorrechte zu vergeben.

Beispielcode herunterladen

Der in diesem Artikel behandelte Code wird als direkt lauffähiges Paket bereitgestellt. Er umfasst das Aktivieren der Protokollierungseinstellungen und das Anlegen des JEA-Endpunkts.

Beispielcode herunterladen (zip)

Da die Beispiele zu diesem Artikel von Windows und einem Mandanten abhängen, wurden sie nicht durch Ausführung verifiziert. Die Syntaxanalyse und die statische Analyse mit PSScriptAnalyzer wurden für jede Datei durchgeführt, prüfen Sie das Verhalten aber unbedingt auf Ihrem eigenen Testrechner.

# Syntaxanalyse + statische Analyse (läuft auch auf Nicht-Windows-Plattformen)
./Invoke-SampleTests.ps1

Die Konfigurationswerte (Pfade, Servernamen, Mandanten-IDs und Ähnliches) sind Beispiele. Führen Sie sie nicht unverändert gegen die Produktivumgebung aus — passen Sie sie an Ihre eigene Umgebung an.

Verwandte Artikel

Verwandte Beratungsleistungen

KomuraSoft LLC übernimmt die Überprüfung der Sicherheitskonfiguration von Windows-Betriebsumgebungen, das betriebliche Design einschließlich der Delegation von Berechtigungen (JEA) sowie die Beratung zum Aufbau und zur Nutzung von Audit-Protokollen.

  1. Microsoft Learn, about_Logging_Windows. Zu den drei Arten der Protokollierung (Modulprotokollierung, Script Block Logging und Transkription), zur Aktivierung über Gruppenrichtlinie und Registrierung (unter HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell), dazu, dass Script Block Logging entschleierten Code als Ereignis-ID 4104 aufzeichnet, dass Modulprotokollierung als Ereignis-ID 4103 aufgezeichnet wird, sowie zu den Einstellungen OutputDirectory und EnableInvocationHeader der Transkription.  2 3 4 5 6 7 8

  2. Microsoft Learn, Antimalware Scan Interface (AMSI). Dazu, dass AMSI ein Mechanismus ist, über den Anwendungen und Dienste Inhalte zur Prüfung an ein beliebiges Antimalware-Produkt übergeben, und dazu, dass Windows-Skript-Engines einschließlich PowerShell damit integriert sind, sodass selbst verschleierte Skripte mit ihrem Inhalt zum Ausführungszeitpunkt geprüft werden können.  2 3

  3. Microsoft Learn, Deprecating Windows PowerShell 2.0. Dazu, dass die Engine Windows PowerShell 2.0 veraltet ist und ihre Deaktivierung empfohlen wird, sowie dazu, dass sie als optionales Windows-Feature bereitgestellt wird, das sich aktivieren oder deaktivieren lässt.  2 3

  4. Microsoft Learn, about_Execution_Policies. Dazu, dass die Ausführungsrichtlinie keine Sicherheitsgrenze ist, sondern eine Sicherheitsfunktion, um Benutzer davon abzuhalten, unbeabsichtigt Skripte auszuführen, sowie zum Vorhandensein mehrerer Umgehungswege.  2 3

  5. Microsoft Learn, about_Language_Modes. Zu den verfügbaren Sprachelementen in FullLanguage, ConstrainedLanguage, RestrictedLanguage und NoLanguage, zur Prüfung des aktuellen Modus über $ExecutionContext.SessionState.LanguageMode, dazu, dass PowerShell im eingeschränkten Sprachmodus läuft, wenn Anwendungssteuerung über WDAC oder AppLocker wirksam ist, sowie dazu, dass die manuelle Konfiguration des Sprachmodus nicht als Sicherheitsfunktion gedacht ist.  2 3 4

  6. Microsoft Learn, Just Enough Administration (JEA) Overview. Dazu, dass JEA ein Mechanismus ist, um nur bestimmte administrative Aufgaben zu delegieren, ohne Administratorrechte zu gewähren, zu VisibleCmdlets in einer Rollenfähigkeitsdatei (.psrc) und Einschränkungen über Parameters und ValidateSet, zu RestrictedRemoteServer, RunAsVirtualAccount, TranscriptDirectory und RoleDefinitions in einer Sitzungskonfigurationsdatei (.pssc), zur Registrierung über Register-PSSessionConfiguration sowie dazu, dass der Sprachmodus in einer JEA-Sitzung eingeschränkt ist. Zur Anforderung, Rollenfähigkeitsdateien im RoleCapabilities-Ordner eines PowerShell-Moduls abzulegen (damit es als Modul auffindbar bleibt, und zum Anlegen von Dateien mit New-PSRoleCapabilityFile), siehe JEA Role Capabilities 2 3 4 5 6

  7. Microsoft Learn, JEA Role Capabilities. Dazu, dass eigene Funktionen in FunctionDefinitions definiert und ihre Namen zusätzlich in VisibleFunctions aufgeführt werden müssen („Don’t forget to add the name of your custom functions to the VisibleFunctions field so they can be run by the JEA users.“), dazu, dass der Rumpf einer Funktion (der Skriptblock) im systemseitigen Standard-Sprachmodus läuft und daher nicht den Einschränkungen von JEA unterliegt, zur Notwendigkeit eines vollqualifizierten Namens (Microsoft.PowerShell.Utility\Select-Object), um die ursprüngliche Implementierung eines von JEA ersetzten, eingeschränkten Befehls zu nutzen, zum Vorgehen, Funktionen bei großer Anzahl in ein Skriptmodul auszulagern und über VisibleFunctions freizugeben, sowie dazu, dass der Modulordner eine Datei mit demselben Namen wie der Ordner benötigt.  2

  8. Microsoft Learn, Set-ItemProperty. Dazu, dass -Type als dynamischer Parameter hinzugefügt wird, wenn der Registrierungsanbieter verwendet wird, wodurch sich der Datentyp eines Registrierungswerts (String / ExpandString / Binary / DWord / MultiString / QWord und weitere) angeben lässt. Einschließlich der Tatsache, dass der Wert angelegt wird, falls er nicht existiert. 

Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.

Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.

Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.

Häufige Fragen

Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.

Sollten wir PowerShell als Sicherheitsmaßnahme unternehmensweit verbieten?
Wir raten davon ab: Die tatsächliche Wirkung ist gering, die Nebenwirkungen sind groß. PowerShell ist selbst die Verwaltungsgrundlage von Windows und im Kern nichts anderes als eine Sammlung von Fähigkeiten auf Basis von .NET. Wird die ausführbare Datei blockiert, bleiben dieselben APIs über andere Wege weiterhin aufrufbar — für einen Angreifer ist das kaum ein Hindernis, während legitime Administration und Automatisierung zuverlässig gestoppt werden. Die realistische Strategie lautet nicht Verbot, sondern Sichtbarkeit und Einschränkung. Zeichnen Sie mit Script Block Logging auf, was ausgeführt wurde, deaktivieren Sie die alte Engine-Version und schränken Sie, wo nötig, mit Sprachmodi oder JEA ein, was ausgeführt werden darf.
Wenn wir Script Block Logging aktivieren, ertrinken wir dann nicht in Protokollen?
Das Volumen nimmt tatsächlich zu, aktivieren Sie es deshalb zusammen mit einer Überprüfung Ihrer Protokollgrößen-Einstellungen. Belassen Sie die Maximalgröße des Protokolls Microsoft-Windows-PowerShell/Operational auf dem Standardwert, läuft es in kurzer Zeit über, und genau die Einträge, die Sie im Ernstfall brauchen, sind dann verschwunden. Betrieblich sollten Sie eine großzügige Protokollgröße vorsehen und die Daten bei Bedarf in einem SIEM oder über Event Forwarding zusammenführen. Es gibt zudem eine Einstellung, die detailliertere Start-/Stopp-Ereignisse einzelner Aufrufe aufzeichnet, deren Ausgabemenge jedoch sehr hoch ist — im Normalfall sollten Sie nur das standardmäßige Script Block Logging aktivieren.
Zählt das Setzen der Ausführungsrichtlinie auf AllSigned als Sicherheitsmaßnahme?
Die Ausführungsrichtlinie ist keine Sicherheitsgrenze. Die offizielle Dokumentation stellt ausdrücklich klar, dass sie ein Mechanismus ist, um Benutzer davon abzuhalten, unbeabsichtigt gefährliche Skripte auszuführen, und keine Maßnahme gegen böswilliges Handeln — da mehrere Umgehungswege existieren. Signierung hat einen eigenständigen Wert, nämlich die Integrität des Verteilten überprüfen zu können; der Kern Ihrer Verteidigung sollte jedoch aus Sichtbarkeit durch Protokolle, Prüfung durch AMSI und Einschränkung von Berechtigungen durch Sprachmodi und JEA bestehen.
Ist es in Ordnung, den ConstrainedLanguage-Modus manuell zu setzen?
Eine manuelle Einstellung über Umgebungsvariablen und Ähnliches wird nicht empfohlen. Sie lässt sich leicht umgehen und wirkt nicht als Sicherheitsfunktion. Der eingeschränkte Sprachmodus ist so konzipiert, dass er als Folge der Konfiguration von Anwendungssteuerung mit WDAC (Windows Defender Application Control) oder AppLocker zum Einsatz kommt, wobei PowerShell dann automatisch in diesen Modus wechselt. Nur den Sprachmodus einzuschränken, ohne Anwendungssteuerung einzuführen, ergibt keinen wirksamen Schutz.
Ich möchte einem Helpdesk-Mitarbeiter erlauben, genau einen bestimmten Dienst auf einem Server neu zu starten, und sonst nichts.
Genau dafür ist JEA (Just Enough Administration) der passende Mechanismus. In einer Rollenfähigkeitsdatei legen Sie fest, „nur diesen Wert, dieses Parameters, dieses Befehls“ zu erlauben, und registrieren dies als Sitzungskonfiguration. Benutzer können dann nur die erlaubten Vorgänge ausführen, über ein virtuelles Konto, ohne selbst Administratorrechte zu besitzen. Die Sitzung ist als eingeschränkter Remoteserver konfiguriert, und standardmäßig ist auch der Sprachmodus eingeschränkt, sodass kein Raum für die Ausführung beliebigen Codes bleibt. Was ausgeführt wird, lässt sich als Transkript aufzeichnen.

Autorenprofil

Profilseite des Artikelautors.

Go Komura

Geschäftsführer von KomuraSoft LLC

Spezialisiert auf Windows-Softwareentwicklung, technische Beratung und Fehleranalyse, insbesondere bei bestehenden Systemen und schwer reproduzierbaren Störungen.

Zurück zum Blog