[Fix #2733] Updated users table to parse all user profiles (#2737)

This commit is contained in:
Nick Anderson 2016-11-12 19:49:33 -05:00 committed by GitHub
parent b63b60e967
commit 602f413950

View File

@ -8,21 +8,19 @@
*
*/
#pragma comment(lib, "netapi32.lib")
#define _WIN32_DCOM
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
// clang-format off
#include <LM.h>
// clang-format on
#include <Shlobj.h>
#include <osquery/core.h>
#include <osquery/tables.h>
#include <osquery/logger.h>
#include "osquery/core/windows/wmi.h"
#include "osquery/core/conversions.h"
namespace osquery {
namespace tables {
@ -30,89 +28,76 @@ namespace tables {
QueryData genUsers(QueryContext& context) {
QueryData results;
// USER_INFO_3 conains generic user information
LPUSER_INFO_3 pUserBuffer = nullptr;
DWORD dwGenericUserLevel = 3;
DWORD dwPreferredMaxLength = MAX_PREFERRED_LENGTH;
DWORD dwEntriesRead = 0;
DWORD dwTotalEntries = 0;
DWORD dwResumeHandle = 0;
NET_API_STATUS nEnumStatus;
nEnumStatus = NetUserEnum(nullptr,
dwGenericUserLevel,
FILTER_NORMAL_ACCOUNT,
(LPBYTE*)&pUserBuffer,
dwPreferredMaxLength,
&dwEntriesRead,
&dwTotalEntries,
&dwResumeHandle);
// We save the original pointer to the USER_INFO_3 buff for mem management
LPUSER_INFO_3 pUserIterationBuffer = pUserBuffer;
if (pUserIterationBuffer == nullptr || nEnumStatus != NERR_Success) {
if (pUserBuffer != nullptr) {
NetApiBufferFree(pUserBuffer);
}
WmiRequest req("select * from Win32_UserProfile");
if (!req.getStatus().ok()) {
return results;
}
for (DWORD i = 0; i < dwEntriesRead; i++) {
auto& wmiResults = req.results();
for (const auto& res : wmiResults) {
Row r;
r["username"] = wstringToString(pUserIterationBuffer->usri3_name);
r["description"] = wstringToString(pUserIterationBuffer->usri3_comment);
r["uid"] = BIGINT(pUserIterationBuffer->usri3_user_id);
r["gid"] = BIGINT(pUserIterationBuffer->usri3_primary_group_id);
r["uid_signed"] = r["uid"];
r["gid_signed"] = r["gid"];
r["shell"] = "C:\\Windows\\system32\\cmd.exe";
// USER_INFO_23 contains detailed info, like the user Sid
DWORD dwDetailedUserLevel = 23;
LPUSER_INFO_23 pSidUserBuffer = nullptr;
std::string sidString;
res.GetString("LocalPath", r["directory"]);
res.GetString("SID", sidString);
r["uuid"] = sidString;
PSID sid;
auto ret = ConvertStringSidToSidA(sidString.c_str(), &sid);
if (ret == 0) {
TLOG << "Convert SID to string failed with: " << GetLastError();
}
wchar_t accntName[UNLEN] = {0};
wchar_t domName[DNLEN] = {0};
unsigned long accntNameLen = UNLEN;
unsigned long domNameLen = DNLEN;
SID_NAME_USE eUse;
/// MSDN guaruntees the string values returned is null terminated.
ret = LookupAccountSidW(
nullptr, sid, accntName, &accntNameLen, domName, &domNameLen, &eUse);
if (ret != 0) {
r["username"] = wstringToString(accntName);
} else {
TLOG << "Lookup Account by SID failed with: " << GetLastError();
}
/// USER_INFO_3 contains detailed info, like the uid
unsigned long dwDetailedUserLevel = 3;
LPUSER_INFO_3 pUserInfoBuffer = nullptr;
NET_API_STATUS nStatus;
nStatus = NetUserGetInfo(nullptr,
pUserIterationBuffer->usri3_name,
accntName,
dwDetailedUserLevel,
(LPBYTE*)&pSidUserBuffer);
if (nStatus != NERR_Success) {
if (pSidUserBuffer != nullptr) {
NetApiBufferFree(pSidUserBuffer);
pSidUserBuffer = nullptr;
reinterpret_cast<LPBYTE*>(&pUserInfoBuffer));
if (nStatus == NERR_Success) {
r["uid"] = INTEGER(pUserInfoBuffer->usri3_user_id);
r["uid_signed"] = INTEGER(pUserInfoBuffer->usri3_user_id);
r["gid"] = INTEGER(pUserInfoBuffer->usri3_primary_group_id);
r["gid_signed"] = INTEGER(pUserInfoBuffer->usri3_primary_group_id);
r["description"] =
SQL_TEXT(wstringToString(pUserInfoBuffer->usri3_comment));
} else {
/// If NetUserGetInfo fails parse the uid manually
auto sidTokens = osquery::split(sidString, "-");
if (sidTokens.size() == 8) {
r["uid"] = INTEGER(sidTokens.back());
r["uid_signed"] = INTEGER(sidTokens.back());
}
continue;
}
LPTSTR sStringSid = nullptr;
auto ret =
ConvertSidToStringSid(pSidUserBuffer->usri23_user_sid, &sStringSid);
if (ret == 0) {
if (pSidUserBuffer != nullptr) {
NetApiBufferFree(pSidUserBuffer);
}
continue;
}
r["uuid"] = sStringSid;
std::string query = "SELECT LocalPath FROM Win32_UserProfile where SID=\"" +
std::string(sStringSid) + "\"";
WmiRequest wmiRequest(query);
std::vector<WmiResultItem>& wmiResults = wmiRequest.results();
if (wmiResults.size() != 0) {
wmiResults[0].GetString("LocalPath", r["directory"]);
}
LocalFree(sStringSid);
NetApiBufferFree(pSidUserBuffer);
/// Note: there may be a way to get the users prefered shell
r["shell"] = "C:\\Windows\\system32\\cmd.exe";
results.push_back(r);
pUserIterationBuffer++;
}
NetApiBufferFree(pUserBuffer);
if (nEnumStatus == ERROR_MORE_DATA) {
LOG(WARNING)
<< "NetUserEnum contains more data: users table may be incomplete";
if (pUserInfoBuffer != nullptr) {
NetApiBufferFree(pUserInfoBuffer);
pUserInfoBuffer = nullptr;
}
}
return results;
}
}