Skip to content

Instantly share code, notes, and snippets.

@ondrasek
Last active September 23, 2022 07:23
Show Gist options
  • Save ondrasek/6b264ae0255addaf614e4e3700736186 to your computer and use it in GitHub Desktop.
Save ondrasek/6b264ae0255addaf614e4e3700736186 to your computer and use it in GitHub Desktop.
spawn-interactive-process: How to launch a process from a Windows Service running in interactive user session(s)
// 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 <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <WtsApi32.h>
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;
}
@s0i37
Copy link

s0i37 commented Nov 24, 2021

Thanks for it.
But I have managed to launch command interactively with a bit changes:

				spawnProcess(
					getTokenFromSession(&pSessionInfo[i]),
					"c:\\path\\to\\prog.exe");

And also for passing arguments:

	result = CreateProcessAsUser(
		hToken,
		NULL,
		exeFileName,
		NULL,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment