How to Detect WMI Persistence and Lateral Movement

Retool's new app builder is where AI-generated code ships safely
Building apps with AI is easy. Getting them to production safely is another story.
WMI persistence evades most persistence-focused detection: it does not show up in Task Scheduler, does not add registry Run keys, does not create services, and does not drop files unless the payload does. The persistence mechanism lives entirely in the WMI repository: a binary database at %windir%\System32\wbem\Repository: and executes silently when the configured trigger fires.
A basic WMI subscription has three components: a filter (the trigger condition: e.g., every 30 minutes, on system startup, or when a specific process starts), a consumer (the action: typically CommandLineEventConsumer running a payload), and a binding that connects the filter to the consumer. Creating all three and persisting them to the WMI repository is a 10-line PowerShell script.
WMI Persistence Mechanics
Creating a WMI subscription (attacker technique):
# Create a filter: trigger every 60 seconds
$Filter = Set-WmiInstance -Namespace 'root\subscription' \
-Class '__EventFilter' -Arguments @{
Name = 'SysmonUpdate'
EventNameSpace = 'root\cimv2'
QueryLanguage = 'WQL'
Query = 'SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE
TargetInstance ISA "Win32_LocalTime" AND
TargetInstance.Seconds = 0'
}
# This filter fires every minute when seconds = 0
# Create a consumer: execute a PowerShell command
$Consumer = Set-WmiInstance -Namespace 'root\subscription' \
-Class 'CommandLineEventConsumer' -Arguments @{
Name = 'SysmonUpdate'
CommandLineTemplate = 'powershell.exe -NoP -NonI -W Hidden -enc [base64_payload]'
}
# Create the binding: link filter to consumer
Set-WmiInstance -Namespace 'root\subscription' \
-Class '__FilterToConsumerBinding' -Arguments @{
Filter = $Filter
Consumer = $Consumer
}
# Persistence is now established: survives reboots, runs as SYSTEM
# Does not appear in Task Scheduler, Services, or registry Run keys
WMI for lateral movement:
# Execute a command on a remote machine via WMI (requires admin credentials)
Invoke-WmiMethod -ComputerName 192.168.1.100 \
-Class Win32_Process \
-Name Create \
-ArgumentList "cmd.exe /c whoami > C:\temp\out.txt"
# -Credential domain\admin (if needed)
# wmic.exe equivalent:
wmic /node:192.168.1.100 /user:CORP\admin process call create "cmd.exe /c whoami"
# These do not require opening port 3389 (RDP) or 445 (SMB for PSExec)
# They use DCOM (port 135 + dynamic ports) and WMI service
# Blend with legitimate admin activity on many Windows networks
Detection: WMI Event IDs
Enable WMI Activity logging (required for Events 5857-5861):
# Enable WMI logging via Group Policy:
# Computer Configuration > Administrative Templates > Windows Components >
# Windows Management Instrumentation (WMI) > WMI Logging Level
# Setting: Verbose (level 2)
# Or enable via wevtutil:
wevtutil sl Microsoft-Windows-WMI-Activity/Operational /e:true
# Verify:
Get-WinEvent -LogName 'Microsoft-Windows-WMI-Activity/Operational' -MaxEvents 1
Key WMI Event IDs:
| Event ID | Description | Security relevance |
|---|---|---|
| 5857 | WMI provider initialized | Baseline: records provider start |
| 5858 | Query failed | Failed WMI queries: reconnaissance indicator |
| 5859 | Subscription created | Permanent subscription creation |
| 5860 | Temporary subscription created | Temporary subscription (less common for persistence) |
| 5861 | Binding activated | Permanent subscription binding: PRIMARY persistence indicator |
Sentinel KQL: detect new WMI subscriptions:
// Alert: new permanent WMI event subscription created
Event
| where Source == "Microsoft-Windows-WMI-Activity"
| where EventID in (5859, 5861)
| where TimeGenerated > ago(24h)
// Parse the subscription details from the message:
| extend
NamespaceName = extract(@'Namespace = ([^;]+)', 1, RenderedDescription),
Consumer = extract(@'Consumer = ([^;]+)', 1, RenderedDescription),
Filter = extract(@'Filter = ([^;]+)', 1, RenderedDescription)
// Exclude known-good subscriptions (AV, management tools):
| where Consumer !contains "SCM Event Log Consumer"
and Consumer !contains "NTEventLogEventConsumer"
and Consumer !contains "BVTConsumer"
| project TimeGenerated, Computer, EventID, NamespaceName, Consumer, Filter
Sysmon Events 19-21 for WMI subscription detection:
<!-- Enable in Sysmon config for WMI subscription monitoring -->
<EventFiltering>
<WmiEvent onmatch="include">
<!-- Catch all WMI subscriptions except known-good ones -->
<Operation condition="is not">SCM Event Log Consumer</Operation>
</WmiEvent>
</EventFiltering>
// Sysmon WMI events in Sentinel:
Event
| where Source == "Microsoft-Windows-Sysmon"
| where EventID in (19, 20, 21)
| extend EventData = parse_xml(EventData)
| extend
EventType = tostring(EventData.DataItem.EventData.Data[3]["#text"]),
Name = tostring(EventData.DataItem.EventData.Data[4]["#text"]),
Query = tostring(EventData.DataItem.EventData.Data[5]["#text"])
| where EventID == 21 // Binding created: final step of subscription setup
| project TimeGenerated, Computer, EventType, Name, Query
Briefings like this, every morning before 9am.
Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.
Detect WMI Lateral Movement
Sysmon Event 1: wmic.exe with remote node:
// Detect wmic.exe executing commands on remote hosts
Event
| where Source == "Microsoft-Windows-Sysmon" and EventID == 1
| extend EventData = parse_xml(EventData)
| extend
Image = tostring(EventData.DataItem.EventData.Data[4]["#text"]),
CommandLine = tostring(EventData.DataItem.EventData.Data[10]["#text"]),
User = tostring(EventData.DataItem.EventData.Data[12]["#text"])
| where Image has "wmic.exe"
and CommandLine has_any ("/node:", "process call create", "Win32_Process")
// Exclude known management tools by username or originating host
| project TimeGenerated, Computer, User, CommandLine
// Also detect PowerShell WMI lateral movement:
| where Image has "powershell"
and CommandLine has_any ("Invoke-WmiMethod", "Invoke-CimMethod", "Win32_Process")
and CommandLine has "-ComputerName"
| project TimeGenerated, Computer, User, CommandLine
Network detection: DCOM connections (WMI uses DCOM port 135):
// Detect unusual processes making DCOM connections (lateral movement via WMI)
Event
| where Source == "Microsoft-Windows-Sysmon" and EventID == 3
| extend EventData = parse_xml(EventData)
| extend
Image = tostring(EventData.DataItem.EventData.Data[4]["#text"]),
DestPort = tostring(EventData.DataItem.EventData.Data[16]["#text"]),
DestIP = tostring(EventData.DataItem.EventData.Data[14]["#text"])
| where DestPort == "135" // DCOM endpoint mapper
and Image !has_any ("mmc", "wmiprvse", "svchost", "taskhostw")
| project TimeGenerated, Computer, Image, DestIP, DestPort
Enumerate and Remove Existing WMI Subscriptions
Audit all existing WMI subscriptions (run during IR):
# List all permanent event filters:
Get-WMIObject -Namespace "root\subscription" -Class __EventFilter |
Select-Object Name, Query, @{N='Created';E={$_.ConvertToDateTime($_.CreationDate)}}
# List all event consumers:
Get-WMIObject -Namespace "root\subscription" -Class __EventConsumer |
Select-Object __CLASS, Name,
@{N='Command';E={if ($_.CommandLineTemplate) {$_.CommandLineTemplate} else {$_.ScriptText}}}
# List all bindings (filter + consumer associations):
Get-WMIObject -Namespace "root\subscription" -Class __FilterToConsumerBinding |
Select-Object Filter, Consumer
# Quick one-liner to show all subscriptions with their commands:
Get-WMIObject -Namespace "root\subscription" -Class __EventConsumer |
ForEach-Object {
Write-Host "Name: $($_.Name)" -ForegroundColor Yellow
Write-Host "Class: $($_.__CLASS)"
if ($_.CommandLineTemplate) { Write-Host "Command: $($_.CommandLineTemplate)" }
if ($_.ScriptText) { Write-Host "Script: $($_.ScriptText)" }
Write-Host "---"
}
Remove a malicious WMI subscription:
# Remove by name: replace 'SysmonUpdate' with the actual subscription name:
Get-WMIObject -Namespace "root\subscription" -Class "__FilterToConsumerBinding" |
Where-Object { $_.Filter -like "*SysmonUpdate*" } | Remove-WmiObject
Get-WMIObject -Namespace "root\subscription" -Class "__EventFilter" |
Where-Object { $_.Name -eq "SysmonUpdate" } | Remove-WmiObject
Get-WMIObject -Namespace "root\subscription" -Class "CommandLineEventConsumer" |
Where-Object { $_.Name -eq "SysmonUpdate" } | Remove-WmiObject
# Remove in order: binding first, then filter and consumer
# (Removing the binding prevents the subscription from firing during cleanup)
Block WMI lateral movement with firewall rules:
# Block inbound DCOM/WMI to workstations (they should not receive WMI management connections)
New-NetFirewallRule -DisplayName "Block Inbound WMI/DCOM" \
-Direction Inbound -Protocol TCP -LocalPort 135 \
-RemoteAddress @("10.0.0.0/8") # Adjust to your network
-Action Block -Profile Domain
# Note: This may impact legitimate admin tools: test in audit mode first
The bottom line
WMI persistence is detected through WMI-Activity Event ID 5861 (permanent subscription binding) and Sysmon Events 19-21 (filter creation, consumer creation, binding). Enable WMI logging with wevtutil sl Microsoft-Windows-WMI-Activity/Operational /e:true. Audit existing subscriptions during IR with Get-WMIObject -Namespace 'root\subscription' -Class __EventConsumer. WMI lateral movement is detected by wmic.exe with /node: arguments (Sysmon Event 1) and DCOM connections from non-management processes on port 135 (Sysmon Event 3). Remove malicious subscriptions by deleting the binding first, then the filter and consumer objects.
Frequently asked questions
How do you detect WMI persistence?
Enable WMI-Activity logging (wevtutil sl Microsoft-Windows-WMI-Activity/Operational /e:true) and monitor Event ID 5861 (permanent subscription binding activated). Enable Sysmon Events 19-21 (WmiEvent) which log filter creation, consumer creation, and binding creation separately. During incident response, enumerate existing subscriptions with Get-WMIObject -Namespace 'root\subscription' -Class __EventConsumer to identify any CommandLineEventConsumer or ScriptEventConsumer entries that execute attacker payloads.
How do attackers use WMI for lateral movement?
Attackers use wmic.exe (/node:target process call create 'cmd.exe') or PowerShell Invoke-WmiMethod to remotely execute commands on systems where they have admin credentials: using DCOM on port 135 instead of RDP (3389) or SMB (445 for PSExec). WMI lateral movement blends with legitimate admin traffic and does not require the target to have RDP enabled. Detect with Sysmon Event 1 for wmic.exe with /node: arguments and unusual DCOM connections on port 135.
What is a WMI event subscription and how do attackers use it for persistence?
WMI event subscriptions are a persistence mechanism that survives reboots and runs code when specific system events occur. Components: an EventFilter (defines the triggering condition, e.g., system startup, process creation, time interval); an EventConsumer (defines the action, typically CommandLineEventConsumer executing a command or ActiveScriptEventConsumer running a script); and a FilterToConsumerBinding linking them. The subscription persists in the WMI repository (%SystemRoot%\System32\wbem\repository). Detect with Sysmon Event IDs 19, 20, 21 (WMI activity events) or query manually with PowerShell: Get-WMIObject -Namespace root\subscription -Class __EventFilter; Get-WMIObject -Namespace root\subscription -Class __EventConsumer.
How do you remove WMI persistence from a compromised Windows system?
First enumerate all WMI subscriptions: Get-WMIObject -Namespace root\subscription -Class __EventFilter, Get-WMIObject -Namespace root\subscription -Class __EventConsumer, and Get-WMIObject -Namespace root\subscription -Class __FilterToConsumerBinding. Review each result for suspicious names, encoded commands, or unexpected executables in the consumer's CommandLineTemplate or ScriptText fields. Remove malicious subscriptions: Get-WMIObject -Namespace root\subscription -Class __FilterToConsumerBinding | Where-Object {$_.Filter -match 'SuspiciousName'} | Remove-WmiObject. Remove the filter and consumer individually after removing the binding. After removal, restart the winmgmt service and verify no subscriptions recreated themselves (re-check within 5 minutes: some persistence mechanisms create a backup subscription that restores the primary).
What is WMI repository forensics and when is it useful?
The WMI repository (%SystemRoot%\System32\wbem\repository) stores WMI class definitions, instances, and event subscriptions in binary format. Repository forensics is useful when: investigating stealthy persistence that antivirus did not detect; recovering evidence after WMI-based persistence was cleaned up (the repository may retain artifacts); and determining when a WMI subscription was first created. Tools for repository analysis: python-cim library (by FireEye/Mandiant) parses the repository offline and extracts object definitions; wmi-forensics Jupyter notebooks provide a structured analysis workflow. Repository forensics is an advanced technique -- most WMI persistence investigations are adequately handled with live WMI queries using the PowerShell methods above.
Which WMI event consumer types do attackers use and how does each differ in detection difficulty?
WMI supports three consumer types for event subscriptions. CommandLineEventConsumer executes a command line when the filter fires and is the most commonly detected: the executable path appears in WMI-Activity Event 5861 and in Sysmon Event 20. ActiveScriptEventConsumer runs JScript or VBScript directly within the WMI subsystem without spawning a child process, making it harder to detect because there is no process creation event in Sysmon -- only Event 5861 with the script text visible in the event data. LogFileEventConsumer writes to a log file and is rarely abused maliciously. For defenders: Event 5861 and Sysmon Event 21 log the consumer name and query regardless of type, and PowerShell enumeration via Get-WMIObject shows CommandLineTemplate or ScriptText for each consumer. Prioritize alerting on ActiveScriptEventConsumer entries in your WMI audit queries since they bypass Sysmon process creation monitoring entirely and are the technique APT groups use when they need lower detection rates.
Sources & references
Free resources
Critical CVE Reference Card 2025–2026
25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.
Ransomware Incident Response Playbook
Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.
Get threat intel before your inbox does.
50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.
Unsubscribe anytime. We never sell your data.

Founder & Cybersecurity Evangelist, Decryption Digest
Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.
Win a $2,495 Black Hat pass.
Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.
