스크립트 추적 및 로깅Script Tracing and Logging

Windows PowerShell에는 cmdlet의 호출을 로깅하는 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 cmdlet을 호출하는 스크립트)을 만드는 경우 결과 스크립트 블록도 로깅됩니다.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 작동Operational
수준Level VerboseVerbose
OpcodeOpcode 만들기Create
작업Task CommandStartCommandStart
키워드Keyword RunspaceRunspace
EventIdEventId Engine_ScriptBlockCompiled(0x1008 = 4104)Engine_ScriptBlockCompiled (0x1008 = 4104)
메시지Message Scriptblock 텍스트를 만드는 중(%1/%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 작동Operational
수준Level VerboseVerbose
OpcodeOpcode 열기(/ 닫기)Open (/ Close)
작업Task CommandStart(/ CommandStop)CommandStart (/ CommandStop)
키워드Keyword RunspaceRunspace
EventIdEventId 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 ScriptBlock ID 호출을 시작했습니다(/ 완료했습니다). %1Started (/ Completed) invocation of ScriptBlock ID: %1
Runspace ID: %2Runspace ID: %2

ID는 이벤트 ID 0x1008과 상호 관련될 수 있는 스크립트 블록을 나타내는 GUID이고 Runspace ID는 이 스크립트 블록이 실행된 runspace를 나타냅니다.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 cmdlet을 사용하여 메시지를 검색한 다음 메시지의 속성 배열을 사용하는 것입니다.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.