1

[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);

    }
Chandru
  • 81
  • 1
  • 9
  • @DanielWiddis Thanks for the quick response. I got stuck in how to get the hToken (win32.WinNT.HANDLE) from the security context or windowsIdentity Object. Most of the answers skip this part. Please suggest me how to extract hToken value in Java way! – Chandru Jul 22 '22 at 05:52

1 Answers1

1

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.

Daniel Widdis
  • 8,424
  • 13
  • 41
  • 63
  • I have updated my question. In order to run ``CreateProcessAsUserA`` we need ``SE_INCREASE_QUOTA_NAME`` privilege. Current user doesnt possess the privilege. So I have enabled the privilege using ``AdjustTokenPrivileges`` function. But user impersonation is failed when I run the ``ImpersonateLoggedOnUser`` with ``hToken``. So process is created, but the owner is server user not the impersonated user. Could you please help me what I am missing here. Any suggestions would be grateful. Thanks! – Chandru Jul 25 '22 at 12:51
  • @chandru-t Check the value of GetLastError to see if your privilege adjustment succeeded: From [`AdjustTokenPrivileges`](https://learn.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-adjusttokenprivileges) docs: `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. – Daniel Widdis Jul 25 '22 at 15:49
  • TOKEN_ADJUST_PRIVILEGES was missing in process token. Once I included it in OpenProcessToken. It has worked! Thanks for the help! – Chandru Jul 26 '22 at 10:07