スクリプトのトレースとログScript Tracing and Logging

Windows PowerShell には、コマンドレットの呼び出しをログに記録する LogPipelineExecutionDetails グループ ポリシー設定が既に備えられていますが、PowerShell のスクリプト言語には、ログへの記録や監査の対象とする可能性がある機能が数多く含まれています。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. 新しい詳細スクリプト トレース機能では、システムで使用される Windows PowerShell スクリプトの詳細な追跡や分析を有効にすることができます。The new Detailed Script Tracing feature lets you enable detailed tracking and analysis of Windows PowerShell scripting use on a system. 詳細なスクリプト トレースを有効にすると、Windows PowerShell はすべてのスクリプト ブロックを ETW イベント ログ 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. スクリプト ブロックによって別のスクリプト ブロックが作成される場合 (たとえば、文字列で Invoke-Expression コマンドレットを呼び出すスクリプト)、その結果として得られるスクリプト ブロックも記録されます。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.

これらのイベントのログ記録は、[PowerShell スクリプト ブロックのログ記録を有効にする] グループ ポリシー設定 ([管理用テンプレート] -> [Windows コンポーネント] -> [Windows PowerShell]) で有効にすることができます。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).

イベントは次のとおりです。The events are:

[チャネル]Channel OperationalOperational
レベルLevel VerboseVerbose
オペコードOpcode 作成Create
タスクTask CommandStartCommandStart
キーワードKeyword RunspaceRunspace
イベント IDEventId Engine_ScriptBlockCompiled (0x1008 = 4104)Engine_ScriptBlockCompiled (0x1008 = 4104)
メッセージMessage Creating Scriptblock text (%1 of %2):Creating Scriptblock text (%1 of %2):
%3%3
ScriptBlock ID: %4ScriptBlock ID: %4

メッセージに埋め込まれたテキストは、コンパイルされたスクリプト ブロックの範囲です。The text embedded in the message is the extent of the script block compiled. ID は、スクリプト ブロックの有効期間に保持される GUID です。The ID is a GUID that is retained for the life of the script block.

詳細ログ記録の機能を有効にすると、次のように開始マーカーと終了マーカーが書き込まれます。When you enable verbose logging, the feature writes begin and end markers:

[チャネル]Channel OperationalOperational
レベルLevel VerboseVerbose
オペコードOpcode Open (/ Close)Open (/ Close)
タスクTask CommandStart (/ CommandStop)CommandStart (/ CommandStop)
キーワードKeyword RunspaceRunspace
イベント 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)
メッセージMessage Started (/ Completed) invocation of ScriptBlock ID: %1Started (/ Completed) invocation of ScriptBlock ID: %1
Runspace ID: %2Runspace ID: %2

ID は (イベント ID 0x1008 に関連付けることができる) スクリプト ブロックを表す GUIDで、Runspace ID はこのスクリプト ブロックが実行された実行空間を表します。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.

呼び出しメッセージ内のパーセント記号は、構造化 ETW プロパティを表します。Percent signs in the invocation message represent structured ETW properties. これらはメッセージ テキストで実際の値に置き換えられますが、Get-WinEvent コマンドレットを使用してメッセージを取得し、メッセージの Properties 配列を使用すると、より信頼性の高い方法でそれらにアクセスできます。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.

この機能を利用して、スクリプトを暗号化および難読化する悪意のある試みをラップ解除する方法の例を次に示します。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

このことを実行すると、次のログ エントリが生成されます。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

スクリプト ブロックの長さが、ETW で単一のイベントに格納できる長さを超えた場合、Windows PowerShell はスクリプトを複数の部分に分割します。 ログ メッセージからスクリプトを再結合するサンプル コードを次に示します。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 })

保持するバッファーが限られているすべてのログ システム (ETW ログなど) の場合と同様、このインフラストラクチャに対して、正しくないイベントでログを溢れさせて以前の証拠を隠す攻撃が行われる場合があります。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. この攻撃から保護するには、何らかの形式のイベント ログ収集が設定されていることを確認し (つまり、Windows イベント転送、「Spotting the Adversary with Windows Event Log Monitoring (Windows イベント ログ監視による敵対者の偵察)」)、できるだけ早くコンピューターからイベント ログを移動します。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.