免杀-无迹可寻:掌握WMI技法,打破进程链的枷锁

admin 2024年2月3日20:20:34评论67 views字数 6147阅读20分29秒阅读模式

什么是WMI?

WMI 是 Microsoft 对 WBEM 和 CIM 的实现,由 DMTF 开发和发布。WMI 提供了在企业环境中收集和发送管理信息的方法。

WMI 是一种基于Windows COM 的服务,它提供对系统相关信息的各种访问方式。

COM是Component Object Model(组件对象模型)的简称,由Windows3.11引入的,目的在于代码复用、进程间通信,广泛用于ActiveX,COM+、DCOM等框架。简单来说,COM 是一个与语言无关的库,它允许不同编程语言的应用程序相互交互。

COM信息存在注册表HKLM、HKCUSoftwareClassesCLSID中,并且HKCU加载优先于HKLM,常见键值如下:

{CLSID}
--InprocServer32 REG_SZ 模块路径
----ThreadingModel REG_SZ 线程模型

WMI 引入了一种称为“托管对象”的东西。这是计算机上由 WMI 管理的组件。托管对象是计算机的逻辑或物理部分,例如硬盘驱动器、网络适配器、数据库系统、操作系统、进程或服务。从这些托管对象检索信息的应用程序称为“消费者”。

消费者从这些称为“提供程序”的事物中查询托管对象。提供程序可以是用户模式 COM DLL,也可以是公开 .默认的 WMI 提供程序称为“标准提供程序”,这意味着我们可以创建自己的提供程序并将其注册到 WMI。此 WMI 提供程序包含在注册表中注册的关联 GUID。

更具体地说,WMI 提供程序将有关托管对象的信息作为类的实例提供,使用者不直接访问这些实例,使用者查询 WMI,然后 WMI 将该查询转发给提供程序。然后,提供者处理查询并将信息发送回 WMI,WMI 再次将其发送回使用者

查询的示例如下:

SELECT * FROM Win32_Process WHERE Name LIKE "%chrome%"

此查询将返回可执行文件名称为 chrome 的所有正在运行的进程。它是 WMI 的 WQL。如果你以前使用过 SQL 编程,会注意到语法非常相似。

免杀-无迹可寻:掌握WMI技法,打破进程链的枷锁

https://learn.microsoft.com/en-us/windows/win32/wmisdk/wmi-start-page

https://learn.microsoft.com/en-us/previous-versions/windows/desktop/regprov/system-registry-provider

进程信任链

一般创建的进程是否可信,除了对它本身的静态特征判断外,还有一个很关键的点就是它的来源。

通常是否可信可通过PKI的签名校验来判定,而建立完整的进程链的却是一个复杂的过程,一般杀软会在CreateProcessNotify回调中建立父子关系,然而进程启动有很多方式,诸如RPC、模拟点击、漏洞等等,Windows并没有提供标准的接口来监控这些点,因此通常的做法是要么放弃、要么看ETW能否监控,或者在调用点Hook。

如果一个不可信的进程执行一些敏感操作,会触发杀软的告警,通常如下:免杀-无迹可寻:掌握WMI技法,打破进程链的枷锁

所以针对攻击者而言,需要寻找一种杀软不容易监控到的方式,让其不能追溯到原始进程,将进程链断掉。

利用wmi standard providers 断链

使用标准提供程序来在wmiPRVSE.exe服务下生成我们选择的任何进程。

参考微软官方文档的代码:https://learn.microsoft.com/zh-cn/windows/win32/wmisdk/example-creating-a-wmi-application

  1. 初始化COM

  2. 创建到 WMI 命名空间的连接

  3. 设置 WMI 连接的安全级别

  4. 使用COM接口

  5. 释放打开的相关资源

#define _WIN32_DCOM

#include <iostream>
using namespace std;
#include <comdef.h>
#include <Wbemidl.h>

#pragma comment(lib, "wbemuuid.lib")

