Transcript Logging WinRM Sessions
TL;DR How to extend PowerShell Transcript Logging to include WinRM sessions, even sessions from non domain joined systems.
Transcript Logging WinRM Sessions

TL;DR How to extend PowerShell Transcript Logging to include WinRM sessions, even sessions from non domain joined systems.
This is essentially Part IV in our mini series on Logging CLI Usage
Part I: PowerShell ScriptBlock & Transcript Logging
Part II: Logging common evasion TTPs
Part III: Whitelisting admins and flagging phishy CLI usage
Part IV: Transcript Logging WinRM Sessions
Background
We started out here by setting up Scriptblock logging to capture PS1s that are ran in the logs. We next setup Transcript Logging to capture everything typed into a PowerShell terminal and clearly show us exactly what was ran and the output, just as if we’d been looking over the person’s shoulder who typed it.
We next examined a few common attacker TTPs for running PowerShell even if PowerShell.exe is blocked and running PS1s without using PowerShell at all here. We examined how to find these TTPs in the logs and still see what was ran.
We dove into whitelisting IT folks and alerting on anyone else who runs a CLI here. Likewise we showed how to scan the Transcript Logs against a Dirty Word List and alert on matches.
However we haven’t yet answered a fundamental question; what if the command were run remotely? Are they still logged?
On an admin note I ran this project from the third forest in Mishky’s Cyber Range. This forest has a DC named “Shop-DC” and a webserver named “Shop-Client” so it was simple to quickly setup logging on the client and then start poking at it from Kali to see what’s viewable in the logs.
But does it log PSExec?
Yes, if PSExec is used to open PowerShell. If it’s simply used to open legacy cmd.exe then you will have to fall back on checking for Event ID 4688 and cmd.exe.
PSExec running PowerShell simply logs to the Transcript logs. The one caveat to be aware of is that no matter what credentials are used the Transcript log will show SYSTEM as the username.

Credentials omitted in order to not spoil Mishky’s Cyber Range

Commands ran via Impacket’s psexec.py are also logged just the same as Metasploit’s PSExec in our testing.
On an interesting sidenote the mere act of connecting with Metasploit’s PSExec leaves evidence in the Transcript Logs and things like “-noni”, “nop”, and “-w hidden”, and other “Dirty Words” we can alert on via our function ‘Get-Dirty’.

