1

I need to run application in Windows with administrator rights on another user desktop.

I could do it with PsExec -i https://learn.microsoft.com/en-us/sysinternals/downloads/psexec but I want to do it in my Java application without additional exe files.

I run my code as administrator with elevated rights.

I found this article (it describes how to do it on .net):

https://www.codeproject.com/Articles/35773/Subverting-Vista-UAC-in-Both-32-and-64-bit-Archite

I translated code from article to Java but advapi32.CreateProcessAsUser returns false and I get 1314 error. Does anybody see what I missed in this code?

pom dependencies

<dependencies>
    <dependency>
        <groupId>net.java.dev.jna</groupId>
        <artifactId>jna</artifactId>
        <version>5.2.0</version>
    </dependency>
    <dependency>
        <groupId>net.java.dev.jna</groupId>
        <artifactId>jna-platform</artifactId>
        <version>5.2.0</version>
    </dependency>
</dependencies>

my code

import com.sun.jna.Native;
import com.sun.jna.platform.win32.*;

public class TestWinRunSessionId {
    public static void main(String[] args) {
        System.out.println(System.getProperty("user.name"));
        // id of the process which we use as a pointer to the target desktop (not administrator) where we will open new application from current user (administrator)
        int procId = 18160;

        WinNT.HANDLE hProcess = Kernel32.INSTANCE.OpenProcess(
                WinNT.PROCESS_ALL_ACCESS,
                false,
                procId
        );
        System.out.println(hProcess);

        WinNT.HANDLEByReference hPToken = new WinNT.HANDLEByReference();
        boolean openProcessToken = Advapi32.INSTANCE.OpenProcessToken(
                hProcess,
                WinNT.TOKEN_DUPLICATE,
                hPToken
        );
        if (!openProcessToken) {
            Kernel32.INSTANCE.CloseHandle(hProcess);
            throw new RuntimeException("1");
        }
        System.out.println(hPToken);

        WinBase.SECURITY_ATTRIBUTES sa = new WinBase.SECURITY_ATTRIBUTES();
        sa.dwLength = new WinDef.DWORD(sa.size());

        WinNT.HANDLEByReference hUserTokenDup = new WinNT.HANDLEByReference();
        boolean duplicateTokenEx = Advapi32.INSTANCE.DuplicateTokenEx(
                hPToken.getValue(),
                WinNT.TOKEN_ALL_ACCESS,
                sa,
                WinNT.SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
                WinNT.TOKEN_TYPE.TokenPrimary,
                hUserTokenDup
        );
        if (!duplicateTokenEx) {
            Kernel32.INSTANCE.CloseHandle(hProcess);
            Kernel32.INSTANCE.CloseHandle(hPToken.getValue());
            throw new RuntimeException("2");
        }
        System.out.println(hUserTokenDup);

        WinBase.STARTUPINFO si = new WinBase.STARTUPINFO();
        si.cb = new WinDef.DWORD(si.size());
        si.lpDesktop = "winsta0\\default";

        boolean result = Advapi32.INSTANCE.CreateProcessAsUser(
                hUserTokenDup.getValue(),  // client's access token
                null,             // file to execute
                "C:\\Windows\\System32\\cmd.exe",  // command line
                sa,           // pointer to process SECURITY_ATTRIBUTES
                sa,           // pointer to thread SECURITY_ATTRIBUTES
                false,            // handles are not inheritable
                WinBase.CREATE_UNICODE_ENVIRONMENT | WinBase.CREATE_NEW_CONSOLE,  // creation flags ???
                null,      // pointer to new environment block ???
                null,             // name of current directory
                si,           // pointer to STARTUPINFO structure
                new WinBase.PROCESS_INFORMATION()      // receives information about new process
        );

        System.out.println("result: " + result);
        System.out.println("error: " + Native.getLastError());
    }
}
  • See if you can get the error code out. Before calling `CreateProcessAsUser`, do `Native.setPreserveLastError(true)` somewhere and immediately after the call to `CreateProcessAsUser` call `Native.getLastError()` to get the error code. – cbr Sep 24 '19 at 12:58
  • I got 5 code. It means access denied but I ran command as Administrator. –  Sep 24 '19 at 18:31
  • I think I should research this case https://stackoverflow.com/questions/13290296/createprocessasuser-error-5 maybe it will help me. –  Sep 24 '19 at 19:15

