javajnawindows-identitywafflecreateprocessasuser

What is hToken (com.sun.jna.platform.win32.WinNT.HANDLE) in CreateProcessAsUser method. How to retrieve it using windowsIdentity Object


[Reference] [1]: Create process as different user with Java and Kerberos

As mentioned in the code, we need to pass hToken in the below snippets:

Advapi32.INSTANCE.ImpersonateLoggedOnUser(/* provide the security context token */) 

In the below code primaryToken is just a empty object,

// create primary token by duplicating impersonation token
WinNT.HANDLEByReference primaryToken = new WinNT.HANDLEByReference();
boolean createProcessResult = Advapi32.INSTANCE.CreateProcessAsUser(
            primaryToken.getValue(),    /* hToken */
            null,           /* lpApplicationName */
            command,                    /* lpCommandLine */
            null,       /* lpProcessAttributes */
            null,           /* lpThreadAttributes */
            false,          /* bInheritHandles */
            WinNT.CREATE_NEW_CONSOLE | WinNT.CREATE_UNICODE_ENVIRONMENT, /* dwCreationFlags */
            environment,                /* lpEnvironment */
            processDirectory,           /* lpCurrentDirectory */
            startupInfo,                /* lpStartupInfo */
            processInfo);               /* lpProcessInformation */

These referenced code is stated as working solution, but mostly it has custom empty objects and other than getting token bytes from authorization Header. We haven't done anything with windowsIdentity object from security context.

Kindly anyone let me know how to get the hToken from the security context.

Enable SE_INCREASE_QUOTA_NAME Privilege -- Returns true

private void adjustProcessPrivilege() {
          HANDLEByReference hToken = new HANDLEByReference();
          WinNT.LUID luid = new WinNT.LUID();
          Advapi32.INSTANCE.OpenProcessToken(Kernel32.INSTANCE.GetCurrentProcess(),
              WinNT.TOKEN_ADJUST_PRIVILEGES, hToken);
          Advapi32.INSTANCE.LookupPrivilegeValue("", WinNT.SE_INCREASE_QUOTA_NAME, luid);
          WinNT.TOKEN_PRIVILEGES tp = new WinNT.TOKEN_PRIVILEGES(1);
          tp.Privileges[0] = new LUID_AND_ATTRIBUTES(luid, new DWORD(WinNT.SE_PRIVILEGE_ENABLED));
          boolean isPrivilegeAdjusted = Advapi32.INSTANCE.AdjustTokenPrivileges(hToken.getValue(), false, tp, tp.size(), null,
              new IntByReference());
          System.out.println(isPrivilegeAdjusted); //Returns True
        }

CreateProcessAsUserA -- Impersonation has failed

