From 28b70eedb8b2ccc59dc81594ef758d3596519316 Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Fri, 14 Oct 2022 13:21:36 +0200 Subject: [PATCH] Add winstd::system_impersonator Signed-off-by: Simon Rozman --- UnitTests/Win.cpp | 16 +++++++ include/WinStd/Win.h | 111 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 117 insertions(+), 10 deletions(-) diff --git a/UnitTests/Win.cpp b/UnitTests/Win.cpp index 15e574de..5cd90ff7 100644 --- a/UnitTests/Win.cpp +++ b/UnitTests/Win.cpp @@ -37,5 +37,21 @@ namespace UnitTests if (!lib_shell32) Assert::Fail(L"LoadLibraryEx failed"); } + + TEST_METHOD(system_impersonator) + { + winstd::win_handle processToken; + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE, processToken)) + Assert::Fail(L"OpenProcessToken failed"); + DWORD isElevated, dwLength; + if (!GetTokenInformation(processToken, TokenElevation, &isElevated, sizeof(isElevated), &dwLength)) + Assert::Fail(L"GetTokenInformation failed"); + winstd::system_impersonator system_impersonator; + // SYSTEM impersonation works in elevated processes only. + if (dwLength == sizeof(isElevated) && isElevated) + Assert::IsTrue(system_impersonator); + else + Assert::IsTrue(!system_impersonator && GetLastError() == ERROR_ACCESS_DENIED); + } }; } diff --git a/include/WinStd/Win.h b/include/WinStd/Win.h index 7d2e21f9..86003238 100644 --- a/include/WinStd/Win.h +++ b/include/WinStd/Win.h @@ -9,6 +9,7 @@ #pragma once #include "Common.h" +#include #include #include @@ -1814,10 +1815,41 @@ namespace winstd ULONG_PTR m_cookie; ///< Cookie for context deactivation }; + /// + /// Base class for thread impersonation of another security context + /// + class impersonator + { + public: + /// + /// Construct the impersonator + /// + impersonator() noexcept : m_cookie(FALSE) {} + + /// + /// Reverts to current user and destructs the impersonator + /// + /// \sa [RevertToSelf function](https://msdn.microsoft.com/en-us/library/windows/desktop/aa379317.aspx) + /// + virtual ~impersonator() + { + if (m_cookie) + RevertToSelf(); + } + + /// + /// Did impersonation succeed? + /// + operator bool () const { return m_cookie; } + + protected: + BOOL m_cookie; ///< Did impersonation succeed? + }; + /// /// Lets the calling thread impersonate the security context of a logged-on user /// - class user_impersonator + class user_impersonator : public impersonator { WINSTD_NONCOPYABLE(user_impersonator) WINSTD_NONMOVABLE(user_impersonator) @@ -1834,20 +1866,64 @@ namespace winstd { m_cookie = hToken && ImpersonateLoggedOnUser(hToken); } + }; + /// + /// Lets the calling thread impersonate the security context of the SYSTEM user + /// + class system_impersonator : public impersonator + { + WINSTD_NONCOPYABLE(system_impersonator) + WINSTD_NONMOVABLE(system_impersonator) + + public: /// - /// Reverts to current user and destructs the impersonator + /// Construct the impersonator and impersonates the SYSTEM user /// - /// \sa [RevertToSelf function](https://msdn.microsoft.com/en-us/library/windows/desktop/aa379317.aspx) - /// - virtual ~user_impersonator() + system_impersonator() noexcept { - if (m_cookie) - RevertToSelf(); - } + TOKEN_PRIVILEGES privileges = { 1, {{{ 0, 0 }, SE_PRIVILEGE_ENABLED }} }; + if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &privileges.Privileges[0].Luid) || + !ImpersonateSelf(SecurityImpersonation)) + return; - protected: - BOOL m_cookie; ///< Did impersonation succeed? + { + HANDLE h; + if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, FALSE, &h)) + goto revert; + win_handle thread_token(h); + if (!AdjustTokenPrivileges(thread_token, FALSE, &privileges, sizeof(privileges), NULL, NULL)) + goto revert; + process_snapshot process_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (!process_snapshot) + goto revert; + PROCESSENTRY32 entry = { sizeof(PROCESSENTRY32) }; + if (!Process32First(process_snapshot, &entry)) + goto revert; + while (_tcsicmp(entry.szExeFile, TEXT("winlogon.exe")) != 0) + if (!Process32Next(process_snapshot, &entry)) + goto revert; + process winlogon_process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, entry.th32ProcessID); + if (!winlogon_process) + goto revert; + if (!OpenProcessToken(winlogon_process, TOKEN_IMPERSONATE | TOKEN_DUPLICATE, &h)) + goto revert; + win_handle winlogon_token(h); + if (!DuplicateToken(winlogon_token, SecurityImpersonation, &h)) + goto revert; + win_handle duplicated_token(h); + if (!SetThreadToken(NULL, duplicated_token)) + goto revert; + } + + m_cookie = TRUE; + return; + + revert: + DWORD dwResult = GetLastError(); + RevertToSelf(); + SetLastError(dwResult); + } }; /// @@ -2308,6 +2384,21 @@ static LSTATUS RegOpenKeyExW( return s; } +/// +/// Opens the access token associated with a process +/// +/// \sa [OpenProcessToken function](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocesstoken) +/// +static BOOL OpenProcessToken(_In_ HANDLE ProcessHandle, _In_ DWORD DesiredAccess, _Inout_ winstd::win_handle &TokenHandle) +{ + HANDLE h; + if (OpenProcessToken(ProcessHandle, DesiredAccess, &h)) { + TokenHandle.attach(h); + return TRUE; + } + return FALSE; +} + #pragma warning(pop) /// @}