int main(int iArgCnt, char** argv)
{
  HRESULT hres;

  // Step 1: --------------------------------------------------
  // Initialize COM. ------------------------------------------

  hres = CoInitializeEx(0, COINIT_MULTITHREADED);
  if (FAILED(hres))
  {
      cout << "Failed to initialize COM library. Error code = 0x"
          << hex << hres << endl;
      return 1;                 // Program has failed.
  }

  // Step 2: --------------------------------------------------
  // Set general COM security levels --------------------------

  hres = CoInitializeSecurity(
      NULL,
      -1,                         // COM negotiates service
      NULL,                       // Authentication services
      NULL,                       // Reserved
      RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication
      RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
      NULL,                       // Authentication info
      EOAC_NONE,                   // Additional capabilities
      NULL                         // Reserved
  );


  if (FAILED(hres))
  {
      cout << "Failed to initialize security. Error code = 0x"
          << hex << hres << endl;
      CoUninitialize();
      return 1;                     // Program has failed.
  }

  // Step 3: ---------------------------------------------------
  // Obtain the initial locator to WMI -------------------------

  IWbemLocator* pLoc = NULL;

  hres = CoCreateInstance(
      CLSID_WbemLocator,
      0,
      CLSCTX_INPROC_SERVER,
      IID_IWbemLocator, (LPVOID*)&pLoc);

  if (FAILED(hres))
  {
      cout << "Failed to create IWbemLocator object. "
          << "Err code = 0x"
          << hex << hres << endl;
      CoUninitialize();
      return 1;                 // Program has failed.
  }

  // Step 4: ---------------------------------------------------
  // Connect to WMI through the IWbemLocator::ConnectServer method

  IWbemServices* pSvc = NULL;

  // Connect to the local rootcimv2 namespace
  // and obtain pointer pSvc to make IWbemServices calls.
  hres = pLoc->ConnectServer(
      _bstr_t(L"ROOT\CIMV2"),
      NULL,
      NULL,
      0,
      NULL,
      0,
      0,
      &pSvc
  );

  if (FAILED(hres))
  {
      cout << "Could not connect. Error code = 0x"
          << hex << hres << endl;
      pLoc->Release();
      CoUninitialize();
      return 1;               // Program has failed.
  }

  cout << "Connected to ROOT\CIMV2 WMI namespace" << endl;


  // Step 5: --------------------------------------------------
  // Set security levels for the proxy ------------------------

  hres = CoSetProxyBlanket(
      pSvc,                       // Indicates the proxy to set
      RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx
      RPC_C_AUTHZ_NONE,           // RPC_C_AUTHZ_xxx
      NULL,                       // Server principal name
      RPC_C_AUTHN_LEVEL_CALL,     // RPC_C_AUTHN_LEVEL_xxx
      RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
      NULL,                       // client identity
      EOAC_NONE                   // proxy capabilities
  );

  if (FAILED(hres))
  {
      cout << "Could not set proxy blanket. Error code = 0x"
          << hex << hres << endl;
      pSvc->Release();
      pLoc->Release();
      CoUninitialize();
      return 1;               // Program has failed.
  }

  // Step 6: --------------------------------------------------
  // Use the IWbemServices pointer to make requests of WMI ----

  // set up to call the Win32_Process::Create method
  BSTR MethodName = SysAllocString(L"Create");
  BSTR ClassName = SysAllocString(L"Win32_Process");

  IWbemClassObject* pClass = NULL;
  hres = pSvc->GetObject(ClassName, 0, NULL, &pClass, NULL);

  IWbemClassObject* pInParamsDefinition = NULL;
  hres = pClass->GetMethod(MethodName, 0,
      &pInParamsDefinition, NULL);

  IWbemClassObject* pClassInstance = NULL;
  hres = pInParamsDefinition->SpawnInstance(0, &pClassInstance);

  // Create the values for the in parameters
  VARIANT varCommand;
  varCommand.vt = VT_BSTR;
  varCommand.bstrVal = _bstr_t(L"notepad.exe");

  // Store the value for the in parameters
  hres = pClassInstance->Put(L"CommandLine", 0,
      &varCommand, 0);
  wprintf(L"The command is: %sn", V_BSTR(&varCommand));

  // Execute Method
  IWbemClassObject* pOutParams = NULL;
  hres = pSvc->ExecMethod(ClassName, MethodName, 0,
      NULL, pClassInstance, &pOutParams, NULL);

  if (FAILED(hres))
  {
      cout << "Could not execute method. Error code = 0x"
          << hex << hres << endl;
      VariantClear(&varCommand);
      SysFreeString(ClassName);
      SysFreeString(MethodName);
      pClass->Release();
      pClassInstance->Release();
      pInParamsDefinition->Release();
      pOutParams->Release();
      pSvc->Release();
      pLoc->Release();
      CoUninitialize();
      return 1;               // Program has failed.
  }

  // To see what the method returned,
  // use the following code. The return value will
  // be in &varReturnValue
  VARIANT varReturnValue;
  hres = pOutParams->Get(_bstr_t(L"ReturnValue"), 0,
      &varReturnValue, NULL, 0);


  // Clean up
  //--------------------------
  VariantClear(&varCommand);
  VariantClear(&varReturnValue);
  SysFreeString(ClassName);
  SysFreeString(MethodName);
  pClass->Release();
  pClassInstance->Release();
  pInParamsDefinition->Release();
  pOutParams->Release();
  pLoc->Release();
  pSvc->Release();
  CoUninitialize();
  return 0;
}

在 Process Hacker 中可以看到生成了 WmiPrvSe 服务

免杀-无迹可寻:掌握WMI技法,打破进程链的枷锁

notepad.exe以 WMiPrvSe.exe 作为其父进程

免杀-无迹可寻:掌握WMI技法,打破进程链的枷锁

原文始发于微信公众号(老鑫安全):免杀-无迹可寻:掌握WMI技法,打破进程链的枷锁

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年2月3日20:20:34
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   免杀-无迹可寻:掌握WMI技法,打破进程链的枷锁http://cn-sec.com/archives/2465784.html

发表评论

匿名网友 填写信息