什么是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 编程,会注意到语法非常相似。
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 standard providers 断链
使用标准提供程序来在wmiPRVSE.exe服务下生成我们选择的任何进程。
参考微软官方文档的代码:https://learn.microsoft.com/zh-cn/windows/win32/wmisdk/example-creating-a-wmi-application
-
初始化COM
-
创建到 WMI 命名空间的连接
-
设置 WMI 连接的安全级别
-
使用COM接口
-
释放打开的相关资源
#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 服务
notepad.exe以 WMiPrvSe.exe 作为其父进程
原文始发于微信公众号(老鑫安全):免杀-无迹可寻:掌握WMI技法,打破进程链的枷锁
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论