Please note that Metasploit’s PSExec will be blocked by Defender by default. Even the text file in the Transcript Logs that Metasploit left behind will be deleted by Defender upon opening the file as it contains obfuscated code that Defender considers malware, and not without good reason.
I had to add an exception for the C:\PS_Logs folder in testing as a result.
But does it log WinRM?
By default no. However the good news is that this is relatively straightforward to setup.
This is where this lab project got truly interesting. I had to create a PS1 called ‘ForceTranscript.ps1’ on the workstation.
# Detect if we are in a remote session
if ($PSSenderInfo) {
$LocalCache = "C:\Transcripts\WinRM_Cache"
if (-not (Test-Path $LocalCache)) { New-Item $LocalCache -ItemType Directory -Force }
# Stop the 'broken' network transcript and start a local one
#Stop-Transcript -ErrorAction SilentlyContinue
$Date = Get-Date -Format "yyyyMMdd-HHmmss"
$User = [Security.Principal.WindowsIdentity]::GetCurrent().Name.Split('\')[-1]
$FileName = "WinRM-Transcript-$User-$Date.txt"
Start-Transcript -Path "$LocalCache\$FileName" -Append -Force
}
I then had to run a command on the workstation to make it run this PS1 upon starting a session.
Set-PSSessionConfiguration -Name "Microsoft.PowerShell" -StartupScript "C:\Scripts\ForceTranscript.ps1" -Force
This logs anything sent to the workstation via a remote session to C:\Transcripts\WinRM_Cache locally on the workstation. I couldn’t set it to log directly to the WEC where the normal Transcript logs are contained due to the ‘Kerberos Double Hop Limitation’.
There are two workarounds:
- Modify the NTFS and share permissions on the WEC so that Everyone can write
- Set a scheduled task to run as SYSTEM and copy the WinRM transcripts to the WEC
Option number 2 is obviously more secure as Domain Computers already have write access to the share containing the Transcripts.
Logging WinRM
The scheduled task runs every 10 minutes and simply copies the WinRM transcripts to a subfolder aptly named WinRM_Logs under C:\PS_Logs on the WEC. This gives us a central repository of both commands run locally directly in the CLI and those run via a remote session.
I ran the entire setup from the Hypervisor via PowerShell Direct.
#This sets Shop-Client up to log WinRM sessions to C:\Transcripts\WinRM_Cache
Copy-VMFile "Shop-Client" -SourcePath ".\Transcript_Logging_Setup\ForceTranscript.ps1" -DestinationPath "C:\Scripts" -CreateFullPath -FileSource Host
Copy-VMFile "Shop-Client" -SourcePath ".\Transcript_Logging_Setup\Get-WinRM_Transcript_Logs.ps1" -DestinationPath "C:\Scripts" -CreateFullPath -FileSource Host
Start-Sleep -Seconds 30
Invoke-Command -VMName "Shop-DC" {New-Item "C:\PS_Logs\WinRM_Logs" -ItemType Directory} -Credential $ThirdDomainAdminCredObject
Invoke-Command -VMName "Shop-Client" {Set-PSSessionConfiguration -Name "Microsoft.PowerShell" -StartupScript "C:\Scripts\ForceTranscript.ps1" -Force} -Credential $ThirdDomainAdminCredObject
Invoke-Command -VMName "Shop-Client" -Credential $ThirdDomainAdminCredObject -ScriptBlock {
#Define the Action (Points to your new script)
$Action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File C:\Scripts\Get-WinRM_Transcript_Logs.ps1"
#Define the Trigger (Runs every 10 minutes, indefinitely)
$Trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 10)
#Define the Principal (The "Who" - SYSTEM is the magic key for the computer account)
$Principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
#Define Task Settings (Allow it to run even if on battery, etc.)
$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable
#Register the Task
Register-ScheduledTask -TaskName "Sync-WinRM-Transcripts" -Action $Action -Trigger $Trigger -Principal $Principal -Settings $Settings -Force
}

Connecting to the Shop-Client from another domain system

Credentials omitted in order to not spoil Mishky’s Cyber Range

Both the domain system’s session and evil-winrm from Kali in the logs
In production one would setup WinRM logging by pushing the PS1s via Group Policy to setup the clients to log remote sessions locally and then copy them to the WEC every 10 minutes.
Why logging WinRM is important
A Red Team will often be using their own systems and only have Domain credentials. An attacker may or may not do similar, but IMHO it’s important to capture what’s sent remotely to domain workstations, maybe even more crucial than logging what’s run locally. If WinRM sessions aren’t logged then you have a rather large blind spot in your Transcript logs.
Summary
As always if you the reader thinks all this is a dumb idea or just not a good fit for your own environment then feel free to ignore it. IMHO PowerShell Transcript logging has value and adding WinRM sessions to those logs adds even more.
The resulting logs are all text files. This means they are quite small and can be searched rather quickly. Exactly what to search for is really only limited by your imagination. I have come up with two ideas so far; looking for “dirty words”, aka suspicious commands attackers like to use, and looking for any non IT folks who open a CLI.
However there are far, far more searches that could be run looking for suspicious things and IOCs. The trick is to have the logging setup in advance so that the data is there to query when a potential incident occurs.
References
PowerShell logging: https://learn.microsoft.com/en-us/answers/questions/1682983/enable-logging
Logging sessions: https://cloud.google.com/blog/topics/threat-intelligence/greater-visibility/
WinRM security: https://commandline.ninja/securing-powershell/
Kerberos Double Hop: https://techcommunity.microsoft.com/blog/askds/understanding-kerberos-double-hop/395463
Indicators of Compromise (IOC): https://www.microsoft.com/en-us/security/business/security-101/what-are-indicators-of-compromise-ioc
메타데이터
- post_id
- d42bb9841b68
- slug
- transcript-logging-winrm-sessions-d42bb9841b68
- url
- https://medium.com/@happycamper84/transcript-logging-winrm-sessions-d42bb9841b68
- canonical_url
- https://medium.com/@happycamper84/transcript-logging-winrm-sessions-d42bb9841b68
- author_url
- https://medium.com/@happycamper84
- status
- ok
- fetched_at
- 2026-06-25 16:53:31