Ablaufverfolgung und Protokollierung von SkriptsScript Tracing and Logging

Während Windows PowerShell bereits über die Gruppenrichtlinieneinstellung LogPipelineExecutionDetails verfügt, um den Aufruf von Cmdlets zu protokollieren, bietet die PowerShell-Skriptsprache viele Features, die Sie ggf. protokollieren und/oder überwachen möchten.While Windows PowerShell already has the LogPipelineExecutionDetails Group Policy setting to log the invocation of cmdlets, PowerShell’s scripting language has plenty of features that you might want to log and/or audit. Das neue Feature zur detaillierten Ablaufverfolgung von Skripts ermöglicht eine detaillierte Nachverfolgung und Analyse der Verwendung von Windows PowerShell-Skripts auf einem System.The new Detailed Script Tracing feature lets you enable detailed tracking and analysis of Windows PowerShell scripting use on a system. Nachdem Sie die detaillierte Ablaufverfolgung von Skripts aktiviert haben, protokolliert Windows PowerShell alle Skriptblöcke im ETW-Ereignisprotokoll Microsoft-Windows-PowerShell/Operational.After you enable detailed script tracing, Windows PowerShell logs all script blocks to the ETW event log, Microsoft-Windows-PowerShell/Operational. Wenn ein Skriptblock einen anderen Skriptblock erstellt (ein Skript z. B. das Cmdlet „Invoke-Expression“ für eine Zeichenfolge aufruft), wird dieser resultierende Skriptblock ebenfalls protokolliert.If a script block creates another script block (for example, a script that calls the Invoke-Expression cmdlet on a string), that resulting script block is logged as well.

Die Protokollierung dieser Ereignisse kann über die Gruppenrichtlinieneinstellung Protokollierung von PowerShell-Skriptblöcken aktivieren (Administrative Vorlagen -> Windows-Komponenten -> Windows PowerShell) aktiviert werden.Logging of these events can be enabled through the Turn on PowerShell Script Block Logging Group Policy setting (in Administrative Templates -> Windows Components -> Windows PowerShell).

Die Ereignisse sind wie folgt:The events are:

KanalChannel BetriebsbereitOperational
EbeneLevel AusführlichVerbose
OpcodeOpcode ErstellenCreate
AufgabeTask CommandStartCommandStart
SchlüsselwortKeyword RunspaceRunspace
Ereignis-IDEventId Engine_ScriptBlockCompiled (0x1008 = 4104)Engine_ScriptBlockCompiled (0x1008 = 4104)
MeldungMessage Skriptblocktext (%1 von %2) wird erstellt:Creating Scriptblock text (%1 of %2):
%3%3
ScriptBlock-ID: %4ScriptBlock ID: %4

Der in der Meldung eingebettete Text gibt das Ausmaß des kompilierten Skriptblocks an.The text embedded in the message is the extent of the script block compiled. Die ID ist eine GUID, die für die Gültigkeitsdauer des Skriptblocks beibehalten wird.The ID is a GUID that is retained for the life of the script block.

Wenn Sie die ausführlichen Protokollierung aktivieren, schreibt das Feature die Markierungen „begin“ und „end“:When you enable verbose logging, the feature writes begin and end markers:

