The issue related to the "CoInitializeSecurity" call:
hr = OLE32$CoInitializeSecurity( //Failure of this function does not necessarily mean we failed to initialize, it will fail on repeated calls, but the values from the original call are retained
NULL,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_DEFAULT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE,
NULL);
The CoInitializeSecurity function initializes the security layer and sets the specified values as the security default. If a process does not call CoInitializeSecurity, COM calls it automatically the first time an interface is marshaled or unmarshaled, registering the system default security. No default security packages are registered until then.
This function is called exactly once per process, either explicitly or implicitly. It can be called by the client, server, or both.
The issue arises when BOF.NET is initialized in a process before any other COM interface, i.e. before a Situational Awareness BOF that uses WMI has been called.
Because BOF.NET fails to call CoInitializeSecurity, COM calls it automatically and without the requisite flags to allow the use of tokens created with make_token. Subsequently, all WMI related functionalities in the beacon process that require the use of an alternate token will fail and this can't be remedied.
To fix this, I have modified the ICorRuntimeHost function in bofnet_execute.cpp by adding a call to CoInitializeSecurity. An entry for CoInitializeSecurity must be made in utils.h also.
I also added a call to CoUninitialize at the bottom of the function to close the COM library once BOF.NET is done with it. This was causing a further issue with the TrustedSec BOF's which would try to intialize COM and fail because BOF.NET had already initialized it but hadn't closed it.
static ICorRuntimeHost* loadCLR(bool v4){
BOF_LOCAL(OLE32, CoInitializeEx);
BOF_LOCAL(OLE32, CoCreateInstance);
BOF_LOCAL(OLE32, CLSIDFromString);
BOF_LOCAL(OLE32, CoUninitialize);
BOF_LOCAL(OLE32, CoInitializeSecurity);
BOF_LOCAL(KERNEL32, LoadLibraryA);
GUID IID_RTH, CLSID_RTH, IID_MH, CLSID_MH, CLSID_RH, IID_RH, IID_RHI;
HRESULT hr;
ICorRuntimeHost* result = nullptr;
ICLRMetaHost* pMetaHost = nullptr;
ICLRRuntimeInfo* pRuntimeInfo = nullptr;
ICLRRuntimeHost* pClrRuntimeHost = nullptr;
CLRCreateInstancePtr pCLRCreateInstance = nullptr;
HMODULE hMod = NULL;
CLSIDFromString(L"{cb2f6722-ab3a-11d2-9c40-00c04fa30a3e}", &IID_RTH);
CLSIDFromString(L"{cb2f6723-ab3a-11d2-9c40-00c04fa30a3e}", &CLSID_RTH);
CLSIDFromString(L"{d332db9e-b9b3-4125-8207-a14884f53216}", &IID_MH);
CLSIDFromString(L"{9280188D-0E8E-4867-B30C-7FA83884E8DE}", &CLSID_MH);
CLSIDFromString(L"{bd39d1d2-ba2f-486a-89b0-b4b0cb466891}", &IID_RHI);
CLSIDFromString(L"{90f1a06e-7712-4762-86b5-7a5eba6bdb02}", &CLSID_RH);
CLSIDFromString(L"{90f1a06c-7712-4762-86b5-7a5eba6bdb02}", &IID_RH);
if( (hMod = LoadLibraryA("mscoree.dll")) != NULL){
pCLRCreateInstance = (CLRCreateInstancePtr)GetProcAddress(hMod,"CLRCreateInstance");
if(pCLRCreateInstance == nullptr){
log("[=]Failed to get v2 ICorRuntimeHost: 0x%x, will try .NET 2 method", hr);
v4 = false;
}
}
hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
hr = CoInitializeSecurity( //Failure of this function does not necessarily mean we failed to initialize, it will fail on repeated calls, but the values from the original call are retained
NULL,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_DEFAULT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_DYNAMIC_CLOAKING,
NULL);
if(v4 && (hr = pCLRCreateInstance(CLSID_MH, IID_MH, (LPVOID*)&pMetaHost) == S_OK)){
if((hr = pMetaHost->GetRuntime(L"v4.0.30319", IID_RHI, (LPVOID*)&pRuntimeInfo)) == S_OK){
if((hr = pRuntimeInfo->GetInterface(CLSID_RH, IID_RH, (LPVOID*)&pClrRuntimeHost)) == S_OK){;
hr = pClrRuntimeHost->Start();
hr = pRuntimeInfo->GetInterface(CLSID_RTH, IID_RTH, (LPVOID *)&result);
}else{
log("Failed to get CLR runtime host: 0x%x", hr);
}
}else{
log("Failed to get v4 runtime info: 0x%x", hr);
}
}else{
if( (hr = CoCreateInstance(CLSID_RTH, nullptr, CLSCTX_ALL, IID_RTH,(LPVOID*)&result)) == S_OK){
hr = result->Start();
}else{
log("Failed to get v2 ICorRuntimeHost: 0x%x", hr);
}
}
CoUninitialize();
return result;
}
TrustedSec recently updated their Situational Awareness BOF toolkit to support/fix remote WMI queries using tokens created with make_token. See issue: https://github.com/trustedsec/CS-Situational-Awareness-BOF/issues/94
The issue related to the "CoInitializeSecurity" call:
Specifically the EOAC_NONE flag was changed to EOAC_DYNAMIC_CLOAKING so that subsequent WMI calls would use the thread token (see more here: https://learn.microsoft.com/en-us/windows/win32/api/objidl/ne-objidl-eole_authentication_capabilities)
From the MSDN page on CoInitializeSecurity(https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coinitializesecurity):
The issue arises when BOF.NET is initialized in a process before any other COM interface, i.e. before a Situational Awareness BOF that uses WMI has been called.
Because BOF.NET fails to call CoInitializeSecurity, COM calls it automatically and without the requisite flags to allow the use of tokens created with make_token. Subsequently, all WMI related functionalities in the beacon process that require the use of an alternate token will fail and this can't be remedied.
To fix this, I have modified the ICorRuntimeHost function in bofnet_execute.cpp by adding a call to CoInitializeSecurity. An entry for CoInitializeSecurity must be made in utils.h also.
I also added a call to CoUninitialize at the bottom of the function to close the COM library once BOF.NET is done with it. This was causing a further issue with the TrustedSec BOF's which would try to intialize COM and fail because BOF.NET had already initialized it but hadn't closed it.