3. Windows Management Instrumentation Event Subscription
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 | wmic qfe list #获取补丁信息 |
WMI结构体系
托管组件
托管组件表示为WMI对象,表示高度结构化的操作系统数据类实例,就好比是编程语言中的类。微软提供了丰富的WMI对象用来与操作系统相关的信息进行通信。我们可以通过powershell或者系统自带的wql程序进行筛选查看。
- Win32_Process:表示Windows中运行的进程。例如我们如果想要查看当前系统中运行的进程,可以通过Powershell调用WMI接口来进行查看。
1 | Get-WmiObject -Query "Select * from win32_process" |
* 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 服务填充 WMI 对象时,有两种类型的类实例。
远程传输WMI数据:Microsoft提供了两个协议用于传输WMI数据,分别是分部署组件对象DCOM和Windows远程管理WinRM。
命名空间
WMI命名空间用于对相同环境中存在的类进行分组,也用于加强安全限制。当请求连接到一个命名空间时会检查用户的权限,这决定命名空间级别的权限。命名空间是一个层次结构,类似文件系统中文件目录的层次结构。
1 | root |
- 利用WQL进行查询:在“运行”中,输入“wbemtest.exe” 。点击连接选择需要连接的命名空间,点击查询输入WQL语句。
- Schema:获取类定义,使用的基本语法形式和其它类型的查询一样,`Select * from Meta_Class Where __CLASS like "Win32_%"`
1 | Select * From Win32_Process |
- Data:获取类的实例,它返回特定类的所有实例,例如查看某个进程
Select * from Win32_Process Where Name="cmd.exe"
Event:获取一个特定事件通知属性,通常用来在一个WMI对象实例创建/修改/删除的时候给用户发送一个消息。同时允许每个查询允许添加一些附加的操作符号,如:
WITHIN:指定轮询(确定WMI轮询监控事件的频率)或分组间隔(确定WMI收集类似事件的初始事件后的时间)。间隔是一个整数值,表示着秒的数量。[由于轮询处理具备的资源特性,所以推荐把WITHIN子句中的轮询间隔设为相对高的值(大于300秒)]
GROUP:分组相同的事件,并为所有的它们生成单独的通知。
在后续的利用内容中,使用到的就是Event查询方法来达到持久化的目的,具体的演示内容我们会在后面的利用手法中详细阐述。
WMI Event Subscription权限维持
利用原理
恶意进程创建:部分 WMI 对象包括可执行的方法。例如攻击者进行横向运动时执行的一个常用方法是在 Win32_Process 类中的静态 Create 方法,此方法可以快速创建一个新的进程。wmic process call create "calc.exe"
事件触发执行:WMI 提供了一个事件系统,使用户可以使用注册事件处理函数进行创建,修改或删除任何 WMI 对象实例。例如用于监控进程创建事件并获得回调。我们可以使用WMI永久事件订阅(permanent event subscriptions)机制来触发特定操作。攻击者经常利用这个功能,在系统启动时执行后门程序,完成本地持久化。
- WMI的事件订阅包含三个核心WMI类
Filter(过滤器)类: WMI Filter用来定义触发Consumer的具体条件,包括系统启动、特定程序执行、特定时间间隔以及其他条件。
Consumer(消费者)类: WMI Consumer用来指定要执行的具体操作,包括执行命令、运行脚本、添加日志条目或者发送邮件。
FilterToConsumerBinding类: FilterToConsumerBinding用来将Consumer与Filter关联在一起。创建一个WMI永久事件订阅需要系统的管理员权限。
- WMI的事件订阅包含三个核心WMI类
事件行为案例
- __InstanceCreationEvent:实例被创建事件。若发生创建实例的事件则会增加对象数量。
1 | # 检查是否有Notepad.exe进程被创建 |
__InstanceDeletionEvent:实例删除/关闭事件
__InstanceModificationEvent:实例修改事件
更多类型的事件类可以查看:https://docs.microsoft.com/zh-cn/windows/win32/wmisdk/–instancecreationevent
利用步骤
- 利用方式一:给刚刚我们构造的查询语句加上动作,当打开记事本时打开计算器。
1 | Register-WmiEvent -Query |
我们可以思维扩展一下,如果说我们的恶意程序不想被关闭,那么只要设置监控关闭的事件,若监测到恶意程序被关闭则通过Action动作再启动一个。经过测试后,发现执行该命令后cmd窗口不能被关闭,否则监控被关闭。那么就引发一思考,这个查询命令必须在后台执行。
- 利用方式二:通过Powershell脚本的方式实现真正的持久化操作。实现的方法理解后非常简单,非常像策略路由,我们首先需要通过一个过滤器来抓取我们的感兴趣“动作”,在创建一个“行为”来执行匹配了这个动作后的操作,最后将我们的定义的动作和行为进行绑定就可以实现持久化的操作。
1 | # 定义一种方法: |
1 | # 移除WMI持久化 |
1 | # 检查各个模块是否安装,可有可无 |
步骤一:开启powershell脚本执行功能,注意使用system32权限的powershell执行命令,
Set-ExecutionPolicy -ExecutionPolicy Bypass
步骤二:将上面的代码合并后,重命名为”.PS1”后缀,通过命令
Import-Module .\<Name>.ps1
导入脚本。步骤三:安装持久化模块
Install-Persistence
- 步骤四:重启系统,等待170S左右反弹shell。注意不要忘记自己的恶意程序evil.exe要放到指定的目录下。
- 你可以通过以下powershell命令来查看当前系统启动的时间。
Get-WmiObject -Query "Select SystemUpTime from Win32_PerfFormattedData_PerfOS_System"
演示效果:这里为了更好的体现出效果,我将动作改为打开Notepad后反弹shell。2021-08-26 09.26.52.gif