// spawn-interactive-process.cpp : Defines the entry point for the console application. // Do not forget to link against wtsapi32.lib // How to test this: use psexec from SysInternals, such as ```psexec -s c:\windows\system32\cmd.exe``` // to run CMD under NT AUTHORITY\SYSTEM account (you can confirm this by running whoami) and then // run spawn-interactive-process (feel free to disable waiting for debugger). The result is a notepad process // running under interactive user credentials and on the interactive desktop launched from a service running under // system account. #include #include #include #include void waitForDebugger() { while (!IsDebuggerPresent()) Sleep(1000); DebugBreak(); } BOOL spawnProcess(HANDLE const hUserToken, PWSTR exeFileName) { BOOL result = FALSE; DWORD lastError = 0; PROCESS_INFORMATION processInfo; STARTUPINFO startupInfo; HANDLE hToken; ZeroMemory(&processInfo, sizeof(processInfo)); ZeroMemory(&startupInfo, sizeof(startupInfo)); startupInfo.cb = sizeof(startupInfo); result = DuplicateTokenEx( hUserToken, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, TokenPrimary, &hToken); if (!result) return result; result = CreateProcessAsUser( hToken, (PWSTR) exeFileName, NULL, NULL, NULL, FALSE, CREATE_BREAKAWAY_FROM_JOB | CREATE_NEW_PROCESS_GROUP, NULL, NULL, &startupInfo, &processInfo); return result; } void dumpSession(WTS_SESSION_INFO_1* pSessionInfo) { printf("SessionId: %d\n", pSessionInfo->SessionId); printf("State: %d\n", pSessionInfo->State); printf("Domain: %ls\n", pSessionInfo->pDomainName); printf("Hostname: %ls\n", pSessionInfo->pHostName); printf("FarmName: %ls\n", pSessionInfo->pFarmName); printf("UserName: %ls\n", pSessionInfo->pUserName); printf("\n"); } HANDLE getTokenFromSession(WTS_SESSION_INFO_1* pSessionInfo) { HANDLE hUserToken = 0; WTSQueryUserToken(pSessionInfo->SessionId, &hUserToken); return hUserToken; } BOOL adjustPrivileges() { LUID tcbPrivilege; HANDLE hToken = GetCurrentProcessToken(); BOOL result = FALSE; TOKEN_PRIVILEGES tokenPrivileges; DWORD dwSize; result = LookupPrivilegeValue(NULL, SE_TCB_NAME, &tcbPrivilege); if (!result) goto error; tokenPrivileges.PrivilegeCount = 1; tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; tokenPrivileges.Privileges[0].Luid = tcbPrivilege; result = AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, sizeof(tokenPrivileges), NULL, &dwSize); goto final; error: return FALSE; final: return TRUE; } int main() { BOOL retVal = FALSE; WTS_SESSION_INFO_1 *pSessionInfo = NULL; DWORD dwLevel = 1; DWORD dwCount = 0; HANDLE hWTSServer = WTS_CURRENT_SERVER_HANDLE; waitForDebugger(); retVal = adjustPrivileges(); if (!retVal) goto error; retVal = WTSEnumerateSessionsEx( hWTSServer, &dwLevel, 0, &pSessionInfo, &dwCount); if (retVal == FALSE) goto error; for (unsigned int i = 0; i < dwCount; i++) { dumpSession(&pSessionInfo[i]); if (pSessionInfo[i].pUserName != NULL) { spawnProcess( getTokenFromSession(&pSessionInfo[i]), L"C:\\Windows\\Notepad.exe"); } } goto final; error: printf("Error: %d.\n", GetLastError()); goto final; final: if (pSessionInfo != NULL) { WTSFreeMemoryEx(WTSTypeSessionInfoLevel1, (void*)pSessionInfo, dwCount); pSessionInfo = NULL; } return 0; }