PowerShell Logging: Making the Invisible Visible
- Oct 24
- 6 min read

If you’ve worked in cybersecurity for a while, you know one truth: PowerShell is both a friend and a foe. Administrators love it because it makes automation simple. Attackers love it because it makes exploitation simple.
From credential theft to data exfiltration, lateral movement, and even memory-only malware — PowerShell can do it all.
So, the real question is not whether PowerShell is being used, but how and by whom.
That’s where PowerShell logging comes into play — your digital microscope into what’s really happening behind that blue window.
----------------------------------------------------------------------------------------------------------
The Problem — Power Without Visibility
Older versions of PowerShell were like stealth bombers — powerful and almost invisible. Before version 5, investigators had very little to work with; attackers could run entire malicious frameworks without leaving much of a trace.
Then came PowerShell v5, and finally, we got some serious logging features. Now we can see what’s being executed, which modules are being loaded, and even the contents of the script itself.
But here’s the catch: most of this logging is not enabled by default, and attackers know it.
That’s why it’s your job to understand it, enable it, and use it wisely.
The Key Event IDs You Must Know
Think of these as your “eyes and ears” inside PowerShell:
Event ID | Log Type | What It Shows | Why It Matters |
4103 | Module Logging | Captures module and pipeline output | Great for spotting command sequences and variables |
4104 | Script Block Logging | Captures the full script content | Lets you see exactly what was executed (with deobfuscation!) |
400 & 800 | Legacy PowerShell Logs | Session start and stop events | Still useful for older systems or downgrade attacks |
WinRM/Operational | Remoting Logs | Tracks inbound/outbound PS remoting | Crucial for identifying remote PowerShell abuse |
Logs to remember:
Microsoft-Windows-PowerShell/Operational.evtx → for PowerShell v5
Microsoft-Windows-PowerShellCore/Operational.evtx → for PowerShell Core (v6, v7)
----------------------------------------------------------------------------------------------------------
The Power of Script Block Logging (EID 4104)
Event ID 4104 is where the magic happens. It records the entire script block that was executed — whether typed in manually, run from a file, or even built dynamically in memory.
What’s even better?
Windows automatically flags potentially malicious script blocks as “Warning” events under EID 4104 — even if script block logging isn’t fully enabled.
That’s right — Microsoft built in a safety net. If a suspicious command like Invoke-Expression, DownloadString, or FromBase64String runs, it gets logged automatically.
So, even in unprepared environments, you might still get lucky.
----------------------------------------------------------------------------------------------------------
Why Attackers Still Get Away — The Downgrade Trick
Attackers know PowerShell v5+ logs everything, so they often downgrade their sessions to PowerShell v2 — which has no useful logging at all.
You can spot this behavior in the legacy logs:
Look in Windows PowerShell.evtx for Event ID 400 with EngineVersion=2.0 or HostVersion=2.0
Also, watch for command-line indicators like:
powershell -Version 2
Best defense?
Disable or uninstall PowerShell v2 entirely. It’s outdated, insecure, and only helps attackers stay invisible.
----------------------------------------------------------------------------------------------------------
Decoding the Attacker’s Syntax
Attackers rarely use PowerShell the normal way. They love stealthy parameters that hide windows, bypass policies, and run scripts directly from memory.
Common flags and their purpose:
Parameter | What It Does | Why It’s Dangerous |
-WindowStyle hidden | Hides PowerShell window | Runs silently in background |
-NoProfile | Skips user profile scripts | Avoids detection via profile hooks |
-NonInteractive | Disables prompts | Prevents blocking |
-ExecutionPolicy Bypass | Disables script execution restrictions | Runs unsigned or malicious scripts |
-EncodedCommand | Runs Base64-encoded script | Obfuscates real command |
Invoke-Expression (IEX) | Executes arbitrary code | Common in download cradles |
----------------------------------------------------------------------------------------------------------
Quick Wins for Analysts
When triaging PowerShell activity, start with these “low-hanging fruit” checks:
Search for suspicious keywords:download, Invoke-Expression, FromBase64String, WebClient, rundll32, Start-BitsTransfer, Invoke-WmiMethod
Filter by Event ID:
4103 → See what modules and variables were used
4104 → See the full script, including deobfuscated code
Look for “Warning” in 4104 — these are Microsoft’s auto-flagged malicious patterns.
Check for Remoting (WinRM):
Event ID 6 — Destination host, IP, and logged-on user
Event ID 91 — Session creation
Event ID 168 — Authenticating user
Remember — even legitimate scripts can look suspicious. Don’t just hunt for what was executed — understand why it was executed.
----------------------------------------------------------------------------------------------------------
PowerShell Core (v6 & v7) — The Next Chapter
PowerShell Core is cross-platform now (Windows, Linux, macOS) — built on .NET Core. It doesn’t replace PowerShell v5, it coexists with it.
That means:
Separate installation
Separate Group Policies
Separate logs
If both versions exist on a system, you must enable logging on both. Attackers already know this — they may run scripts through PowerShell 7 to bypass your v5 monitoring.
----------------------------------------------------------------------------------------------------------
Practical Tip: Centralize, Don’t Silo
The biggest mistake I see? Logs stay on endpoints until it’s too late.
Forward PowerShell logs, Security logs (4688), and Defender logs (1116–1119) to a central SIEM or log collector. Once an attacker wipes or disables logging locally, your central copy will be the only evidence left.
----------------------------------------------------------------------------------------------------------
The Art of Obfuscation
Attackers now rely heavily on obfuscation — the practice of making a script unreadable to humans and confusing to machines. If you’ve ever seen something like ${#/~) or $a+$b+$c forming into a download cradle, that’s obfuscation at work.
One of the most famous tools behind this is Invoke-Obfuscation, created by Daniel Bohannon.
It showed the world just how easily PowerShell scripts could be twisted into unrecognizable forms while still running perfectly. Since then, threat actors and cybercrime groups have taken this concept to a new level, turning PowerShell into a weapon that can blend right into legitimate system activity.
The scary part?
A heavily obfuscated script can look like complete gibberish but still download malware or run a tool like Mimikatz in the background.
The good news is that defenders have caught up. Windows 10 introduced AMSI (Antimalware Scanning Interface), which allows security tools to inspect scripts as they’re executed — even if they’re obfuscated. This means we can now catch malicious activity closer to the source.
----------------------------------------------------------------------------------------------------------
Understanding PowerShell Logging
PowerShell isn’t just powerful for attackers — it’s also a goldmine for forensic analysts. The tool leaves behind several kinds of logs, and each tells a different part of the story.
PowerShell Transcript Logs
Transcript logs are like a screen recorder for the PowerShell terminal. They capture exactly what was typed and what the system replied with. That includes both the commands (inputs) and their results (outputs).
These logs are incredibly useful because they give you the complete picture — especially in scenarios where an attacker runs tools like Mimikatz to dump credentials.
Script Block Logging might tell you what script was executed, but Transcript Logging shows what actually happened, including whether the attack succeeded.
Transcript logs are saved as text files, usually under:
C:\Users\<username>\Documents\
But storing logs in user folders is risky — attackers can easily delete or modify them. That’s why redirect them to a write-only network share or a secure folder with limited permissions.
You can enable transcription via Group Policy under:
Computer Configuration >Administrative Templates >Windows Components >
Windows PowerShell >Turn on PowerShell Transcription
It’s lightweight, easy to enable, and provides incredible visibility with minimal storage cost.
Script Block Logging
Script Block Logging focuses on the code itself — the PowerShell commands and functions that get executed, even if they’re hidden or dynamically generated.
In combination with transcript logs, Script Block Logging helps you catch both the “what” and the “how” of PowerShell activity.
PSReadLine History
Starting with PowerShell v5, Microsoft introduced something even simpler — a local history file called ConsoleHost_history.txt.
If you’ve used Linux before, this is similar to .bash_history. It records the last 4,096 commands typed into the PowerShell console (but not the output). The file is stored here:
%UserProfile%\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadline\
This is often the first artifact investigators check when trying to reconstruct what an attacker was doing manually.
However, there are two caveats:
Attackers can easily delete or modify it.
It only records interactive console sessions, not background or remote executions.
Even so, it’s incredibly handy — especially because it’s enabled by default.
----------------------------------------------------------------------------------------------------------
Why This Matters for Defenders
Every PowerShell log type provides a different piece of the puzzle:
Log Type | Captures | Typical Use Case |
Script Block Logging | Code that was executed | Detect obfuscated or malicious scripts |
Transcript Logging | Commands + Output | Reconstruct what happened in a session |
PSReadLine History | Last 4,096 typed commands | Identify manual actions or testing |
When combined, they can help analysts piece together how an attacker moved through a system, what tools they executed, and what data they may have accessed.
----------------------------------------------------------------------------------------------------------
Wrapping Up
PowerShell is not the enemy — blindness is and PowerShell logging isn’t just a feature — it’s a window into the attacker’s mind/
When properly logged, PowerShell becomes one of your most valuable forensic resources. You can see who ran what, when they ran it, and what exactly executed in memory.
----------------------------------------------Dean---------------------------------------------------




Comments