Can't create process with current token - winapi

I want to create a process from my program and inject a dll into the process.
But I keep having these "Access Denied" or "A required privilege is not held by the client." errors.
This is part of my code:
HANDLE hCurrentProcess = GetCurrentProcess();
HANDLE hCurrentToken;
BOOL tokenResult = OpenProcessToken(hCurrentProcess,
TOKEN_ALL_ACCESS,
&hCurrentToken);
BOOL result = CreateProcessWithTokenW(hCurrentToken, // hToken
0, // dwLogonFlags
processToInjectInto, // lpApplicationName
NULL, // lpCommandLine
CREATE_DEFAULT_ERROR_MODE, // dwCreationFlags
NULL, // lpEnvironment
NULL, // lpCurrentDirectory
&startupInfo, // lpStartupInfo
&processInformation); // lpProcessInformation
if(!result) {
cout << GetLastError();
return 1;
}
Can someone tell my what is wrong there? In this case i'm having 1314 error code (A required privilege is not held by the client).
BTW - I'm using windows 7 x64.
Update:
The problem was that I was trying to inject from a 32bit to 64bit process.

Did you check MSDN?
The process that calls CreateProcessWithTokenW must have the
SE_IMPERSONATE_NAME privilege. If this function fails with
ERROR_PRIVILEGE_NOT_HELD (1314), use the CreateProcessAsUser or
CreateProcessWithLogonW function instead. Typically, the process that
calls CreateProcessAsUser must have the SE_INCREASE_QUOTA_NAME
privilege and may require the SE_ASSIGNPRIMARYTOKEN_NAME privilege if
the token is not assignable. CreateProcessWithLogonW requires no
special privileges, but the specified user account must be allowed to
log on interactively. Generally, it is best to use
CreateProcessWithLogonW to create a process with alternate credentials
Using the token fromOpenProcessToken(GetCurrentProcess()) is also pointless since you could just call the normal CreateProcess function...

Related

Windows process with Untrusted Integrity level

I couldn't find much information about Untrusted integrity level in Windows, and have some questions about it:
Is there a place where an untrusted integrity level process can create named objects? (mutexes, events, etc..)
Should untrusted integrity level process be able to open an existing named object, that was given a security descriptor in it's creation time with ACE with SYSTEM_MANDATORY_LABEL_NO_WRITE_UP to MandatoryLevelUntrusted? When I try it, it fails with 0xc0000022(access denied), while with MandatoryLevelLow it works great.
How do usually untrusted integrity processes communicate with their broker process? (like how does a google chrome tab communicates with the google chrome broker?)
Is there a place where an untrusted integrity level process can create
named objects? (mutexes, events, etc..)
by default - no. code with untrusted token (thread or process) can create object only in directory with Untrusted Mandatory Level - no one standard folders have this kind of label. some have Low Mandatory Level but untrusted - no.
but you can easy create this folder yourself. with Untrusted Mandatory Level and NULL DACL - untrusted code can create objects in this folder.
NTSTATUS CreateUntrustedFolder(PHANDLE phObject, PCUNICODE_STRING ObjectName)
{
ULONG cb = MAX_SID_SIZE;
PSID UntrustedSid = (PSID)alloca(MAX_SID_SIZE);
if (CreateWellKnownSid(WinUntrustedLabelSid, 0, UntrustedSid, &cb))
{
PACL Sacl = (PACL)alloca(cb += sizeof(ACL) + sizeof(ACE_HEADER) + sizeof(ACCESS_MASK));
InitializeAcl(Sacl, cb, ACL_REVISION);
if (AddMandatoryAce(Sacl, ACL_REVISION, 0, 0, UntrustedSid))
{
SECURITY_DESCRIPTOR sd;
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
SetSecurityDescriptorSacl(&sd, TRUE, Sacl, FALSE);
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, (PUNICODE_STRING)ObjectName, OBJ_CASE_INSENSITIVE|OBJ_OPENIF, &sd };
return ZwCreateDirectoryObject(phObject, DIRECTORY_ALL_ACCESS, &oa);
}
}
return STATUS_UNSUCCESSFUL;
}
about untrusted code creation - if start process at begin with token marked as untrusted integrity level - process fail to start. this when ntdll.dll try load kernel32.dll - it try open section \KnownDlls\kernel32.dll with SECTION_MAP_WRITE as well , but this object have Low Mandatory Level with SYSTEM_MANDATORY_LABEL_NO_WRITE_UP - as result untrusted code fail open this section with write access.
as result you need initially create process say with Low Mandatory Level and then set untrusted level
ULONG SetProcessUntrusted(HANDLE hProcess)
{
TOKEN_MANDATORY_LABEL tml = { { (PSID)alloca(MAX_SID_SIZE), SE_GROUP_INTEGRITY } };
ULONG cb = MAX_SID_SIZE;
HANDLE hToken;
if (!CreateWellKnownSid(WinUntrustedLabelSid, 0, tml.Label.Sid, &cb) ||
!OpenProcessToken(hProcess, TOKEN_ADJUST_DEFAULT, &hToken))
{
return GetLastError();
}
ULONG dwError = NOERROR;
if (!SetTokenInformation(hToken, TokenIntegrityLevel, &tml, sizeof(tml)))
{
dwError = GetLastError();
}
CloseHandle(hToken);
return dwError;
}
Should untrusted integrity level process be able to open an existing
named object
this depend from object label(level and mask) , code intergrity level and required access. if code intergrity level >= object label level - we can open object (if dacl let do this). otherwise need look for object label mask and required access. for example object have Low Mandatory Level with SYSTEM_MANDATORY_LABEL_NO_WRITE_UP and code Untrusted Mandatory Level - this code can open object with read and execute access, but fail open it for write access

