1

I've tried mixing the code here and here to run an GUI exe from a service that was initialized through QtService, but whenever I run the code bellow I get an error 5 from the CreateProcessAsUser.

Also, I saw the answer to a similar question here on StackOverflow but couldn't figure out how the DACL is related with the problem, and can't use the answer by Harry Johnson because I wouldn't have the logon info from the users.

So, can someone help me understand why am I receiving the error 5 (Acess Denied) from the code below?

if(initUiWin())
    log->write("InitUiWin executed.");
else {
    QString errorNumber = QString::number(GetLastError());
    log->write("InitUiWin error: " + errorNumber);
}

-

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

    // obtain the process id of the winlogon process that
    // is running within the currently active session
    QString processName("winlogon.exe");
    DWORD winlogonPID = FindProcessId(processName.toStdWString(),dwSessionId);
    if( winlogonPID != 0 ) {
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, winlogonPID);
    HANDLE hToken;
    OpenProcessToken(hProcess,TOKEN_READ,&hToken);

    // Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser
    SECURITY_ATTRIBUTES sa;
    sa.nLength = static_cast<DWORD>(sizeof(SECURITY_ATTRIBUTES));

    // copy the access token of the winlogon process;
    // the newly created token will be a primary token
    HANDLE hUserTokenDup;
    if (!DuplicateTokenEx(hToken,MAXIMUM_ALLOWED,&sa,
                            SecurityIdentification,TokenPrimary,&hUserTokenDup)) {
        CloseHandle(hProcess);
        CloseHandle(hToken);
        return false;
    }

    // Get Handle to the interactive window station
    HWINSTA hwinsta = NULL;
    hwinsta = OpenWindowStation(
           _T(L"winsta0"),                   // the interactive window station
           FALSE,                       // handle is not inheritable
           READ_CONTROL | WRITE_DAC);   // rights to read/write the DACL
    if(hwinsta == NULL)
        return false;

    // To get the correct default desktop, set the caller's
    // window station to the interactive window station.
    if (!SetProcessWindowStation(hwinsta))
        return false;

    // Get a handle to the interactive desktop.
    HDESK hdesk = NULL;
    hdesk = OpenDesktop(
          _T(L"default"),     // the interactive window station
          0,             // no interaction with other desktop processes
          FALSE,         // handle is not inheritable
          READ_CONTROL | // request the rights to read and write the DACL
          WRITE_DAC |
          DESKTOP_WRITEOBJECTS |
          DESKTOP_READOBJECTS);
    if(hdesk == NULL)
        return false;

    // Get the SID for the client's logon session.
    PSID pSid = NULL;
    if (!GetLogonSID(hUserTokenDup, &pSid))
          return false;

    // Allow logon SID full access to interactive window station.
    if (!AddAceToWindowStation(hwinsta, pSid) )
          return false;

    // Allow logon SID full access to interactive desktop.
    if (!AddAceToDesktop(hdesk, pSid) )
          return false;

    // Impersonate client to ensure access to executable file.
    if (!ImpersonateLoggedOnUser(hUserTokenDup) )
          return false;

    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    ZeroMemory(&si, sizeof(STARTUPINFO));
    si.cb = static_cast<DWORD>(sizeof(STARTUPINFO));

    // interactive window station parameter; basically this indicates
    // that the process created can display a GUI on the desktop
    wchar_t auxBuffer[16] = L"winsta0\\default";
    si.lpDesktop = auxBuffer;

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

    // create a new process in the current User's logon session
    bool result = CreateProcessAsUser(hUserTokenDup,  // client's access token
                                    L"test-ui-systray.exe",             // file to execute
                                    NULL,  // command line
                                    &sa,           // pointer to process SECURITY_ATTRIBUTES
                                    &sa,           // 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
                                    &pi      // receives information about new process
                                    );

    if (pSid)
          FreeLogonSID(&pSid);
    if (hdesk)
          CloseDesktop(hdesk);
    if (hwinsta)
          CloseWindowStation(hwinsta);
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);
    return result;
}

return false;

}
Community
  • 1
  • 1
thiroc
  • 91
  • 2
  • 8
  • 3
    If a function (like `CreateProcessAsUser`) fails, you should call `GetLastError()` at **that point** and save the error code. At the moment you're calling other functions like `CloseDesktop`, `CloseWindowStation`, etc all of which could mess with the error code that `GetLastError()` returns. – Jonathan Potter Sep 17 '15 at 19:56
  • 1
    Is your service running as `LocalSystem`? If so, the MSDN article is pretty clear about what to do. (`WTSQueryUserToken (WTSGetActiveConsoleSessionId (), &hToken);` then `CreateProcessAsUser(hToken, ...`) If you're not running as `LocalSystem` then "there is no _legitimate_ way to obtain the logged on user's token with right Session ID". – theB Sep 17 '15 at 20:17
  • The logic for changing the window station and desktop permissions is wrong - that code changes the permissions on *your* window station and desktop, not on the window station and desktop in the user's session. But in this situation you shouldn't need to change the permissions anyway. Are you sure it is CreateProcessAsUser that is failing and not one of the other functions? – Harry Johnston Sep 17 '15 at 22:11
  • 1
    The most obvious problem is that `sa` isn't initialized. – Harry Johnston Sep 17 '15 at 22:13
  • I think that was meant for @JonathanPotter. I was asking about whether your service is running as `LocalSystem`. – theB Sep 18 '15 at 02:33
  • Yeah, I messed up while sending, @theB. Your way works as the process is created. But, the user for the process ends up being the currently logged user, and the idea was that it end up being the SYSTEM user. – thiroc Sep 18 '15 at 02:48
  • @HarryJohnston: I setup verifications for each function, but error continued to in the CreateProcessAsUser while trying to follow the [article from CodeProject](http://www.codeproject.com/Articles/35773/Subverting-Vista-UAC-in-Both-and-bit-Archite?fid=1539607&df=10000&mpp=50&prof=False&sort=Position&view=Expanded&spc=Relaxed&fr=76#xx0xx). Except when I tried to initialize a SECURITY_ATTRIBUTE copying/pasting the code from [here](https://msdn.microsoft.com/pt-br/library/windows/desktop/aa446595(v=vs.85).aspx), that way the error appeared at DuplicateTokenEx. So, it may be related to the `sa`. – thiroc Sep 18 '15 at 02:53
  • I don't think you need `sa` at all. Try `NULL`. – Harry Johnston Sep 18 '15 at 02:55
  • Same result, error 5 at DuplicateTokenEx. – thiroc Sep 18 '15 at 03:00
  • The problem might be that you're only opening the token with `TOKEN_READ` whereas CreateProcessAsUser() needs `TOKEN_QUERY`, `TOKEN_DUPLICATE`, and `TOKEN_ASSIGN_PRIMARY`. When you duplicate the token you ask to expand the rights, but I'm not sure that works for tokens. – Harry Johnston Sep 18 '15 at 03:01
  • @HarryJohnston: That was it! Thanks! (I should probably pay better attention to the documentation next time) – thiroc Sep 18 '15 at 03:14

1 Answers1

2

Putting it here for visibility. HarryJohnston answered in the comments. The problem was the flags in the OpenProcessToken, I just changed

OpenProcessToken(hProcess,TOKEN_READ,&hToken)

to

OpenProcessToken(hProcess,TOKEN_READ|TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY,&hToken)
thiroc
  • 91
  • 2
  • 8