README.md
Rendering markdown...
#include "pch.hpp"
#include "impersonate.hpp"
BOOL c_impersonate::token_is_not_restricted(HANDLE hToken, PBOOL pbIsNotRestricted)
{
BOOL bReturnValue = FALSE;
DWORD dwSize = 0;
PTOKEN_GROUPS pTokenGroups = NULL;
if (!GetTokenInformation(hToken, TokenRestrictedSids, NULL, dwSize, &dwSize))
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
log_err("GetTokenInformation");
goto end;
}
}
pTokenGroups = (PTOKEN_GROUPS)LocalAlloc(LPTR, dwSize);
if (!pTokenGroups)
goto end;
if (!GetTokenInformation(hToken, TokenRestrictedSids, pTokenGroups, dwSize, &dwSize))
{
log_err("GetTokenInformation");
goto end;
}
*pbIsNotRestricted = pTokenGroups->GroupCount == 0;
bReturnValue = TRUE;
end:
if (pTokenGroups)
LocalFree(pTokenGroups);
return bReturnValue;
}
BOOL c_impersonate::token_get_sid(HANDLE hToken, PSID* ppSid)
{
BOOL bReturnValue = TRUE;
DWORD dwSize = 0;
PTOKEN_USER pTokenUser = NULL;
if (!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize))
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
log_err("GetTokenInformation");
goto end;
}
}
pTokenUser = (PTOKEN_USER)LocalAlloc(LPTR, dwSize);
if (!pTokenUser)
goto end;
if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwSize, &dwSize))
{
log_err("GetTokenInformation");
goto end;
}
*ppSid = (PSID)LocalAlloc(LPTR, SECURITY_MAX_SID_SIZE);
if (!*ppSid)
goto end;
if (!CopySid(SECURITY_MAX_SID_SIZE, *ppSid, pTokenUser->User.Sid))
{
log_err("CopySid");
LocalFree(*ppSid);
goto end;
}
bReturnValue = TRUE;
end:
if (pTokenUser)
LocalFree(pTokenUser);
return bReturnValue;
}
BOOL c_impersonate::token_get_username(HANDLE hToken, LPWSTR* ppwszUsername)
{
BOOL bReturnValue = FALSE;
PSID pSid = NULL;
const DWORD dwMaxSize = 256;
WCHAR wszUsername[dwMaxSize] = { 0 };
WCHAR wszDomain[dwMaxSize] = { 0 };
DWORD dwMaxUsername = dwMaxSize;
DWORD dwMaxDomain = dwMaxSize;
SID_NAME_USE type;
if (!this->token_get_sid(hToken, &pSid))
goto end;
if (!LookupAccountSid(NULL, pSid, wszUsername, &dwMaxUsername, wszDomain, &dwMaxDomain, &type))
{
log_err("LookupAccountSid");
goto end;
}
*ppwszUsername = (LPWSTR)LocalAlloc(LPTR, (dwMaxSize * 2 + 1) * sizeof(WCHAR));
if (!*ppwszUsername)
goto end;
StringCchPrintf(*ppwszUsername, dwMaxSize * 2 + 1, L"%ws\\%ws", wszDomain, wszUsername);
bReturnValue = TRUE;
end:
if (pSid)
LocalFree(pSid);
return bReturnValue;
}
BOOL c_impersonate::token_compare_sids(PSID pSidA, PSID pSidB)
{
BOOL bReturnValue = FALSE;
LPWSTR pwszSidA = NULL;
LPWSTR pwszSidB = NULL;
if (ConvertSidToStringSid(pSidA, &pwszSidA) && ConvertSidToStringSid(pSidB, &pwszSidB))
{
bReturnValue = _wcsicmp(pwszSidA, pwszSidB) == 0;
LocalFree(pwszSidA);
LocalFree(pwszSidB);
}
else
log_err("ConvertSidToStringSid");
return bReturnValue;
}
BOOL c_impersonate::find_process_token_and_duplicate(_In_ LPCWSTR pwszTargetSid, _Out_ PHANDLE phToken, _In_opt_ LPCWSTR pwszPrivileges[], _In_ DWORD dwPrivilegeCount)
{
BOOL bReturnValue = FALSE;
PSID pTargetSid = NULL;
PVOID pBuffer = NULL;
PSYSTEM_PROCESS_INFORMATION pProcInfo = NULL;
HANDLE hProcess = NULL, hToken = NULL, hTokenDup = NULL;
DWORD dwReturnedLen = 0, dwBufSize = 0x1000, dwSessionId = 0;
PSID pSidTmp = NULL;
NTSTATUS status = STATUS_INFO_LENGTH_MISMATCH;
LPWSTR pwszUsername = NULL;
if (!ConvertStringSidToSid(pwszTargetSid, &pTargetSid))
goto end;
while (TRUE)
{
pBuffer = LocalAlloc(LPTR, dwBufSize);
if (!pBuffer || status != STATUS_INFO_LENGTH_MISMATCH)
break;
status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemProcessInformation, pBuffer, dwBufSize, &dwReturnedLen);
if (NT_SUCCESS(status))
{
pProcInfo = (PSYSTEM_PROCESS_INFORMATION)pBuffer;
while (TRUE) {
if (hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, PtrToUlong(pProcInfo->UniqueProcessId)))
{
if (OpenProcessToken(hProcess, TOKEN_QUERY | TOKEN_DUPLICATE, &hToken))
{
if (DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, TokenImpersonation, &hTokenDup))
{
if (this->token_get_sid(hTokenDup, &pSidTmp) && this->token_get_username(hTokenDup, &pwszUsername))
{
if (this->token_compare_sids(pSidTmp, pTargetSid))
{
log_debug("Found a potential Process candidate: PID=%d - Image='%ws' - User='%ws'", PtrToUlong(pProcInfo->UniqueProcessId), pProcInfo->ImageName.Buffer, pwszUsername);
BOOL bTokenIsNotRestricted = FALSE;
this->token_is_not_restricted(hTokenDup, &bTokenIsNotRestricted);
if (bTokenIsNotRestricted)
log_debug("This token is not restricted.");
else
log_debug("This token is restricted.");
if (bTokenIsNotRestricted)
{
if (pwszPrivileges && dwPrivilegeCount != 0)
{
DWORD dwPrivilegeFound = 0;
for (DWORD i = 0; i < dwPrivilegeCount; i++)
{
if (this->token_check_privilege(hTokenDup, pwszPrivileges[i], FALSE))
dwPrivilegeFound++;
}
log_debug("Found %d/%d required privileges in token.", dwPrivilegeFound, dwPrivilegeCount);
if (dwPrivilegeFound == dwPrivilegeCount)
{
log_debug("Found a valid Token candidate.");
*phToken = hTokenDup;
bReturnValue = TRUE;
}
}
else
{
log_debug("Found a valid Token.");
*phToken = hTokenDup;
bReturnValue = TRUE;
}
}
}
LocalFree(pSidTmp);
LocalFree(pwszUsername);
}
if (!bReturnValue)
CloseHandle(hTokenDup);
}
CloseHandle(hToken);
}
CloseHandle(hProcess);
}
// If we found a valid token, stop
if (bReturnValue)
break;
// If next entry is null, stop
if (!pProcInfo->NextEntryOffset)
break;
// Increment SYSTEM_PROCESS_INFORMATION pointer
pProcInfo = (PSYSTEM_PROCESS_INFORMATION)((PBYTE)pProcInfo + pProcInfo->NextEntryOffset);
}
}
LocalFree(pBuffer);
dwBufSize <<= 1;
}
end:
if (pTargetSid)
LocalFree(pTargetSid);
return bReturnValue;
}
BOOL c_impersonate::token_check_privilege(HANDLE hToken, LPCWSTR pwszPrivilege, BOOL bEnablePrivilege)
{
BOOL bReturnValue = FALSE;
DWORD dwTokenPrivilegesSize = 0, i = 0, dwPrivilegeNameLength = 0;
PTOKEN_PRIVILEGES pTokenPrivileges = NULL;
LUID_AND_ATTRIBUTES laa = { 0 };
TOKEN_PRIVILEGES tp = { 0 };
LPWSTR pwszPrivilegeNameTemp = NULL;
if (!GetTokenInformation(hToken, TokenPrivileges, NULL, dwTokenPrivilegesSize, &dwTokenPrivilegesSize))
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
log_err("GetTokenInformation");
goto end;
}
}
pTokenPrivileges = (PTOKEN_PRIVILEGES)LocalAlloc(LPTR, dwTokenPrivilegesSize);
if (!pTokenPrivileges)
goto end;
if (!GetTokenInformation(hToken, TokenPrivileges, pTokenPrivileges, dwTokenPrivilegesSize, &dwTokenPrivilegesSize))
{
log_err("GetTokenInformation");
goto end;
}
for (i = 0; i < pTokenPrivileges->PrivilegeCount; i++)
{
laa = pTokenPrivileges->Privileges[i];
dwPrivilegeNameLength = 0;
if (!LookupPrivilegeName(NULL, &(laa.Luid), NULL, &dwPrivilegeNameLength))
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
log_err("LookupPrivilegeName");
goto end;
}
}
dwPrivilegeNameLength++;
if (pwszPrivilegeNameTemp = (LPWSTR)LocalAlloc(LPTR, dwPrivilegeNameLength * sizeof(WCHAR)))
{
if (LookupPrivilegeName(NULL, &(laa.Luid), pwszPrivilegeNameTemp, &dwPrivilegeNameLength))
{
if (!_wcsicmp(pwszPrivilegeNameTemp, pwszPrivilege))
{
if (bEnablePrivilege)
{
ZeroMemory(&tp, sizeof(TOKEN_PRIVILEGES));
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = laa.Luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL))
bReturnValue = TRUE;
else
log_err("AdjustTokenPrivileges");
}
else
{
bReturnValue = TRUE;
}
break;
}
}
else
log_err("LookupPrivilegeName");
LocalFree(pwszPrivilegeNameTemp);
}
}
end:
if (pTokenPrivileges)
LocalFree(pTokenPrivileges);
return bReturnValue;
}
BOOL c_impersonate::impersonate(_In_ HANDLE hToken)
{
HANDLE hThread = GetCurrentThread(); // Pseudo handle, does not need to be closed
if (!SetThreadToken(&hThread, hToken))
{
log_err("SetThreadToken");
return FALSE;
}
return TRUE;
}
HANDLE c_impersonate::impersonate_as_local_service() {
BOOL bReturnValue = FALSE;
HANDLE hCurrentProcessToken = NULL;
HANDLE hToken = NULL;
HANDLE hCurrentThread = NULL;
if (!OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED, &hCurrentProcessToken))
{
log_err("OpenProcessToken");
goto end;
}
if (!this->token_check_privilege(hCurrentProcessToken, SE_DEBUG_NAME, TRUE))
goto end;
if (!this->token_check_privilege(hCurrentProcessToken, SE_IMPERSONATE_NAME, TRUE))
goto end;
if (!this->find_process_token_and_duplicate(L"S-1-5-19", &hToken, NULL, 0))
goto end;
log_debug("Impersonating as LOCAL SERVICE.");
if (!this->impersonate(hToken))
goto end;
bReturnValue = TRUE;
end:
if (hCurrentProcessToken)
CloseHandle(hCurrentProcessToken);
return hToken;
}
HANDLE c_impersonate::impersonate_as_system() {
BOOL bReturnValue = FALSE;
HANDLE hCurrentProcessToken = NULL;
HANDLE hToken = NULL;
HANDLE hCurrentThread = NULL;
LPCWSTR pwszPrivileges[2] = {
L"SeDebugPrivilege",
L"SeAssignPrimaryTokenPrivilege"
};
if (!OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED, &hCurrentProcessToken))
{
log_err("OpenProcessToken");
goto end;
}
if (!this->token_check_privilege(hCurrentProcessToken, SE_DEBUG_NAME, TRUE))
goto end;
if (!this->token_check_privilege(hCurrentProcessToken, SE_ASSIGNPRIMARYTOKEN_NAME, TRUE))
goto end;
if (!this->find_process_token_and_duplicate(L"S-1-5-18", &hToken, pwszPrivileges, ARRAYSIZE(pwszPrivileges)))
goto end;
log_debug("Impersonating as SYSTEM.");
if (!this->impersonate(hToken))
goto end;
bReturnValue = TRUE;
end:
if (hCurrentProcessToken)
CloseHandle(hCurrentProcessToken);
return hToken;
}
BOOL c_impersonate::is_elevated() {
BOOL fRet = FALSE;
HANDLE hToken = NULL;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
TOKEN_ELEVATION Elevation;
DWORD cbSize = sizeof(TOKEN_ELEVATION);
if (GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof(Elevation), &cbSize)) {
fRet = Elevation.TokenIsElevated;
}
}
if (hToken) {
CloseHandle(hToken);
}
return fRet;
}