Running a process which requires admin from a window service which runs with admin credentials when a standard (non-admin) user logged on

Darren Rose 261 Reputation points
2025-02-15T19:40:03.1166667+00:00

Trying to achieve this - Running a process which requires admin from a window service which runs with admin credentials when a standard (non-admin) user logged on

Have spent many hours today trawling internet and testing various projects I found, some of them quite old now and seem to be going round in circles.

I found this which gave me some hope, https://github.com/murrayju/CreateProcessAsUser?tab=readme-ov-file, but still not working as I need.

So the users logged on to the computer will just be standard users without admin rights.

My service will be installed and running with specific credentials which include admin rights

the process I want to run needs admin rights

tried various version of above but keep coming back to an error 740?

Any thoughts please, would be much appreciated!!!

Windows 11
Windows 11
A Microsoft operating system designed for productivity, creativity, and ease of use.
10,781 questions
{count} votes

2 answers

Sort by: Most helpful
  1. Marcin Policht 36,260 Reputation points MVP
    2025-02-15T22:23:05.9666667+00:00

    Use either Group Policy or Group Policy Preferences via User Configuration and run either of them in the security context of the SYSTEM account

    More at https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/dn789189(v=ws.11)


    If the above response helps answer your question, remember to "Accept Answer" so that others in the community facing similar issues can easily find the solution. Your contribution is highly appreciated.

    hth

    Marcin

    0 comments No comments

  2. RLWA32 47,246 Reputation points
    2025-02-17T11:15:33.1566667+00:00

    @Darren Rose, Following is the code that will start a process in a standard user's interactive session running as an Administrator when it is called from a Windows Service running as SYSTEM.A couple of things to note :

    1. The code assumes that UAC is active.
    2. The profile of the Administrator account being used is loaded. This is needed for user environment variables and HKCU use by the started process.
    3. The unicode (UTF-16LE) version of Windows API functions is used. Any command line string passed to the function must be writable to avoid an possible access violation.
    4. Upon a successful return from CreateProcessAsUser the code will wait for the started process to terminate. This is to give it an opportunity to unload the user profile after it is no longer in use by the started process.
    5. Since the code will wait indefinitely for the started process to terminate you may want to use a dedicated thread in your Windows Service or otherwise advise a threadpool that the function will not return quickly.

    Following is the code -

    #include <Windows.h>
    #include <UserEnv.h>
    #include <WtsApi32.h>
    
    typedef BOOL(WINAPI* LOGONUSEREXEXW)( // LogonUserExExW
        _In_ LPCWSTR lpszUsername,
        _In_opt_ LPCWSTR lpszDomain,
        _In_opt_ LPCWSTR lpszPassword,
        _In_ DWORD dwLogonType,
        _In_ DWORD dwLogonProvider,
        _In_opt_ PTOKEN_GROUPS pTokenGroups,
        _Out_opt_ PHANDLE phToken,
        _Out_opt_ PSID* ppLogonSid,
        _Out_opt_ PVOID* ppProfileBuffer,
        _Out_opt_ LPDWORD pdwProfileLength,
        _Out_opt_ PQUOTA_LIMITS pQuotaLimits
        );
    
    typedef struct {
        DWORD               GroupCount;
        SID_AND_ATTRIBUTES  Groups[3];
    } LGEXGROUPS, * PLGEXGROUPS;
    
    DWORD GetInteractiveSession()
    {
        DWORD dwCount{}, dwSession{(DWORD) -1};
        PWTS_SESSION_INFOW pwsi{};
    
        if (WTSEnumerateSessionsW(WTS_CURRENT_SERVER, 0, 1, &pwsi, &dwCount))
        {
            for (DWORD i = 0; i < dwCount; i++)
            {
                if (pwsi[i].State == WTSActive)
                {
                    dwSession = pwsi[i].SessionId;
                    break;
                }
            }
    
            WTSFreeMemory(pwsi);
        }
    
        return dwSession;
    }
    
    BOOL LogonWithGroups( LPCWSTR lpUser,
                          LPCWSTR lpDomain,
                          LPCWSTR lpPass,
                          PTOKEN_GROUPS plogonsid,
                          PHANDLE ptoken)
    {
        BOOL ret{ FALSE };
        LOGONUSEREXEXW LogonUserExEx{};
        static HMODULE hMod = NULL;
        BYTE localSid[SECURITY_MAX_SID_SIZE]{};
        DWORD cblocalSid{ ARRAYSIZE(localSid) };
        BYTE localAccountSid[SECURITY_MAX_SID_SIZE]{};
        DWORD cblocalAccountSid{ ARRAYSIZE(localAccountSid) };
    
        if (!hMod)
            hMod = LoadLibraryW(L"advapi32.dll");
    
        if (hMod)
        {
            LogonUserExEx = (LOGONUSEREXEXW)GetProcAddress(hMod, "LogonUserExExW");
            if (LogonUserExEx)
            {
                if (CreateWellKnownSid(WinLocalSid, NULL, localSid, &cblocalSid) &&
                    CreateWellKnownSid(WinLocalAccountSid, NULL, localAccountSid, &cblocalAccountSid))
                {
                    LGEXGROUPS tg = {
                        ARRAYSIZE(tg.Groups),
                        {   {localSid, SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT},
                            {localAccountSid, SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT},
                            {plogonsid->Groups[0].Sid, SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT},
                        }
                    };
    
                    ret = LogonUserExEx(lpUser, lpDomain, lpPass,
                                        LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, (PTOKEN_GROUPS) &tg,
                                        ptoken, NULL, NULL, NULL, NULL);
                }
            }
        }
    
        return ret;
    }
    
    DWORD StartAdminUser( LPCWSTR lpUser,
                          LPCWSTR lpDomain,
                          LPCWSTR lpPass,
                          LPCWSTR lpApp,
                          LPWSTR  lpCmdline,
                          LPCWSTR lpDir,
                          int nShow)
    {
        STARTUPINFOW si{ sizeof si };
        DWORD dwSession{}, err{ ERROR_SUCCESS };
        PROCESS_INFORMATION pi{};
        HANDLE hToken{}, hCurrentUser{};
        TOKEN_LINKED_TOKEN tlt{};
        PROFILEINFOW pInfo{ sizeof pInfo };
        LPVOID pEnvironment{};
        BOOL bImpersonating{ FALSE };
    
        si.dwFlags = STARTF_USESHOWWINDOW;
        si.wShowWindow = nShow;
        pInfo.lpUserName = (LPWSTR)lpUser;
    
        __try {
            dwSession = GetInteractiveSession();
            if (dwSession == (DWORD)-1)
            {
                err = GetLastError();
                __leave;
            }
    
            if (!WTSQueryUserToken(dwSession, &hCurrentUser))
            {
                err = GetLastError();
                __leave;
            }
    
            DWORD dwLen{};
            BYTE logonsid[sizeof(TOKEN_GROUPS) + SECURITY_MAX_SID_SIZE]{};
            DWORD cblogonsid{ ARRAYSIZE(logonsid) };
    
            if (!GetTokenInformation(hCurrentUser, TokenLogonSid, logonsid, cblogonsid, &dwLen))
            {
                err = GetLastError();
                __leave;
            }
    
            if (!LogonWithGroups(lpUser, lpDomain, lpPass, (PTOKEN_GROUPS)logonsid, &hToken))
            {
                err = GetLastError();
                __leave;
            }
    
            if (!GetTokenInformation(hToken, TokenLinkedToken, &tlt, sizeof tlt, &dwLen))
            {
                err = GetLastError();
                __leave;
            }
    
            if (!SetTokenInformation(tlt.LinkedToken, TokenSessionId, &dwSession, sizeof(DWORD)))
            {
                err = GetLastError();
                __leave;
            }
    
            if (!LoadUserProfileW(tlt.LinkedToken, &pInfo))
            {
                err = GetLastError();
                __leave;
            }
    
            if (!CreateEnvironmentBlock(&pEnvironment, tlt.LinkedToken, FALSE))
            {
                err = GetLastError();
                __leave;
            }
    
            bImpersonating = ImpersonateLoggedOnUser(tlt.LinkedToken);
    
            if (!CreateProcessAsUserW(tlt.LinkedToken, lpApp, lpCmdline, nullptr, nullptr,
                                      FALSE, CREATE_UNICODE_ENVIRONMENT, pEnvironment, lpDir, &si, &pi))
            {
                err = GetLastError();
                __leave;
            }
    
            if (bImpersonating)
            {
                RevertToSelf();
                bImpersonating = FALSE;
            }
    
            WaitForSingleObject(pi.hProcess, INFINITE);
        }
        __finally {
    
            if (bImpersonating)
                RevertToSelf();
    
            if (hCurrentUser)
                CloseHandle(hCurrentUser);
    
            if (hToken)
                CloseHandle(hToken);
    
            if(pInfo.hProfile)
                UnloadUserProfile(tlt.LinkedToken, pInfo.hProfile);
    
            if (tlt.LinkedToken)
                CloseHandle(tlt.LinkedToken);
    
            if (pEnvironment)
                DestroyEnvironmentBlock(pEnvironment);
    
            if (pi.hThread)
                CloseHandle(pi.hThread);
    
            if (pi.hProcess)
                CloseHandle(pi.hProcess);
        }
    
        return err;
    }
    
    

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.