@GetMapping("createProcessAsUserNew")
    public String createProcessAsUserNew() {
        adjustProcessPrivilege();
        HANDLEByReference phToken = new HANDLEByReference();
        HANDLE processHandle = null;
        HANDLE threadHandle = null;
        Win32Exception err = null;
        HANDLE hToken = null;
        try {
            // open thread or process token
            threadHandle = Kernel32.INSTANCE.GetCurrentThread();
            if (!Advapi32.INSTANCE.OpenThreadToken(threadHandle, WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY | WinNT.TOKEN_ASSIGN_PRIMARY, false, phToken)) {
                int rc = Kernel32.INSTANCE.GetLastError();
                if (rc != W32Errors.ERROR_NO_TOKEN) {
                    throw new Win32Exception(rc);
                }
                processHandle = Kernel32.INSTANCE.GetCurrentProcess();
                if (!Advapi32.INSTANCE.OpenProcessToken(processHandle, WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY | WinNT.TOKEN_ASSIGN_PRIMARY, phToken)) {
                    throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
                }
            }
        }
        catch (Win32Exception e) {
            err = e;
            throw err;
        } finally {
            hToken = phToken.getValue();
            if (!WinBase.INVALID_HANDLE_VALUE.equals(hToken)) {
                try {
                    Kernel32Util.closeHandle(hToken);
                } catch (Win32Exception e) {
                    if (err == null) {
                        err = e;
                    } else {
                        err.addSuppressed(e);
                    }
                }
            }
            if (err != null) {
                throw err;
            }
        }
        
        boolean isUserImpersonated = Advapi32.INSTANCE.ImpersonateLoggedOnUser(hToken); //This returns false
        System.out.printf("User Impersonated:",isUserImpersonated);
        // create primary token by duplicating impersonation token
        WinNT.HANDLEByReference impersonateUserToken = new WinNT.HANDLEByReference();

        boolean primaryTokenResult = Advapi32.INSTANCE.DuplicateTokenEx(
                threadHandle,                                     /* hExistingToken */
                WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY | WinNT.TOKEN_ASSIGN_PRIMARY, /* dwDesiredAccess */
                null,                                           /* lpTokenAttributes */
                WinNT.SECURITY_IMPERSONATION_LEVEL.SecurityDelegation,      /* ImpersonationLevel */
                WinNT.TOKEN_TYPE.TokenPrimary,                              /* TokenType */
                impersonateUserToken);    
        
        WinBase.STARTUPINFO startupInfo = new WinBase.STARTUPINFO();
        WinBase.PROCESS_INFORMATION processInfo = new WinBase.PROCESS_INFORMATION();
    
        boolean createProcessResult = Advapi32.INSTANCE.CreateProcessAsUser(
                impersonateUserToken.getValue(),    /* hToken */
                "C:\\Windows\\notepad.exe",           /* lpApplicationName */
                null,                    /* lpCommandLine */
                null,       /* lpProcessAttributes */
                null,           /* lpThreadAttributes */
                false,          /* j */
                WinNT.CREATE_NEW_CONSOLE | WinNT.CREATE_UNICODE_ENVIRONMENT, /* dwCreationFlags */
                null,                /* lpEnvironment */
                null,           /* lpCurrentDirectory */
                startupInfo,                /* lpStartupInfo */
                processInfo);               /* lpProcessInformation */
        System.out.println(createProcessResult);
        return String.format("New Process created ",createProcessResult);

    }

Solution

  • The API Documentation for CreateProcessAsUser tells you how to get the token (bold emphasis at the end is mine):

    [in, optional] 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. ... The user represented by the token must have read and execute access to the application specified by the lpApplicationName or the lpCommandLine parameter.

    To get a primary token that represents the specified user, call the LogonUser function. Alternatively, you can call the DuplicateTokenEx function to convert an impersonation token into a primary token. This allows a server application that is impersonating a client to create a process that has the security context of the client.

    Given your focus on impersonating a client, DuplicateTokenEx looks like precisely what you need. This is done in the post to which you linked but you only quoted the first line and skipped the next code which populates that value:

    // create primary token by duplicating impersonation token
    WinNT.HANDLEByReference primaryToken = new WinNT.HANDLEByReference();
    
    boolean primaryTokenResult = Advapi32.INSTANCE.DuplicateTokenEx(
            threadToken.getValue(),                                     /* hExistingToken */
            WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY | WinNT.TOKEN_ASSIGN_PRIMARY, /* dwDesiredAccess */
            null,                                           /* lpTokenAttributes */
            WinNT.SECURITY_IMPERSONATION_LEVEL.SecurityDelegation,      /* ImpersonationLevel */
            WinNT.TOKEN_TYPE.TokenPrimary,                              /* TokenType */
            primaryToken);        
    

    As you indicate in your edit, you need to add the SE_INCREASE_QUOTA_NAME privilege. However, you are not adequately checking whether that privilege was assigned. The return value from AdjustTokenPrivileges is not sufficient:

    If the function succeeds, the return value is nonzero. To determine whether the function adjusted all of the specified privileges, call GetLastError, which returns one of the following values when the function succeeds:

    ...

    ERROR_NOT_ALL_ASSIGNED

    The token does not have one or more of the privileges specified in the NewState parameter. The function may succeed with this error value even if no privileges were adjusted.