WMI Persistence Investigation Checklist
Step-by-step checklist for WMI persistence: live queries, the repository, event IDs 5860 and 5861, Sysmon 19-21, execution evidence and a safe clean-up.
TL;DR. Collect the repository first, then look at the host live, then correlate. The objects tell you what runs and when; Microsoft-Windows-WMI-Activity/Operational (event ID 5861) and Sysmon (19–21) tell you when it was registered; Prefetch, Amcache and process telemetry tell you whether it ran. Clean up only after evidence is preserved.
1. Preserve the repository
Copy C:\Windows\System32\wbem\Repository from a shadow copy before touching WMI: how to collect the WMI repository. Removing a subscription, installing software or rebuilding the repository can overwrite deleted records you have not read yet.
2. Look at the host live
List what WMI currently runs, across the usual namespaces:
'root/subscription','root/cimv2','root/default' | ForEach-Object {
$ns = $_
'__EventFilter','__EventConsumer','__FilterToConsumerBinding' | ForEach-Object {
Get-CimInstance -Namespace $ns -ClassName $_ -ErrorAction SilentlyContinue
}
} | Format-List *
Sysinternals Autoruns has a WMI tab with the same live view. Both show only live objects and rely on a WMI service an attacker may control.
3. Parse the collected files
Drop the Repository folder on WMI Parser. Check, in order:
- Bindings that are not Windows defaults. The SCM Event Log and BVT subscriptions are expected — when their content matches (details).
- The consumer's action: command line, script text or file, and paths. Encoded PowerShell, binaries under
C:\ProgramData,C:\Users\…or%TEMP%, and script consumers deserve a close read. - The filter's trigger: uptime (fires after each boot), logon, timers, process start or device events.
CreatorSIDon each object: which account registered it.- Recovered objects: deleted bindings, older versions, unbound consumers. They show what existed and what was cleaned up (how recovery works).
- Namespaces other than
root\subscription.
4. Correlate with event logs
| Log | Event | What it gives |
|---|---|---|
| Microsoft-Windows-WMI-Activity/Operational | 5861 | A permanent consumer bound to a filter: namespace, filter, consumer and their content |
| Microsoft-Windows-WMI-Activity/Operational | 5860 | A temporary subscription (lives only while its process runs) |
| Microsoft-Windows-WMI-Activity/Operational | 5857 | A WMI provider loaded, with its DLL path |
| Sysmon | 19, 20, 21 | Filter, consumer and binding created or deleted, with the user |
| Security | 4688 | Process creation (if enabled): children of WmiPrvSE.exe and scrcons.exe |
The repository itself has no documented creation time for its objects; these events are how you date them. The two FILETIMEs WMI Parser shows for each instance header are leads to check against them.
5. Look for execution
A subscription proves configuration, not execution. For the consumer's payload, check Prefetch, Amcache, ShimCache, SRUM and EDR telemetry; for scripts, the process tree under scrcons.exe; for command lines, processes whose parent is WmiPrvSE.exe. Line their times up with reboots (for uptime triggers) or logons (for logon triggers).
6. Check the neighbours
- MOF files and AutoRecover: a
.mofcompiled with#pragma autorecoveris replayed when the repository is rebuilt. ReviewC:\Windows\System32\wbem\AutoRecoverand theAutorecover MOFsvalue underHKLM\SOFTWARE\Microsoft\Wbem\CIMOM. - Custom classes: data or code stashed in the properties of an attacker-defined class, outside the subscription triad.
- Other hosts: the same filter and consumer names across machines point to remote deployment.
7. Clean up safely
When evidence is preserved and the scope is known, remove the three objects in the namespace where they live — binding first:
$ns = 'root/subscription'
Get-CimInstance -Namespace $ns -ClassName __FilterToConsumerBinding | Where-Object { $_.Filter.Name -eq 'IntelPerfMonitor' } | Remove-CimInstance
Get-CimInstance -Namespace $ns -ClassName CommandLineEventConsumer -Filter "Name='IntelPerfMonitor'" | Remove-CimInstance
Get-CimInstance -Namespace $ns -ClassName __EventFilter -Filter "Name='IntelPerfMonitor'" | Remove-CimInstance
Replace the names with yours (these are from the fictional sample). Then delete the payload the consumer pointed to and re-run the live query to confirm.
FAQ
Which event ID shows a new WMI persistence?
Event ID 5861 in Microsoft-Windows-WMI-Activity/Operational is logged when a permanent event consumer is bound to a filter. Sysmon, when configured, adds events 19, 20 and 21 for filters, consumers and bindings.
How do I remove WMI persistence?
After collecting evidence, remove the binding, then the consumer and the filter with Get-CimInstance … | Remove-CimInstance in the namespace where they live, and delete the payload the consumer pointed to.
Is the live query enough?
No. It shows only live objects of the namespace you query, never deleted ones, and it relies on the WMI service of a possibly compromised host. Parse the collected repository as well.