Rastreamento e registro de scriptsScript Tracing and Logging

Embora o Windows PowerShell já tenha a configuração LogPipelineExecutionDetails da Política de Grupo para registrar em log a invocação de cmdlets, a linguagem de scripts do PowerShell traz vários recursos que talvez você queira registrar em log e/ou auditar.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. O novo recurso de Rastreamento de Script Detalhado permite habilitar o acompanhamento detalhado e a análise de uso de scripts do Windows PowerShell em um sistema.The new Detailed Script Tracing feature lets you enable detailed tracking and analysis of Windows PowerShell scripting use on a system. Depois de habilitar o rastreamento de script detalhado, o Windows PowerShell registra em log todos os blocos de script no log de eventos do 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. Caso um bloco de script crie outro bloco de script (por exemplo, um script que chama o cmdlet Invoke-Expression em uma cadeia de caracteres), esse bloco de script resultante também será registrado em log.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.

O registro desses eventos pode ser habilitado por meio da configuração Ativar Registro de Bloco de Script do PowerShell da Política de Grupo (em Modelos Administrativos -> Componentes do 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).

Os eventos são:The events are:

CanalChannel OperacionalOperational
NívelLevel VerboseVerbose
OpcodeOpcode CriarCreate
TarefaTask CommandStartCommandStart
Palavra-chaveKeyword RunspaceRunspace
EventIdEventId Engine_ScriptBlockCompiled (0x1008 = 4104)Engine_ScriptBlockCompiled (0x1008 = 4104)
MensagemMessage Criando texto Scriptblock (%1 de %2):Creating Scriptblock text (%1 of %2):
%3%3
ID de ScriptBlock: %4ScriptBlock ID: %4

O texto inserido na mensagem é a extensão do bloco de script compilado.The text embedded in the message is the extent of the script block compiled. A ID é um GUID que é retido durante o ciclo de vida do bloco de script.The ID is a GUID that is retained for the life of the script block.

Ao habilitar o registro detalhado, o recurso escreve marcadores de início e de fim:When you enable verbose logging, the feature writes begin and end markers:

CanalChannel OperacionalOperational
NívelLevel VerboseVerbose
OpcodeOpcode Abrir (/ Fechar)Open (/ Close)
TarefaTask CommandStart (/ CommandStop)CommandStart (/ CommandStop)
Palavra-chaveKeyword 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)
MensagemMessage Iniciada (/ Concluída) a invocação da ID do ScriptBlock: %1Started (/ Completed) invocation of ScriptBlock ID: %1
Runspace ID: %2Runspace ID: %2

A ID é o GUID que representa o bloco de script (que pode ser correlacionado com a ID de evento 0x1008), e a ID do Runspace representa o runspace no qual este bloco de script foi executado.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.

Sinais de porcentagem na mensagem de invocação representam propriedades estruturadas do ETW.Percent signs in the invocation message represent structured ETW properties. Embora eles sejam substituídos pelos valores reais no texto da mensagem, uma maneira mais robusta de acessá-los é recuperar a mensagem com o cmdlet Get-WinEvent e depois usar a matriz Properties da mensagem.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.

Aqui está um exemplo de como essa funcionalidade pode ajudar a descodificar uma tentativa mal-intencionada de criptografar e ocultar um script: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

A execução disso gera as seguintes entradas de log: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

Caso o tamanho do bloco de script exceda o que o ETW pode reter em um único evento, o Windows PowerShell interromperá o script em várias partes. Aqui está o código de exemplo para recombinar um script desde suas mensagens de log: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 })

Assim como acontece com todos os sistemas de registro que têm um buffer de retenção limitado (ou seja, logs do ETW), um ataque contra essa infraestrutura seria inundar o log com eventos diferentes para ocultar a evidência anterior.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. Para se proteger contra esse ataque, verifique se você tem alguma forma de coleção de log de eventos configurada (ou seja, Windows Event Forwarding, Spotting the Adversary with Windows Event Log Monitoring) para mover os logs de eventos para fora do computador assim que possível.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.