Difference between access token taken from process than from user/pass

I am trying to understand the difference between 2 supposly identical access tokens.
One way to get access token is using OpenProcessToken(). The function returns an access token that can be used to create other processes or to impersonate.
Another way (and there are more, but lets focus these two) is to use LogonUser() function that also returns an access token that can be used to create other processes or to impersonate.
The difference is that the access token from (1) is valid as long as the process exists, while the access token in (2) is not attached to any process.
So, (1) what exactly do I get from OpenProcessToken()? Not an access token?
(2) Can I "change" the access token from OpenProcessToken() to be "detached" from the process? (i.e. still valid after the process dies)
access token from (1) is valid as long as the process exists
This is not true. The token is not "attached" to any process. Of course this isn't documented, but you can parse the pdb symbols and observe that struct _TOKEN doesn't have any links to the _EPROCESS object.
As with any kernel object, the token has a reference count, and it will be destroyed when the reference count reaches zero - but until then, it remains valid. Every _EPROCESS has a reference to the token, but not the other way around. Once you've opened the process token, it will remain valid as long as you keep it open, even if the process that you obtained it from terminates.
This is easy to test: open a token from some process (with the necessary permissions) and then close/terminate the process. You will find that you can still use the token with CreateProcessAsUserW to start a new process.
So no, there is no difference between the two tokens; in both cases you have a handle to a kernel object.
Here is demonstration code showing that the token from OpenProcessToken remains valid even after the process exits. We create an instance of notepad, open its token, wait until the process exists, and then attempt (successfully!) to use the token to start a new instance of notepad.
void demo()
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
// create new process in suspended state
// path to notepad hardcoded for for simplicity
if (CreateProcessW(L"c:\\windows\\notepad.exe", 0, 0, 0, 0, CREATE_SUSPENDED, 0, 0,&si, &pi))
{
// open process token with TOKEN_QUERY only
HANDLE hToken;
NTSTATUS status = NtOpenProcessToken(pi.hProcess, TOKEN_QUERY, &hToken);
// resume process
ResumeThread(pi.hThread);
NtClose(pi.hThread);
// close notepad
WaitForInputIdle(pi.hProcess, INFINITE);
PostThreadMessage(pi.dwThreadId, WM_QUIT, 0, 0);
// wait notepad exit
ZwWaitForSingleObject(pi.hProcess, FALSE, 0);
NtClose(pi.hProcess);
// try use "invalid" token from died process
if (0 <= status)
{
// we need TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY access for CreateProcessAsUserW
// we can at begin (in NtOpenProcessToken open it with the necessary access, this is only for demo
if (0 <= ZwDuplicateObject(NtCurrentProcess(),
hToken, NtCurrentProcess(), &hToken,
TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY, 0, DUPLICATE_CLOSE_SOURCE))
{
if (CreateProcessAsUserW(hToken, L"c:\\windows\\notepad.exe", 0, 0, 0, 0, 0, 0, 0, &si, &pi))
{
NtClose(pi.hThread);
NtClose(pi.hProcess);
}
else
{
GetLastError();
}
NtClose(hToken);
}
}
}
}

Use of lpEnvironment in CreateProcessAsUser

