Status WindowsEventSubscriber::Callback(const ECRef& ec, const SCRef& sc) { Row r; FILETIME cTime; GetSystemTimeAsFileTime(&cTime); r["time"] = BIGINT(filetimeToUnixtime(cTime)); r["datetime"] = ec->eventRecord.get("Event.System.TimeCreated.<xmlattr>.SystemTime", ""); r["source"] = ec->eventRecord.get("Event.System.Channel", ""); r["provider_name"] = ec->eventRecord.get("Event.System.Provider.<xmlattr>.Name", ""); r["provider_guid"] = ec->eventRecord.get("Event.System.Provider.<xmlattr>.Guid", ""); r["eventid"] = INTEGER(ec->eventRecord.get("Event.System.EventID", -1)); r["task"] = INTEGER(ec->eventRecord.get("Event.System.Task", -1)); r["level"] = INTEGER(ec->eventRecord.get("Event.System.Level", -1)); r["keywords"] = BIGINT(ec->eventRecord.get("Event.System.Keywords", -1)); /* * From the MSDN definition of the Event Schema, each event will have * an XML choice element containing the event data, if any. The first * iteration enumerates this choice, and the second iteration enumerates * all data elements belonging to the choice. */ pt::ptree jsonOut; std::map<std::string, std::string> results; std::string eventDataType; for (const auto& node : ec->eventRecord.get_child("Event", pt::ptree())) { /// We have already processed the System event data above if (node.first == "System" || node.first == "<xmlattr>") { continue; } eventDataType = node.first; parseTree(node.second, results); } for (const auto& val : results) { /// Reconstruct the event format as much as possible jsonOut.put(eventDataType + "." + val.first, val.second); } std::stringstream ss; boost::property_tree::write_json(ss, jsonOut, false); auto s = ss.str(); if (s.at(s.size() - 1) == '\n') { s.erase(s.end()); } r["data"] = s; add(r); return Status(0, "OK"); }
/// Microsoft helper function for getting the contents of a registry key void queryKey(const std::string& hive, const std::string& key, QueryData& results) { if (kRegistryHives.count(hive) != 1) { return; } HKEY hRegistryHandle; auto ret = RegOpenKeyEx(kRegistryHives.at(hive), TEXT(key.c_str()), 0, KEY_READ, &hRegistryHandle); if (ret != ERROR_SUCCESS) { return; } const DWORD maxKeyLength = 255; const DWORD maxValueName = 16383; TCHAR achClass[MAX_PATH] = TEXT(""); DWORD cchClassName = MAX_PATH; DWORD cSubKeys = 0; DWORD cbMaxSubKey; DWORD cchMaxClass; DWORD cValues; DWORD cchMaxValueName; DWORD cbMaxValueData; DWORD cbSecurityDescriptor; DWORD retCode; FILETIME ftLastWriteTime; retCode = RegQueryInfoKey(hRegistryHandle, achClass, &cchClassName, nullptr, &cSubKeys, &cbMaxSubKey, &cchMaxClass, &cValues, &cchMaxValueName, &cbMaxValueData, &cbSecurityDescriptor, &ftLastWriteTime); TCHAR achKey[maxKeyLength]; DWORD cbName; // Process registry subkeys if (cSubKeys > 0) { for (DWORD i = 0; i < cSubKeys; i++) { cbName = maxKeyLength; retCode = RegEnumKeyEx(hRegistryHandle, i, achKey, &cbName, nullptr, nullptr, nullptr, &ftLastWriteTime); if (retCode != ERROR_SUCCESS) { continue; } Row r; fs::path keyPath(key); r["hive"] = hive; r["key"] = keyPath.string(); r["subkey"] = (keyPath / achKey).string(); r["name"] = "(Default)"; r["type"] = "REG_SZ"; r["data"] = "(value not set)"; r["mtime"] = std::to_string(filetimeToUnixtime(ftLastWriteTime)); results.push_back(r); } } if (cValues <= 0) { return; } BYTE* bpDataBuff = new BYTE[cbMaxValueData]; DWORD cchValue = maxKeyLength; TCHAR achValue[maxValueName]; // Process registry values for (size_t i = 0, retCode = ERROR_SUCCESS; i < cValues; i++) { size_t cnt = 0; ZeroMemory(bpDataBuff, cbMaxValueData); cchValue = maxValueName; achValue[0] = '\0'; retCode = RegEnumValue(hRegistryHandle, static_cast<DWORD>(i), achValue, &cchValue, nullptr, nullptr, nullptr, nullptr); if (retCode != ERROR_SUCCESS) { continue; } DWORD lpData = cbMaxValueData; DWORD lpType; retCode = RegQueryValueEx( hRegistryHandle, achValue, 0, &lpType, bpDataBuff, &lpData); if (retCode != ERROR_SUCCESS) { continue; } Row r; fs::path keyPath(key); r["hive"] = hive; r["key"] = keyPath.string(); r["subkey"] = keyPath.string(); r["name"] = achValue; if (kRegistryTypes.count(lpType) > 0) { r["type"] = kRegistryTypes.at(lpType); } else { r["type"] = "UNKNOWN"; } r["mtime"] = std::to_string(filetimeToUnixtime(ftLastWriteTime)); bpDataBuff[cbMaxValueData - 1] = 0x00; /// REG_LINK is a Unicode string, which in Windows is wchar_t char* regLinkStr = nullptr; if (lpType == REG_LINK) { regLinkStr = new char[cbMaxValueData]; const size_t newSize = cbMaxValueData; size_t convertedChars = 0; wcstombs_s(&convertedChars, regLinkStr, newSize, (wchar_t*)bpDataBuff, _TRUNCATE); } BYTE* bpDataBuffTmp = bpDataBuff; std::vector<std::string> multiSzStrs; std::vector<char> regBinary; std::string data; switch (lpType) { case REG_FULL_RESOURCE_DESCRIPTOR: case REG_RESOURCE_LIST: case REG_BINARY: for (unsigned int i = 0; i < cbMaxValueData; i++) { regBinary.push_back((char)bpDataBuff[i]); } boost::algorithm::hex( regBinary.begin(), regBinary.end(), std::back_inserter(data)); r["data"] = data; break; case REG_DWORD: r["data"] = std::to_string(*((int*)bpDataBuff)); break; case REG_DWORD_BIG_ENDIAN: r["data"] = std::to_string(_byteswap_ulong(*((int*)bpDataBuff))); break; case REG_EXPAND_SZ: r["data"] = std::string((char*)bpDataBuff); break; case REG_LINK: r["data"] = std::string(regLinkStr); break; case REG_MULTI_SZ: while (*bpDataBuffTmp != 0x00) { std::string s((char*)bpDataBuffTmp); bpDataBuffTmp += s.size() + 1; multiSzStrs.push_back(s); } r["data"] = boost::algorithm::join(multiSzStrs, ","); break; case REG_NONE: r["data"] = std::string((char*)bpDataBuff); break; case REG_QWORD: r["data"] = std::to_string(*((unsigned long long*)bpDataBuff)); break; case REG_SZ: r["data"] = std::string((char*)bpDataBuff); break; default: r["data"] = ""; break; } results.push_back(r); if (regLinkStr != nullptr) { delete (regLinkStr); } } delete (bpDataBuff); RegCloseKey(hRegistryHandle); };
void enumerateTasksForFolder(std::string path, QueryData& results) { void* vpService = nullptr; auto ret = CoCreateInstance(CLSID_TaskScheduler, nullptr, CLSCTX_INPROC_SERVER, IID_ITaskService, &vpService); if (FAILED(ret)) { VLOG(1) << "Failed to create COM instance " << ret; return; } auto pService = static_cast<ITaskService*>(vpService); ret = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t()); if (FAILED(ret)) { VLOG(1) << "Failed to connect to TaskService " << ret; pService->Release(); return; } ITaskFolder* pRootFolder = nullptr; auto widePath = stringToWstring(path); ret = pService->GetFolder(BSTR(widePath.c_str()), &pRootFolder); pService->Release(); if (FAILED(ret)) { VLOG(1) << "Failed to get root task folder " << ret; return; } IRegisteredTaskCollection* pTaskCollection = nullptr; ret = pRootFolder->GetTasks(TASK_ENUM_HIDDEN, &pTaskCollection); pRootFolder->Release(); if (FAILED(ret)) { VLOG(1) << "Failed to get task collection for root folder " << ret; return; } long numTasks = 0; pTaskCollection->get_Count(&numTasks); for (size_t i = 0; i < numTasks; i++) { IRegisteredTask* pRegisteredTask = nullptr; // Collections are 1-based lists ret = pTaskCollection->get_Item(_variant_t(i + 1), &pRegisteredTask); if (FAILED(ret)) { VLOG(1) << "Failed to process task"; continue; } Row r; BSTR taskName; ret = pRegisteredTask->get_Name(&taskName); std::wstring wTaskName(taskName, SysStringLen(taskName)); r["name"] = ret == S_OK ? SQL_TEXT(wstringToString(wTaskName.c_str())) : ""; VARIANT_BOOL enabled = false; pRegisteredTask->get_Enabled(&enabled); r["enabled"] = enabled ? INTEGER(1) : INTEGER(0); TASK_STATE taskState; pRegisteredTask->get_State(&taskState); r["state"] = kStateMap.count(taskState) > 0 ? kStateMap.at(taskState) : kStateMap.at(TASK_STATE_UNKNOWN); BSTR taskPath; ret = pRegisteredTask->get_Path(&taskPath); std::wstring wTaskPath(taskPath, SysStringLen(taskPath)); r["path"] = ret == S_OK ? wstringToString(wTaskPath.c_str()) : ""; VARIANT_BOOL hidden = false; pRegisteredTask->get_Enabled(&hidden); r["hidden"] = hidden ? INTEGER(1) : INTEGER(0); HRESULT lastTaskRun = E_FAIL; pRegisteredTask->get_LastTaskResult(&lastTaskRun); _com_error err(lastTaskRun); r["last_run_message"] = err.ErrorMessage(); r["last_run_code"] = INTEGER(lastTaskRun); // We conver the COM Date type to a unix epoch timestamp DATE dRunTime; SYSTEMTIME st; FILETIME ft; FILETIME locFt; pRegisteredTask->get_LastRunTime(&dRunTime); VariantTimeToSystemTime(dRunTime, &st); SystemTimeToFileTime(&st, &ft); LocalFileTimeToFileTime(&ft, &locFt); r["last_run_time"] = INTEGER(filetimeToUnixtime(locFt)); pRegisteredTask->get_NextRunTime(&dRunTime); VariantTimeToSystemTime(dRunTime, &st); SystemTimeToFileTime(&st, &ft); LocalFileTimeToFileTime(&ft, &locFt); r["next_run_time"] = INTEGER(filetimeToUnixtime(locFt)); ITaskDefinition* taskDef = nullptr; IActionCollection* tActionCollection = nullptr; pRegisteredTask->get_Definition(&taskDef); if (taskDef != nullptr) { taskDef->get_Actions(&tActionCollection); } long actionCount = 0; if (tActionCollection != nullptr) { tActionCollection->get_Count(&actionCount); } std::vector<std::string> actions; // Task collections are 1-indexed for (auto j = 1; j <= actionCount; j++) { IAction* act = nullptr; IExecAction* execAction = nullptr; tActionCollection->get_Item(j, &act); if (act == nullptr) { continue; } act->QueryInterface(IID_IExecAction, reinterpret_cast<void**>(&execAction)); if (execAction == nullptr) { continue; } BSTR taskExecPath; execAction->get_Path(&taskExecPath); std::wstring wTaskExecPath(taskExecPath, SysStringLen(taskExecPath)); BSTR taskExecArgs; execAction->get_Arguments(&taskExecArgs); std::wstring wTaskExecArgs(taskExecArgs, SysStringLen(taskExecArgs)); BSTR taskExecRoot; execAction->get_WorkingDirectory(&taskExecRoot); std::wstring wTaskExecRoot(taskExecRoot, SysStringLen(taskExecRoot)); auto full = wTaskExecRoot + L" " + wTaskExecPath + L" " + wTaskExecArgs; actions.push_back(wstringToString(full.c_str())); act->Release(); } if (tActionCollection != nullptr) { tActionCollection->Release(); } if (taskDef != nullptr) { taskDef->Release(); } r["action"] = !actions.empty() ? osquery::join(actions, ",") : ""; results.push_back(r); pRegisteredTask->Release(); } pTaskCollection->Release(); }
void enumerateTasksForFolder(std::string path, QueryData& results) { void* vpService = nullptr; auto ret = CoCreateInstance(CLSID_TaskScheduler, nullptr, CLSCTX_INPROC_SERVER, IID_ITaskService, &vpService); if (FAILED(ret)) { VLOG(1) << "Failed to create COM instance " << ret; return; } auto pService = static_cast<ITaskService*>(vpService); ret = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t()); if (FAILED(ret)) { VLOG(1) << "Failed to connect to TaskService " << ret; pService->Release(); return; } ITaskFolder* pRootFolder = nullptr; auto widePath = stringToWstring(path.c_str()); ret = pService->GetFolder(BSTR(widePath.c_str()), &pRootFolder); pService->Release(); if (FAILED(ret)) { VLOG(1) << "Failed to get root task folder " << ret; return; } IRegisteredTaskCollection* pTaskCollection = nullptr; ret = pRootFolder->GetTasks(TASK_ENUM_HIDDEN, &pTaskCollection); pRootFolder->Release(); if (FAILED(ret)) { VLOG(1) << "Failed to get task collection for root folder " << ret; return; } long numTasks = 0; pTaskCollection->get_Count(&numTasks); for (size_t i = 0; i < numTasks; i++) { IRegisteredTask* pRegisteredTask = nullptr; // Collections are 1-based lists ret = pTaskCollection->get_Item(_variant_t(i + 1), &pRegisteredTask); if (FAILED(ret)) { VLOG(1) << "Failed to process task"; continue; } Row r; BSTR taskName; ret = pRegisteredTask->get_Name(&taskName); std::wstring wTaskName(taskName, SysStringLen(taskName)); r["name"] = ret == S_OK ? SQL_TEXT(wstringToString(wTaskName.c_str())) : ""; VARIANT_BOOL enabled = false; pRegisteredTask->get_Enabled(&enabled); r["enabled"] = enabled ? INTEGER(1) : INTEGER(0); TASK_STATE taskState; pRegisteredTask->get_State(&taskState); r["state"] = kStateMap.count(taskState) > 0 ? kStateMap.at(taskState) : kStateMap.at(TASK_STATE_UNKNOWN); BSTR taskPath; ret = pRegisteredTask->get_Path(&taskPath); std::wstring wTaskPath(taskPath, SysStringLen(taskPath)); r["path"] = ret == S_OK ? SQL_TEXT(wstringToString(wTaskPath.c_str())) : ""; VARIANT_BOOL hidden = false; pRegisteredTask->get_Enabled(&hidden); r["hidden"] = hidden ? INTEGER(1) : INTEGER(0); HRESULT lastTaskRun = E_FAIL; pRegisteredTask->get_LastTaskResult(&lastTaskRun); _com_error err(lastTaskRun); r["last_run_message"] = err.ErrorMessage(); r["last_run_code"] = INTEGER(lastTaskRun); // We conver the COM Date type to a unix epoch timestamp DATE dRunTime; SYSTEMTIME st; FILETIME ft; FILETIME locFt; pRegisteredTask->get_LastRunTime(&dRunTime); VariantTimeToSystemTime(dRunTime, &st); SystemTimeToFileTime(&st, &ft); LocalFileTimeToFileTime(&ft, &locFt); r["last_run_time"] = INTEGER(filetimeToUnixtime(locFt)); pRegisteredTask->get_NextRunTime(&dRunTime); VariantTimeToSystemTime(dRunTime, &st); SystemTimeToFileTime(&st, &ft); LocalFileTimeToFileTime(&ft, &locFt); r["next_run_time"] = INTEGER(filetimeToUnixtime(locFt)); results.push_back(r); pRegisteredTask->Release(); } pTaskCollection->Release(); }
Status PowershellEventSubscriber::Callback(const ECRef& ec, const SCRef& sc) { // For script block logging we only care about events with script blocks auto eid = ec->eventRecord.get("Event.System.EventID", -1); if (eid != kScriptBlockLoggingEid) { return Status(); } Row results; for (const auto& node : ec->eventRecord.get_child("Event", pt::ptree())) { if (node.first == "System" || node.first == "<xmlattr>") { continue; } // #4357: This should make use of RapidJSON parseTree(node.second, results); } FILETIME etime; GetSystemTimeAsFileTime(&etime); results["time"] = BIGINT(filetimeToUnixtime(etime)); results["datetime"] = ec->eventRecord.get("Event.System.TimeCreated.<xmlattr>.SystemTime", ""); // If there's only one script block no reassembly is needed if (results["MessageTotal"] == "1") { addScriptResult(results); return Status(); } // Add the script content to the DB for later reassembly auto s = setDatabaseValue(kEvents, kScriptBlockPrefix + results["ScriptBlockId"] + "." + results["MessageNumber"], results["ScriptBlockText"]); if (!s.ok()) { LOG(WARNING) << "Failed to add new Powershell block to database for script " << results["ScriptBlockId"]; } // If we expect more blocks bail out early if (results["MessageNumber"] != results["MessageTotal"]) { return Status(); } // Otherwise all script blocks should be accounted for so reconstruct std::vector<std::string> keys; s = scanDatabaseKeys( kEvents, keys, kScriptBlockPrefix + results["ScriptBlockId"]); if (!s.ok()) { LOG(WARNING) << "Failed to look up powershell script blocks for " << results["ScriptBlockId"]; return Status(1); } std::string powershell_script{""}; for (const auto& key : keys) { std::string val{""}; s = getDatabaseValue(kEvents, key, val); if (!s.ok()) { LOG(WARNING) << "Failed to retrieve script block " << key; continue; } powershell_script += val; s = deleteDatabaseValue(kEvents, key); if (!s.ok()) { LOG(WARNING) << "Failed to delete script block key from db " << key; } } results["ScriptBlockText"] = powershell_script; addScriptResult(results); return Status(); }