CreateProcessAsUser not setting the user correctly

1.7k views Asked by At

Calling a GUI app using

[DllImport(
    "advapi32.dll",
    EntryPoint = "CreateProcessAsUser",
    SetLastError = true,
    CharSet = CharSet.Ansi,
    CallingConvention = CallingConvention.StdCall)]
private static extern bool CreateProcessAsUser(
    IntPtr hToken,
    string lpApplicationName,
    string lpCommandLine,
    ref SECURITY_ATTRIBUTES lpProcessAttributes,
    ref SECURITY_ATTRIBUTES lpThreadAttributes,
    bool bInheritHandle,
    int dwCreationFlags,
    IntPtr lpEnvironment,
    string lpCurrentDirectory,
    ref STARTUPINFO lpStartupInfo,
    out PROCESS_INFORMATION lpProcessInformation);


bool result = CreateProcessAsUser(
    hUserTokenDup,
    null,
    applicationName + " " + arguments,
    ref sa,                 // pointer to process SECURITY_ATTRIBUTES
    ref sa,                 // pointer to thread SECURITY_ATTRIBUTES
    false,                  // handles are not inheritable
    NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE,        // creation flags
    IntPtr.Zero,            // pointer to new environment block 
    null,                   // name of current directory 
    ref si,                 // pointer to STARTUPINFO structure
    out procInfo);          // receives information about new process

from a LocalSystem Windows Service works. The window pops up in the user screen, but the process user is still LocalSystem. Is there any way to change that?

PS As requested I get hUserTokenDup from

[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
private static extern bool DuplicateTokenEx(
    IntPtr ExistingTokenHandle,
    uint dwDesiredAccess,
    ref SECURITY_ATTRIBUTES lpThreadAttributes,
    int TokenType,
    int ImpersonationLevel,
    ref IntPtr DuplicateTokenHandle);

 DuplicateTokenEx(
     hPToken,
     MAXIMUM_ALLOWED,
     ref sa,
     (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
     (int)TOKEN_TYPE.TokenPrimary,
     ref hUserTokenDup);
3

There are 3 answers

5
Remy Lebeau On BEST ANSWER

In my service, I use WTSGetActiveConsoleSessionId(), WTSQueryUserToken(), and DuplicationTokenEx() before calling CreateProcessAsUser(), and it works fine for me. The spawned process runs in the user account, not the service account.

1
joshk0 On

It looks like instead of using DuplicateTokenEx to copy the current token, you need to call LogonUser to get a token that represents the target user. Simply calling DuplicateTokenEx will create a token for the Local System user .. if I understand your code snippet correctly.

Also, since you are targetting an interactive user, consider the CreateProcessWithLogonW function instead.

0
Roland On

Thanks to Remy Lebeau for the hint to use WTSGetActiveConsoleSessionId()

The Function below have to call from the "System"-user like a win-service and needs a running physical console (a normal user-logon-session, no remote terminal session). You are able to start from a win-service a new process in a running logon-session with this user security attributes. If the caller is not "System" then its not possible to start a new process with the impersonate users.

 public static bool StartProcessAndBypassUAC(ProcessStartInfo ps, out PROCESS_INFORMATION procInfo)
 {
 // code based on http://www.codeproject.com/Articles/35773/Subverting-Vista-UAC-in-Both-32-and-64-bit-Archite
    uint winlogonPid = 0;
    IntPtr hUserTokenDup = IntPtr.Zero, hPToken = IntPtr.Zero, hProcess = IntPtr.Zero;
    procInfo = new PROCESS_INFORMATION();

    // obtain the currently active session id; every logged on user in the system has a unique session id
    uint dwSessionId = WTSGetActiveConsoleSessionId();

    if (dwSessionId == 0xFFFFFFFF)
    {
        // no  physical console
        return false;
    }

    if (!WTSQueryUserToken(dwSessionId, ref hPToken))
    {
        return false ;
    }

    // Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser
    // I would prefer to not have to use a security attribute variable and to just 
    // simply pass null and inherit (by default) the security attributes
    // of the existing token. However, in C# structures are value types and therefore
    // cannot be assigned the null value.
    SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
    sa.Length = Marshal.SizeOf(sa);

    // copy the access token of the winlogon process; the newly created token will be a primary token
    if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)TOKEN_TYPE.TokenPrimary, ref hUserTokenDup))
    {
        CloseHandle(hProcess);
        CloseHandle(hPToken);
        return false;
    }

    // By default CreateProcessAsUser creates a process on a non-interactive window station, meaning
    // the window station has a desktop that is invisible and the process is incapable of receiving
    // user input. To remedy this we set the lpDesktop parameter to indicate we want to enable user 
    // interaction with the new process.
    STARTUPINFO si = new STARTUPINFO();
    si.cb = (int)Marshal.SizeOf(si);
    si.lpDesktop = @"winsta0\default"; // interactive window station parameter; basically this indicates that the process created can display a GUI on the desktop

    // flags that specify the priority and creation method of the process
    int dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;

    // create a new process in the current user's logon session
    bool result = CreateProcessAsUser(hUserTokenDup,        // client's access token
                                    null,                   // file to execute
                                    ps.FileName,            // command line
                                    ref sa,                 // pointer to process SECURITY_ATTRIBUTES
                                    ref sa,                 // pointer to thread SECURITY_ATTRIBUTES
                                    true,                  // handles are not inheritable
                                    dwCreationFlags,        // creation flags
                                    IntPtr.Zero,            // pointer to new environment block 
                                    ps.WorkingDirectory,    // name of current directory 
                                    ref si,                 // pointer to STARTUPINFO structure
                                    out procInfo            // receives information about new process
                                    );

    // invalidate the handles
    CloseHandle(hProcess);
    CloseHandle(hPToken);
    CloseHandle(hUserTokenDup);

    return result; // return the result
 }