I need to understand the use of lpEnvironment in CreateProcessAsUser.
I have these two lines of code in my solution which in invoked by a windows service running as Local System.
CreateProcessAsUser(hTokenCpy, null, applicationName, ref saProcess, ref saThread, false, CREATE_UNICODE_ENVIRONMENT, hEnv, null, ref si, out procInfo)
which is called after calling this
CreateEnvironmentBlock(out hEnv, hTokenCpy, false) || hEnv == IntPtr.Zero)
However if the call to CreateProcessAsUser fails.
Another attempt is made by passing hEnv/IpEnvironment = 0
CreateProcessAsUser(hTokenCpy, null, applicationName, ref saProcess, ref saThread, false, 0, IntPtr.Zero, null, ref si, out procInfo
However when the process is created under a specific user name the created process fails to interact with the desktop. Even though the logon user and the user under which the process is running are same.
Is this because the process is getting created under the memory block of the Windows service instead of the logged on User ?

CreateProcessAsUser from service with proper PEB and ACL

I have read tons of SO questions on this matter, but I didn't find a real definitive guide for doing this the right way.
My goal is to enumerate [disconnected and active] user console sessions and start a process in each one of them. Every user session process requires at least these rights in its DACL :
Token access rights :
TOKEN_QUERY (for GetTokenInformation())
TOKEN_QUERY_SOURCE (for GetTokenInformation())
Process access rights :
PROCESS_QUERY_INFORMATION (for OpenProcessToken())
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ (for GetModuleFileNameEx())
PROCESS_VM_OPERATION (used with GetTokenInformation() to get other processes' username later with LookupAccountSid())
But as you can read here (at the bottom) : "Windows Vista introduces protected processes to enhance support for Digital Rights Management. The system restricts access to protected processes and the threads of protected processes."
So I thought maybe only with PROCESS_QUERY_LIMITED_INFORMATION I can get some information about other processes. I tried QueryFullProcessImageName() for elevated processes starting from Vista (see Giori's answer) but it doesn't work anymore as it seems.
Solution : CreateProcessAs_LOCAL_SYSTEM using a duplicated token of the Windows service.
Problem : The spawned processes should have the respective logged on user's environment variables set to be able to locate network printers and mapped drives among other things. But if I use the service's token I inherit its PEB and I can't even translate the mapped drives to their UNC paths.
So I started looking for ways to "elevate" the process and bypassing the UAC prompt, I tried :
Enabling some privileges like SE_DEBUG_PRIVILEGE in the token using AdjustTokenPrivileges() (does not work if the token does not have those privileges, verification can be done first using LookUpPrivilegeValue())
using the token from winlogon.exe. (does not work)
Changing the DACL (source code) (didn't work)
The steps I'm following are :
Enumerate sessions using WTSEnumerateSessions()
Get the token (two choices) :
SYSTEM token : OpenProcessToken(GetCurrentProcess(),TokenAccessLevels.MaximumAllowed, out hProcessToken)
User token : WTSQueryUserToken(sessionId, out hUserToken)
Duplicate the token using DuplicateTokenEx()
LookUpPrivilegeValue() / AdjustTokenPrivileges() (useless ?)
CreateEnvironmentBlock()
CreateProccessAsUser(), flags : NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT, Startup info's desktop : "WinSta0\Default"
Change process DACL (see link above, useless ?)
Dispose/Clean : destroy PEB created, close opened handles and free memory.
My question : how to grant the process created using CreateProccessAsUser() from a Windows Service running under LOCAL_SYSTEM account enough privileges/rights to get information on other processes (from other sessions; of other users and different integrity levels) without losing the user's environment variables ?
You're confused about a number of things.
Every user session process requires at least these rights in its DACL
The process DACL controls access to that process, it does not determine what access that process has. The security token for the process determines access rights.
Windows Vista introduces protected processes to enhance support for Digital Rights Management.
It seems clear that you haven't gotten far enough to worry about protected processes yet. Get it to work for ordinary processes first!
The spawned processes should have the respective logged on user's environment variables set to be able to locate network printers and mapped drives among other things.
Network printers and mapped drives have nothing to do with environment variables. I think what you're trying to do is to put the new process into the user's logon session, that's what controls network drive mappings and the like.
how to grant the process created using CreateProccessAsUser() [...] enough privileges/rights to get information on other processes (from other sessions; of other users and different integrity levels) without losing the user's environment variables ?
Don't. This would violate the integrity of the security model.
Instead, enumerate and query processes from the system service, and pass only whatever information is necessary to the user session processes, using shared memory (look up "file mapping object" in MSDN) or another suitable IPC mechanism.
I know that this has been asked a while ago. Since I happened to have been doing the same, below is the working pseudo-code.
First, how to run a process in a user session from a service:
//IMPORTANT: All error checks are omitted for brevity!
// Each of the lines of code below MUST be
// checked for possible errors!!!
//INFO: The following pseudo-code is intended to run
// from the Windows local service.
DWORD dwSessionID; //Session ID to run your user process in
//Get token for the session ID
HANDLE hToken;
WTSQueryUserToken(dwSessionID, &hToken);
//Duplicate this token
HANDLE hToken2;
DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hToken2);
PSID gpSidMIL_High = NULL;
if(you_want_to_change_integrity_level_for_user_process)
{
if(!Windows_XP)
{
//For example, create "high" mandaroty integrity level SID
::ConvertStringSidToSid(L"S-1-16-12288", &gpSidMIL_High);
TOKEN_MANDATORY_LABEL tml = {0};
tml.Label.Attributes = SE_GROUP_INTEGRITY;
tml.Label.Sid = gpSidMIL_High;
SetTokenInformation(hToken2, TokenIntegrityLevel, &tml,
sizeof(TOKEN_MANDATORY_LABEL) + ::GetSidLengthRequired(1));
}
}
//Copy environment strings
LPVOID pEnvBlock = NULL;
CreateEnvironmentBlock(&pEnvBlock, hToken2, FALSE);
//Initialize the STARTUPINFO structure.
// Specify that the process runs in the interactive desktop.
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.lpDesktop = _T("winsta0\\default");
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
//Create non-const buffer
TCHAR pBuffCmdLine[MAX_PATH];
pBuffCmdLine[0] = 0;
//Copy process path & parameters to the non-constant buffer
StringCchCopy(pBuffCmdLine, MAX_PATH, L"\"C:\\Program Files (x86)\\Company\\Brand\\process.exe\" -parameter");
//Impersonate the user
ImpersonateLoggedOnUser(hToken2);
//Launch the process in the user session.
bResult = CreateProcessAsUser(
hToken2, // client's access token
L"C:\\Program Files (x86)\\Company\\Brand\\process.exe", // file to execute
pBuffCmdLine[0] != 0 ? pBuffCmdLine : NULL, // command line
NULL, // pointer to process SECURITY_ATTRIBUTES
NULL, // pointer to thread SECURITY_ATTRIBUTES
FALSE, // handles are not inheritable
NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT, // creation flags
pEnvBlock, // pointer to new environment block
NULL, // name of current directory
&si, // pointer to STARTUPINFO structure
&pi // receives information about new process
);
//Get last error
nOSError = GetLastError();
//Revert to self
RevertToSelf();
//At this point you may want to wait for the user process to start, etc.
//using its handle in `pi.hProcess`
...
//Otherwise, close handles
if(pi.hProcess)
CloseHandle(pi.hProcess);
if(pi.hThread)
CloseHandle(pi.hThread);
//Clean-up
if(pEnvBlock)
DestroyEnvironmentBlock(pEnvBlock);
CloseHandle(hToken2);
CloseHandle(hToken);
if(gpSidMIL_High)
::LocalFree(gpSidMIL_High);
If you need to run your process in all sessions with a logged in interactive user, you can run the method I gave above for the sessions that you can obtain from the following enumeration:
//Enumerate all sessions
WTS_SESSION_INFO* pWSI = NULL;
DWORD nCntWSI = 0;
if(WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, NULL, 1, &pWSI, &nCntWSI))
{
//Go through all sessions
for(DWORD i = 0; i < nCntWSI; i++)
{
//To select logged in interactive user session,
//try to get its name. If you get something, then
//this session has a user logged in to...
LPTSTR pUserName = NULL;
DWORD dwcbSzUserName = 0;
if(WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE,
pWSI[i].SessionId,
WTSUserName, &pUserName, &dwcbSzUserName) &&
pUserName &&
dwcbSzUserName >= sizeof(TCHAR) &&
pUserName[0] != 0)
{
//Use my method above to run your user process
// in this session.
DWORD dwSessionID = pWSI[i].SessionId;
}
//Free mem
if(pUserName)
WTSFreeMemory(pUserName);
}
//Free mem
WTSFreeMemory(pWSI);
}

Why do we need to add SE_SHUTDOWN_NAME privilege before using ExitWindowsEx

Before we can use the ExitWindowsEx function for shutting down the computer, we must add the SE_SHUTDOWN_NAME privilege to the process like this:
HANDLE hToken = NULL;
LUID luid;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);
LookupPrivilegeValue(L"", SE_SHUTDOWN_NAME, &luid);
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
bRet = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, 0);
ExitWindowsEx(EWX_REBOOT, 0);
This works perfectly fine.
I know that this is by design, but I don't understand what's the motivation of Microsoft for deciding that a privilege must be enabled before the ExitWindowsEx function can be used. It's obviously not designed to prevent a process from rebooting the computer, because all it needs to do for rebooting is adding the SE_SHUTDOWN_NAME to the process and call ExitWindowsEx.
The Windows developpment term could have put the code above directly into the ExitWindowsEx function.
You don't need to add it (you can't add a privilege to your current token), you need to enable it.
Most privileges are disabled by default, probably to avoid an accidental use (just like the read-only attribute on files). There's no need to run around with the privilege enabled at all times, and having the ExitWindowsEx function enable it by itself would break consistency with other privilege-dependent functions.

Resources