Skip to content

Execution

PowerShell logs and history

PowerShell forensics: 4103/4104 script block logs, 400/403/800 engine events, PSReadLine ConsoleHost_history.txt, transcripts and logging policy keys.

Location
C:\Windows\System32\winevt\Logs\Microsoft-Windows-PowerShell%4Operational.evtx
Proves
Which PowerShell code ran, under which account, and when; typed commands per user
Timestamps
Event TimeCreated as FILETIME, UTC; PSReadLine lines undated (file $SI only); transcript names and headers in local time, no zone
Retention
Verbose logging off by default; EVTX rolls over at max size (days on busy hosts); PSReadLine keeps the last 4,096 commands
Access
Administrator for EVTX (locked live, export with wevtutil epl); PSReadLine history is a plain text file in each user profile
Evidence of
Execution
Type
Event log
Windows
Vista / 78 / 8.11011
KAPE target
EventLogsPowerShellConsolePowerShellTranscripts
Velociraptor
Windows.Triage.Targets:EventLogsPowerShellConsolePowerShellTranscripts

What it is

PowerShell is the attacker's favourite LOLBin: it downloads payloads, runs code in memory and drives lateral movement without dropping a binary. Windows keeps several independent traces of it. Two event logs record the engine and the code itself, the PSReadLine module saves what users typed at the console, and optional transcription writes whole sessions to text files. Each source has different defaults, so knowing what is logged out of the box is as important as knowing where to look.

Location

SourceLocationAvailability
Operational log (Windows PowerShell 5.x)Microsoft-Windows-PowerShell/Operational → C:\Windows\System32\winevt\Logs\Microsoft-Windows-PowerShell%4Operational.evtxScript block logging needs PowerShell 5 (native on Windows 10/11, WMF 5.1 on 7/8.1)
Classic logWindows PowerShell → C:\Windows\System32\winevt\Logs\Windows PowerShell.evtxAll versions
PowerShell 7 logPowerShellCore/Operational → PowerShellCore%4Operational.evtxOnly if PowerShell 7 is installed and its event provider is registered
PSReadLine historyC:\Users\<user>\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txtPowerShell 5.1+ (PSReadLine ships with Windows 10 and later)
TranscriptsPowerShell_transcript.<COMPUTER>.<random>.<yyyyMMddHHmmss>.txt, in the user's Documents folder by default or in a policy OutputDirectory (per-day yyyyMMdd subfolders)Only when transcription is enabled
Logging policyHKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ (ScriptBlockLogging, ModuleLogging, Transcription); PowerShell 7: HKLM\SOFTWARE\Policies\Microsoft\PowerShellCore\SOFTWARE hive (User Configuration policies land in NTUSER.DAT)

The history file name follows the host: ConsoleHost_history.txt for the console, Visual Studio Code Host_history.txt for the VS Code extension. Accounts such as SYSTEM keep theirs under C:\Windows\System32\config\systemprofile\AppData\Roaming\....

What it proves

  • Code executed: event 4104 stores the text of each script block PowerShell compiled, including decoded -EncodedCommand content and code from Invoke-Expression, under the account that ran it.
  • Engine start and command line: 400 (engine state None → Available) and 403 (Available → Stopped) in Windows PowerShell.evtx, with a HostApplication field holding the full launch command line.
  • Commands and parameters: 4103 (module logging) in the Operational log and 800 (pipeline execution details) in the classic log record cmdlets and their bound parameters.
  • What a user typed: ConsoleHost_history.txt lists interactive console commands, including those from sessions whose logs were cleared.
  • It does not prove success: a logged block may have failed. PSReadLine does not capture scripts run with -File, non-interactive -Command launches or remoting sessions.

Key fields

Event / fileUseful fieldsNotes
4104 OperationalMessageNumber, MessageTotal, ScriptBlockText, ScriptBlockId, PathLarge scripts are split across several events sharing one ScriptBlockId
4103 OperationalContextInfo (host, user, command name), Payload (parameter bindings)Only with module logging enabled
4105 / 4106 OperationalScriptBlockId, RunspaceIdInvocation start/stop; only with invocation logging
400 / 403 classicHostApplication, EngineVersion, HostNameEngineVersion 2.0 suggests a downgrade to bypass logging
800 classicUserId, HostApplication, command detailsWritten when module logging is on
ConsoleHost_history.txtOne command per line (multi-line commands kept)Plain text, no timestamps
TranscriptHeader (user, machine, host application, start time), then input and outputPer-command headers with EnableInvocationHeader