KanalChannel BetriebsbereitOperational
EbeneLevel AusführlichVerbose
OpcodeOpcode Öffnen (/ Schließen)Open (/ Close)
AufgabeTask CommandStart (/ CommandStop)CommandStart (/ CommandStop)
SchlüsselwortKeyword RunspaceRunspace
Ereignis-IDEventId ScriptBlock_Invoke_Start_Detail (0x1009 = 4105) /ScriptBlock_Invoke_Start_Detail (0x1009 = 4105) /
ScriptBlock_Invoke_Complete_Detail (0x100A = 4106) /ScriptBlock_Invoke_Complete_Detail (0x100A = 4106)
MessageMessage Der Aufruf der ScriptBlock-ID wurde gestartet (/ abgeschlossen: %1Started (/ Completed) invocation of ScriptBlock ID: %1
Runspace-ID: %2Runspace ID: %2

Die ID ist die GUID, die den Skriptblock darstellt (der mit der Ereignis-ID 0x1008 korreliert werden kann). Die Runspace-ID stellt den Runspace dar, in dem dieser Skriptblock ausgeführt wurde.The ID is the GUID representing the script block (that can be correlated with event ID 0x1008), and the Runspace ID represents the runspace in which this script block was run.

Prozentzeichen in der Aufrufmeldung stellen strukturierte ETW-Eigenschaften dar.Percent signs in the invocation message represent structured ETW properties. Wenngleich sie im Meldungstext durch die tatsächlichen Werte ersetzt werden, empfiehlt sich für den Zugriff darauf eher das Abrufen der Meldung mit dem Cmdlet „Get-WinEvent“ und dann das Untersuchen des Bereichs Eigenschaften der Meldung.While they are replaced with the actual values in the message text, a more robust way to access them is to retrieve the message with the Get-WinEvent cmdlet, and then use the Properties array of the message.

Hier ist ein Beispiel, wie diese Funktionalität helfen kann, einen böswilligen Versuch zum Verschlüsseln und Verschleiern eines Skripts zu erkennen:Here's an example of how this functionality can help unwrap a malicious attempt to encrypt and obfuscate a script:

## Malware
function SuperDecrypt
{
    param($script)
    $bytes = [Convert]::FromBase64String($script)

    ## XOR “encryption”
    $xorKey = 0x42
    for($counter = 0; $counter -lt $bytes.Length; $counter++)
    {
        $bytes[$counter] = $bytes[$counter] -bxor $xorKey
    }
    [System.Text.Encoding]::Unicode.GetString($bytes)
}

$decrypted = SuperDecrypt "FUIwQitCNkInQm9CCkItQjFCNkJiQmVCEkI1QixCJkJlQg=="
Invoke-Expression $decrypted

Bei Ausführen dieses Befehls werden die folgenden Einträge generiert:Running this generates the following log entries:

Compiling Scriptblock text (1 of 1):
function SuperDecrypt
{
    param($script)
    $bytes = [Convert]::FromBase64String($script)
    ## XOR "encryption"
    $xorKey = 0x42
    for($counter = 0; $counter -lt $bytes.Length; $counter++)
    {
        $bytes[$counter] = $bytes[$counter] -bxor $xorKey
    }
    [System.Text.Encoding]::Unicode.GetString($bytes)

}
ScriptBlock ID: ad8ae740-1f33-42aa-8dfc-1314411877e3

Compiling Scriptblock text (1 of 1):
$decrypted = SuperDecrypt "FUIwQitCNkInQm9CCkItQjFCNkJiQmVCEkI1QixCJkJlQg=="
ScriptBlock ID: ba11c155-d34c-4004-88e3-6502ecb50f52

Compiling Scriptblock text (1 of 1):
Invoke-Expression $decrypted
ScriptBlock ID: 856c01ca-85d7-4989-b47f-e6a09ee4eeb3

Compiling Scriptblock text (1 of 1):
Write-Host 'Pwnd'
ScriptBlock ID: 5e618414-4e77-48e3-8f65-9a863f54b4c8

Wenn die Länge eines Skriptblocks größer als das Maximum ist, das ETW in einem einzelnen Ereignis zulässt, teilt Windows PowerShell das Skript in mehrere Teile auf. Hier ist Beispielcode für das erneute Zusammenzusetzen der Protokollmeldungen eines Skripts:Here is sample code to recombine a script from its log messages:

$created = Get-WinEvent -FilterHashtable @{ ProviderName="Microsoft-Windows-PowerShell"; Id = 4104 } | Where-Object { $_.<...> }
$sortedScripts = $created | sort { $_.Properties[0].Value }
$mergedScript = -join ($sortedScripts | % { $_.Properties[2].Value })

Wie alle Protokollierungssysteme, die einen begrenzten Aufbewahrungspuffer haben (wie z. B. ETW-Protokolle), besteht ein Angriff auf diese Infrastruktur darin, dass Protokoll mit gefälschten Ereignissen zu überfluten, um ein früheres Vorkommen zu vertuschen.As with all logging systems that have a limited retention buffer (i.e. ETW logs), one attack against this infrastructure is to flood the log with spurious events to hide earlier evidence. Um sich vor einem solchen Angriff zu schützen, stellen Sie sicher, dass Sie eine Form der Ereignisprotokollsammlung eingerichtet haben (z. B. Windows-Ereignisweiterleitung, Spotting the Adversary with Windows Event Log Monitoring [Erkennen des Gegners mithilfe der Überwachung des Windows-Ereignisprotokolls]), um Ereignisprotokolle so schnell wie möglich vom Computer zu verschieben.To protect yourself from this attack, ensure that you have some form of event log collection set up (i.e., Windows Event Forwarding, Spotting the Adversary with Windows Event Log Monitoring) to move event logs off of the computer as soon as possible.