1 Answers1

2
  1. According to the CreateProcessAsUser.hToken:

A handle to the primary token that represents a user. The handle must have the TOKEN_QUERY, TOKEN_DUPLICATE, and TOKEN_ASSIGN_PRIMARY access rights.

So, you should OpenProcessToken with TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY.

  1. The token duplicated does also not have enough permissions. You only specify the permissions of READ_CONTROL.

According to DuplicateTokenEx.dwDesiredAccess:

To request the same access rights as the existing token, specify zero.

So, you need to set securityLevel to zero.

  1. Or juse specify TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY directly at DuplicateTokenEx

According to the document, CreateProcessAsUser requires two privileges:

  1. SE_INCREASE_QUOTA_NAME
  2. SE_ASSIGNPRIMARYTOKEN_NAME

Corresponding to Control Panel\All Control Panel Items\Administrative Tools\Local Security Policy\Security Settings\Local Policies\User Rights Assignment:

  1. Adjust memory quotas for a process
  2. Replace a process level token

EDIT:

Finally, I found a way to do(The error checking was removed and pay attention to the comments inside):

#include <windows.h>
#include <iostream>
#include <stdio.h>

#pragma comment(lib, "Advapi32.lib")
int main()
{
    DWORD session_id = 0;

    //Get a system token from System process id.
    //Why? Because the following call: "SetTokenInformation" needs "the Act as part of the operating system" privilege, and local system has.
    HANDLE hSys_Process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, 588);
    HANDLE Sys_Token = 0;
    OpenProcessToken(hSys_Process, TOKEN_QUERY| TOKEN_DUPLICATE, &Sys_Token);
    CloseHandle(hSys_Process);
    HANDLE Sys_Token_Dup;
    if (!DuplicateTokenEx(Sys_Token, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &Sys_Token_Dup))
    {
        printf("DuplicateTokenEx ERROR: %d\n", GetLastError());
        return FALSE;
    }

    //Enabling Privileges: "SE_INCREASE_QUOTA_NAME" and "SE_ASSIGNPRIMARYTOKEN_NAME" for CreateProcessAsUser().
    TOKEN_PRIVILEGES *tokenPrivs=(TOKEN_PRIVILEGES*)malloc(sizeof(DWORD)+2* sizeof(LUID_AND_ATTRIBUTES));
    tokenPrivs->PrivilegeCount = 2;
    LookupPrivilegeValue(NULL, SE_INCREASE_QUOTA_NAME, &tokenPrivs->Privileges[0].Luid);
    LookupPrivilegeValue(NULL, SE_ASSIGNPRIMARYTOKEN_NAME, &tokenPrivs->Privileges[1].Luid);
    tokenPrivs->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    tokenPrivs->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
    AdjustTokenPrivileges(Sys_Token_Dup, FALSE, tokenPrivs, 0, (PTOKEN_PRIVILEGES)NULL, 0);
    free(tokenPrivs);

    //let the calling thread impersonate the local system, so that we can call SetTokenInformation().
    ImpersonateLoggedOnUser(Sys_Token_Dup);

    //get current process user token.
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, GetCurrentProcessId());
    HANDLE Token = 0, hTokenDup = 0;
    OpenProcessToken(hProcess, TOKEN_QUERY | TOKEN_DUPLICATE, &Token);
    CloseHandle(hProcess);
    if (!DuplicateTokenEx(Token, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hTokenDup))
    {
        printf("DuplicateTokenEx ERROR: %d\n", GetLastError());
        return FALSE;
    }

    //set session id to token.
    if (!SetTokenInformation(hTokenDup, TokenSessionId, &session_id, sizeof(DWORD)))
    {
        printf("SetTokenInformation Error === %d\n", GetLastError());
        return FALSE;
    }

    //init struct.
    STARTUPINFO si;
    ZeroMemory(&si, sizeof(STARTUPINFO));
    si.cb = sizeof(STARTUPINFO);
    char temp[] = "winsta0\\default";
    char applicationName[] = "C:\\Windows\\System32\\cmd.exe";
    si.lpDesktop = temp;
    PROCESS_INFORMATION procInfo;
    ZeroMemory(&procInfo, sizeof(PROCESS_INFORMATION));

    //will return error 5 without CREATE_BREAKAWAY_FROM_JOB
    //see https://blogs.msdn.microsoft.com/alejacma/2012/03/09/createprocessasuser-fails-with-error-5-access-denied-when-using-jobs/
    int dwCreationFlags = CREATE_BREAKAWAY_FROM_JOB | CREATE_NEW_CONSOLE; 


    BOOL result = CreateProcessAsUser(
        hTokenDup,
        NULL,             // file to execute
        applicationName,  // command line
        NULL,           // pointer to process SECURITY_ATTRIBUTES
        NULL,           // 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
        &procInfo      // receives information about new process
    );
    RevertToSelf();

    return 0;
}
Drake Wu
  • 6,927
  • 1
  • 7
  • 30
  • Does this help? – Drake Wu Sep 26 '19 at 15:00
  • Sorry I haven't checked it yet. As soon as I test it I will mark your answer as correct. –  Sep 29 '19 at 19:14
  • Now I get 1314 error. https://www.ibm.com/support/pages/error-1314-starting-remote-command-service –  Sep 30 '19 at 10:40
  • Finally: I tried your recommendations and got 1314 then I changed code according to the article https://www.codeproject.com/Articles/35773/Subverting-Vista-UAC-in-Both-32-and-64-bit-Archite and got 1314 (I found equivalent for MAXIMUM_ALLOWED). Now I have two places in the code that makes me confused. I marked them with "???". –  Sep 30 '19 at 18:48
  • For `dwCreationFlags` and `lpEnvironment`, see the parameter descriptions in the [`CreateProcessAsUser`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera#parameters) documentation. [Process Creation Flags](https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags) details all `dwCreationFlags` and their meanings. – Drake Wu Oct 01 '19 at 01:58
  • For error 1314, which means "A required privilege is not held by the client." You'll need to elevate the rights of the account calling `CreateProcessAsUser` with the "Replace a process level token" right. See this answer: https://social.msdn.microsoft.com/Forums/vstudio/en-US/c905c900-cae1-4081-b0c9-00f10238e7ad/createprocessasuser-failed?forum=clr. – Drake Wu Oct 01 '19 at 01:58
  • According to [`CreateProcessAsUser`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera) **If this function fails with ERROR_PRIVILEGE_NOT_HELD (1314), use the [`CreateProcessWithLogonW`](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createprocesswithlogonw) function instead.** – Drake Wu Oct 01 '19 at 01:59
  • Thanks a lot... dwCreationFlags, lpEnvironment I tried to find equivalent for .NET example from article: IntPtr.Zero and NORMAL_PRIORITY_CLASS. But I don't think that it's a cause of error. May be the source example isn't working and there are no errors in my translation to Java. –  Oct 01 '19 at 08:21
  • Yes I saw decision which describes rights elevation to "Replace a process level token". But in this case I have to make some configuration in Windows. PsExec.exe -i doing it without any configuration and it's exactly what I need. https://learn.microsoft.com/en-us/sysinternals/downloads/psexec –  Oct 01 '19 at 08:21
  • CreateProcessWithLogonW requires a username and password but I haven't user credentials. –  Oct 01 '19 at 08:22
  • Did you use `psexec -i -s` option? `-s` specified to run the process in the local system account, the local system is a highly privileged built-in account. It has extensive privileges on the local system. To add a privilege to any account, by programming, see [Assigning Privileges to an Account](https://learn.microsoft.com/en-us/windows/win32/secbp/assigning-privileges-to-an-account) with [`LsaAddAccountRights`](https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaaddaccountrights) – Drake Wu Oct 01 '19 at 08:56
  • Now I run Runtime.getRuntime().exec("PsExec.exe -i 5 my_app.exe") as administrator and it's starts my_app.exe as administrator at desktop in session #5. It's what I need and it works fine but I want do it without PsExec. –  Oct 01 '19 at 14:26
  • Yes. Ok I'll try it. –  Oct 02 '19 at 05:50
  • It seems it's not what I need because I can't set session id with CreateProcess. –  Oct 02 '19 at 19:32