Timestamps

TimestampSet whenFormatPitfalls
Event TimeCreatedScript block compiled, engine started/stopped, pipeline ranFILETIME, UTCEvent Viewer shows local time; convert before correlating (UTC and local time)
History file $SI modificationLast command appendedNTFS, UTCOnly dates the last line; individual commands are undated
History file $SI creationFirst command saved for that hostNTFS, UTCResets if the file was deleted and recreated
Transcript file name and headerSession startLocal time, yyyyMMddHHmmssCarries no time zone indicator

Retention and clearing

  • Nothing verbose is on by default. Script block, module and invocation logging must be enabled by policy (EnableScriptBlockLogging, EnableModuleLogging plus a ModuleNames list, EnableScriptBlockInvocationLogging). Without them, PowerShell still writes a warning-level 4104 for blocks containing content it considers suspicious.
  • Event logs roll over when they reach their maximum size; on a busy host with script block logging, the Operational log can cover only days.
  • PSReadLine keeps the last 4,096 commands by default (MaximumHistoryCount), saves after each command, and drops lines matching sensitive strings such as password, asplaintext, token, apikey or secret.
  • Anti-forensics: Set-PSReadLineOption -HistorySaveStyle SaveNothing, deleting or editing the history file, wevtutil cl, running powershell -Version 2, or deleting the policy keys. Clearing a log leaves event 104 in System (1102 for Security); see Windows event logs.

Collection

  • KAPE targets: EventLogs (all .evtx), PowerShellConsole (PSReadLine history and ISE autosave files), PowerShellTranscripts (default and common transcript folders).
  • Velociraptor (Windows.Triage.Targets): EventLogs, PowerShellConsole, PowerShellTranscripts.
  • Built-in: the policy keys live in the SOFTWARE hive (RegistryHivesSystem); export them or read them live.
wevtutil epl Microsoft-Windows-PowerShell/Operational E:\case\PS-Operational.evtx
wevtutil epl "Windows PowerShell" E:\case\WindowsPowerShell.evtx
Get-ChildItem C:\Users\*\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\*_history.txt -Force |
  Copy-Item -Destination E:\case\PSReadLine\ -PassThru
reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell /s

A transcript in a custom OutputDirectory is not covered by the default targets: read the Transcription policy key first and collect that folder separately.

Parsing tools

  • EvtxECmd (Eric Zimmerman): ships maps for 4103, 4104, 400, 403, 600 and 800, plus 4104 in PowerShellCore/Operational.
  • Chainsaw and Hayabusa (open source): Sigma-based hunting over 4104 and the classic log.
  • Velociraptor: Windows.EventLogs.PowershellScriptblock (4104, defaults to warning-level blocks), Windows.EventLogs.PowershellModule (4103) and Windows.System.Powershell.PSReadline.
  • Get-WinEvent: filter by ID and join ScriptBlockText segments by ScriptBlockId to rebuild long scripts.
  • Any text editor for ConsoleHost_history.txt and transcripts.

Investigation tips

  • Start with warning-level 4104 events: they exist even where nobody enabled logging, and they flag exactly the blocks PowerShell itself found suspicious.
  • Rebuild split scripts by ScriptBlockId before decoding; the payload is often a Base64 or compressed blob in a later segment.
  • Compare HostApplication in 400 with 4104: powershell.exe -nop -w hidden -enc ... launched by wmiprvse.exe, mshta.exe or an Office process points to WMI or macro execution.
  • Check every user's ConsoleHost_history.txt, including service and admin accounts: attackers on an interactive session often type Invoke-WebRequest, Add-MpPreference -ExclusionPath or credential-dumping commands.
  • Correlate powershell.exe runs in Prefetch with 4104 time ranges, and look for AMSI detections in Microsoft Defender logs for the same scripts.
  • Review the logging policy keys and their last write time: logging switched off shortly before the incident is itself anti-forensics.

Related artifacts

Glossary terms

Guides

All artifacts on one page: cheat sheet