I hope someone can help me with this! I have a similar problem as decribed in:
Impersonate standard user
I want to be able to create a process as standard user from application that runs with elevated admin privileges ( UAC Execution level: requireAdministrator ). The user starts the application by borrowing privileges from one of the administrator accounts.
I have succeeded in acquiring a handle to explorer.exe process of this user and it is stored in variable m_hExplorerProc. After that I proceed as follows:
HANDLE hProcToken = NULL;
BOOL success = OpenProcessToken(m_hExplorerProc, TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_PRIVILEGES, &hProcToken);
BOOL lookupRet = LookupPrivilegeValue(NULL, SE_ASSIGNPRIMARYTOKEN_NAME,
&(tokenPrivs.Privileges[0].Luid));
tokenPrivs.PrivilegeCount = 1;
tokenPrivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
BOOL adjustRet = AdjustTokenPrivileges(hProcToken, FALSE, &tokenPrivs, 0, NULL, NULL);
lookupRet = LookupPrivilegeValue(NULL, SE_INCREASE_QUOTA_NAME, &(tokenPrivs.Privileges[0].Luid));
tokenPrivs.PrivilegeCount = 1;
tokenPrivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
adjustRet = AdjustTokenPrivileges(hProcToken, FALSE, &tokenPrivs, 0, NULL, NULL);
HANDLE hDuplicatedToken = NULL;
success = DuplicateTokenEx(hProcToken,
TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_PRIVILEGES,
NULL,
SECURITY_IMPERSONATION_LEVEL::SecurityImpersonation,
TOKEN_TYPE::TokenPrimary,
&hDuplicatedToken);
int err = 0;
if(FALSE == success)
err = GetLastError();
LPCTSTR appName = L"C:\\testapp.exe";
PROCESS_INFORMATION procInfo;
ZeroMemory(&procInfo, sizeof(procInfo));
STARTUPINFO startupInfo;
ZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
startupInfo.wShowWindow = SW_NORMAL;
startupInfo.dwFlags = STARTF_USESHOWWINDOW;
success = CreateProcessAsUser(hDuplicatedToken, appName, NULL, NULL, NULL, FALSE,
NORMAL_PRIORITY_CLASS | CREATE_DEFAULT_ERROR_MODE,
NULL, L"C:\\", &startupInfo, &procInfo);
if(FALSE == success)
err = GetLastError();
The process is not created and last error is 1314 which translates to: "A required privilege is not held by the client".
In this code I am just trying to execute a dummy app but I eventually want to run browser which was selected as default by this user. Does someone have an idea what am I doing wrong, or perhaps suggest an alternate solution to my problem?
I know it's not cool to answer your own question but three month have passed and still no one suggested an answer.
I have not been able so solve the problem described above using function CreateProcessAsUser but I have stumbled upon an alternate method using ShellExecute from IShellDispatch2 interface which enables you to start a program as current interactive user. Complete code can be found at:
https://code.google.com/p/mulder/source/browse/trunk/Utils/nsis_stdutils/Contrib/StdUtils/ShellExecAsUser.cpp?r=327
I hope this helps someone with a similar problem!
I know this question is very old, but I would like to add something useful (I think). If you want to use CreateProcessAsUser you need privileges, which can be obtained by impersonating a powerful token. See this answer for detail.
Related
I've tried mixing the code here and here to run an GUI exe from a service that was initialized through QtService, but whenever I run the code bellow I get an error 5 from the CreateProcessAsUser.
Also, I saw the answer to a similar question here on StackOverflow but couldn't figure out how the DACL is related with the problem, and can't use the answer by Harry Johnson because I wouldn't have the logon info from the users.
So, can someone help me understand why am I receiving the error 5 (Acess Denied) from the code below?
if(initUiWin())
log->write("InitUiWin executed.");
else {
QString errorNumber = QString::number(GetLastError());
log->write("InitUiWin error: " + errorNumber);
}
-
bool initUiWin()
{
// obtain the currently active session id; every logged on
// User in the system has a unique session id
uint dwSessionId = WTSGetActiveConsoleSessionId();
// obtain the process id of the winlogon process that
// is running within the currently active session
QString processName("winlogon.exe");
DWORD winlogonPID = FindProcessId(processName.toStdWString(),dwSessionId);
if( winlogonPID != 0 ) {
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, winlogonPID);
HANDLE hToken;
OpenProcessToken(hProcess,TOKEN_READ,&hToken);
// Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser
SECURITY_ATTRIBUTES sa;
sa.nLength = static_cast<DWORD>(sizeof(SECURITY_ATTRIBUTES));
// copy the access token of the winlogon process;
// the newly created token will be a primary token
HANDLE hUserTokenDup;
if (!DuplicateTokenEx(hToken,MAXIMUM_ALLOWED,&sa,
SecurityIdentification,TokenPrimary,&hUserTokenDup)) {
CloseHandle(hProcess);
CloseHandle(hToken);
return false;
}
// Get Handle to the interactive window station
HWINSTA hwinsta = NULL;
hwinsta = OpenWindowStation(
_T(L"winsta0"), // the interactive window station
FALSE, // handle is not inheritable
READ_CONTROL | WRITE_DAC); // rights to read/write the DACL
if(hwinsta == NULL)
return false;
// To get the correct default desktop, set the caller's
// window station to the interactive window station.
if (!SetProcessWindowStation(hwinsta))
return false;
// Get a handle to the interactive desktop.
HDESK hdesk = NULL;
hdesk = OpenDesktop(
_T(L"default"), // the interactive window station
0, // no interaction with other desktop processes
FALSE, // handle is not inheritable
READ_CONTROL | // request the rights to read and write the DACL
WRITE_DAC |
DESKTOP_WRITEOBJECTS |
DESKTOP_READOBJECTS);
if(hdesk == NULL)
return false;
// Get the SID for the client's logon session.
PSID pSid = NULL;
if (!GetLogonSID(hUserTokenDup, &pSid))
return false;
// Allow logon SID full access to interactive window station.
if (!AddAceToWindowStation(hwinsta, pSid) )
return false;
// Allow logon SID full access to interactive desktop.
if (!AddAceToDesktop(hdesk, pSid) )
return false;
// Impersonate client to ensure access to executable file.
if (!ImpersonateLoggedOnUser(hUserTokenDup) )
return false;
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = static_cast<DWORD>(sizeof(STARTUPINFO));
// interactive window station parameter; basically this indicates
// that the process created can display a GUI on the desktop
wchar_t auxBuffer[16] = L"winsta0\\default";
si.lpDesktop = auxBuffer;
// flags that specify the priority and creation method of the process
int dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_BREAKAWAY_FROM_JOB;
// create a new process in the current User's logon session
bool result = CreateProcessAsUser(hUserTokenDup, // client's access token
L"test-ui-systray.exe", // file to execute
NULL, // command line
&sa, // pointer to process SECURITY_ATTRIBUTES
&sa, // pointer to thread SECURITY_ATTRIBUTES
false, // handles are not inheritable
dwCreationFlags, // creation flags
NULL, // pointer to new environment block
NULL, // name of current directory
&si, // pointer to STARTUPINFO structure
&pi // receives information about new process
);
if (pSid)
FreeLogonSID(&pSid);
if (hdesk)
CloseDesktop(hdesk);
if (hwinsta)
CloseWindowStation(hwinsta);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return result;
}
return false;
}
Putting it here for visibility. HarryJohnston answered in the comments. The problem was the flags in the OpenProcessToken, I just changed
OpenProcessToken(hProcess,TOKEN_READ,&hToken)
to
OpenProcessToken(hProcess,TOKEN_READ|TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY,&hToken)
My goal is to copy privileges from one primary user token to another before I start a user mode process with the destination token. I created a sample pseudo code to illustrate what I need to accomplish.
The following will be run from a local system service:
//dwSessionId = user session ID to run process in
HANDLE hToken1 = NULL; //Source user token
WTSQueryUserToken(dwSessionId, &hToken1);
HANDLE hSelfToken = NULL; //User token for system service
HANDLE hToken2 = NULL; //Adjusted self-token
OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &hSelfToken);
DuplicateTokenEx(hSelfToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS,
NULL, SecurityIdentification, TokenPrimary, &hToken2);
//Specify user session to run in
SetTokenInformation(hToken2, TokenSessionId, &dwSessionId, sizeof(dwSessionId));
//Now I need to set privileges in 'hToken2' as they are in 'hToken1'
...
//Then use 'hToken2' in CreateProcessAsUser() to start a process
Any idea how to copy privileges from hToken1 to hToken2?
Use the following code to obtain hToken2 from hToken, and then use it in CreateProcessAsUser. There is no need to use hSelfToken, actually.
HANDLE GetAdjustedToken(HANDLE hSrcToken)
{
TOKEN_LINKED_TOKEN admin = {};
HANDLE hTarToken = 0;
DWORD dw = 0;
if (GetTokenInformation(hSrcToken, (TOKEN_INFORMATION_CLASS)TokenLinkedToken, &admin, sizeof(TOKEN_LINKED_TOKEN), &dw))
{
hTarToken = admin.LinkedToken;
}
else
{
DuplicateTokenEx(hSrcToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hTarToken);
}
return hTarToken;
}
And if you want to create the new process at a low mandatory integrity level, see this MSDN article.
First you have to call GetTokenInformation using TokenPrivileges class for your original token to obtain TOKEN_PRIVILEGES structure. Then call AdjustTokenPrivileges with it to update your cloned token. You will have to call GetTokenInformation twice - first one to obtain buffer length and second - to get actual data.
HANDLE hSelfToken = NULL; //User token for system service
HANDLE hToken2 = NULL; //Adjusted self-token
OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &hSelfToken);
DuplicateTokenEx(hSelfToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS,
NULL, SecurityIdentification, TokenPrimary, &hToken2);
PTOKEN_PRIVILEGES pPriv = NULL;
DWORD dwLen = 0;
GetTokenInformation(hSelfToken, TokenPrivileges, (LPVOID)pPriv, 0, &dwLen);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
return(0);
pPriv = (PTOKEN_PRIVILEGES)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLen);
if (!GetTokenInformation(hSelfToken, TokenPrivileges, (LPVOID)pPriv, dwLen, &dwLen))
return(0);
AdjustTokenPrivileges(hToken2, FALSE, pPriv, dwLen, NULL, NULL);
OK. I might have gotten something close to what I needed. It's not really copying privileges, but for the purpose of "stripping out" privileges I can create a restricted token that will have almost all privileges removed (except SeChangeNotifyPrivilege and SeSystemtimePrivilege.)
//Call the following instead of DuplicateTokenEx() in my example above
CreateRestrictedToken(hSelfToken, DISABLE_MAX_PRIVILEGE,
0, NULL, 0, NULL, 0, NULL,
&hToken2);
FYI: Just a curious fact. For some reason I was not able to remove the SeSystemtimePrivilege from the token. Not with this method, nor using AdjustTokenPrivileges with its attribute set to SE_PRIVILEGE_REMOVED. All other privileges could be removed just fine, except this one. So if anyone have any idea why, I'd be glad to learn it?
I use the CreateProcess() function to launch the rdp client app using "mstsc.exe". After that, I want to terminate it so I use TerminateProcess() function, but it fails with error code of 5. If I replace the "mstsc.exe" with "notepad.exe", the terminate function works. The code are as follows:
TCHAR szCommandLine[] = TEXT("mstsc.exe");
STARTUPINFO si = {sizeof(si)};
PROCESS_INFORMATION pi;
BOOL bResult = CreateProcess(NULL, szCommandLine, NULL, NULL,
FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
::Sleep(3000);
if (TerminateProcess(pi.hProcess, 0) == 0) {
printf("failed: %d", GetLastError());
}
Can anyone help explain it and solve it?
What I observed is that the pid of the pi returned is different from the id of the process "mstsc.exe" observed in taksmanager or "Process Explorer".
Is your host process 32-bit and you are running on 64-bit windows?
If so, you are invoking the 32-bit mstsc and it is spawning a 64-bit version, hence the different PID. Check out this thread
You must gain the privilege before terminating another process.
Try this:
void UpdatePrivilege(void)
{
HANDLE hToken;
TOKEN_PRIVILEGES tp;
LUID luid;
if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,&hToken))
{
LookupPrivilegeValue(NULL,SE_DEBUG_NAME, &luid);
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
}
}
Call this function before calling TerminateProcess.
I noticed that GoogleToolbarNotifier.exe cannot be killed from Process Explorer. It returns "Access Denied". It runs as the user, it runs "Normal" priority, and it runs from Program Files.
How did they do it?
I think there might be a way to modify the ACL, or mark the process as 'critical', but I cannot seem to locate anything.
Update:
I found the answer with a good bit of digging. #Alex K. was correct in that PROCESS_TERMINATE permission was removed for the process, but I wanted to supply the answer in code:
static const bool ProtectProcess()
{
HANDLE hProcess = GetCurrentProcess();
EXPLICIT_ACCESS denyAccess = {0};
DWORD dwAccessPermissions = GENERIC_WRITE|PROCESS_ALL_ACCESS|WRITE_DAC|DELETE|WRITE_OWNER|READ_CONTROL;
BuildExplicitAccessWithName( &denyAccess, _T("CURRENT_USER"), dwAccessPermissions, DENY_ACCESS, NO_INHERITANCE );
PACL pTempDacl = NULL;
DWORD dwErr = 0;
dwErr = SetEntriesInAcl( 1, &denyAccess, NULL, &pTempDacl );
// check dwErr...
dwErr = SetSecurityInfo( hProcess, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, pTempDacl, NULL );
// check dwErr...
LocalFree( pTempDacl );
CloseHandle( hProcess );
return dwErr == ERROR_SUCCESS;
}
The code given in the question is misleading. It constructs a DACL with no allow entries and one deny entry; that might make sense if you were applying the DACL to a file with inheritance enabled, but in this case the deny entry is redundant. In the Windows access control model, if a DACL exists but contains no matching ACE, access is implicitly denied.
Here's my version, which applies an empty DACL, denying all access. (Note that it returns an error code rather than a boolean.)
DWORD ProtectProcess(void)
{
HANDLE hProcess = GetCurrentProcess();
PACL pEmptyDacl;
DWORD dwErr;
// using malloc guarantees proper alignment
pEmptyDacl = (PACL)malloc(sizeof(ACL));
if (!InitializeAcl(pEmptyDacl, sizeof(ACL), ACL_REVISION))
{
dwErr = GetLastError();
}
else
{
dwErr = SetSecurityInfo(hProcess, SE_KERNEL_OBJECT,
DACL_SECURITY_INFORMATION, NULL, NULL, pEmptyDacl, NULL);
}
free(pEmptyDacl);
return dwErr;
}
When running my copy of that has Deny set on the Terminate permission (Process Explorer shows this).
Presumably they call SetKernelObjectSecurity to change/remove the ACLs when their process loads.
I have tried to do it with the help of writing windows services ..and then making some changes
here is the link to write a simple windows service
http://code.msdn.microsoft.com/windowsdesktop/CppWindowsService-cacf4948
and we can update Servicabase.cpp file with the following two statements..
fCanStop=FALSE;
fCanShutdown=FALSE;
I am trying to use the TerminateProcess to terminate an app launched by ShellExecuteEX like this:
SHELLEXECUTEINFO ExecuteInfo;
ExecuteInfo.fMask = SEE_MASK_FLAG_NO_UI; /* Odd but true */
ExecuteInfo.hwnd = NULL;
ExecuteInfo.cbSize = sizeof(ExecuteInfo);
ExecuteInfo.lpVerb = NULL;
ExecuteInfo.lpFile = "http://www.microsoft.com";
ExecuteInfo.lpParameters = "";
ExecuteInfo.lpDirectory = NULL;
ExecuteInfo.nShow = SW_SHOW;;
ShellExecuteEx(&ExecuteInfo);
//WaitForSingleObject(ExecuteInfo.hProcess, 0);
Sleep(4000);
TerminateProcess(ExecuteInfo.hProcess, 0);
IE gets opened but it never closes. Am I doing something wrong?
According to MSDN, fMask must be set to SEE_MASK_NOCLOSEPROCESS for .hProcess to get set. I would add a test to see if it is NULL. As a side note I've always had better luck using CreateProcess.
Edit:
This is how you do it using CreateProcess:
PROCESS_INFORMATION pi = {0};
STARTUPINFO si = {0};
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
CreateProcess( NULL,
"C:\\Program Files\\Internet Explorer\\iexplore.exe http://www.google.com/",
NULL,
NULL,
FALSE,
NORMAL_PRIORITY_CLASS,
NULL,
NULL,
&si,
&pi );
Sleep(4000);
TerminateProcess(pi.hProcess, 0);
You should add error-checking and could query the path of the default browser using: AssocQueryString like this:
AssocQueryString(0,ASSOCSTR_EXECUTABLE,"http","open", szExe, &cchExe);
You can get more information by checking the returned hProcess (e.g., in a debugger). Also make sure you have the SEE_MASK_NOCLOSEPROCESS flag set.
But my psychic powers say: Opening an IE document doesn't necessarily create a new process. And, if it does, it may not be the one you think it is: The process you may have created may have spawned yet another process to actually host the document. Raymond Chen mentions that about halfway down this blog post.