Execute command on remote computer and show UI to logged on user

11,102

Solution 1

You're going to find this to be exceedingly difficult, if not impossible, to do. You would think it would be easy, but it isn't. Microsoft has made this purposefully difficult because it exposes a type of security vulnerability ("shatter" attacks) if you can manipulate the window manager in another user's security context.

The Session 0 Isolation feature, introduced in Windows Vista, makes it difficult for a service program (like PsExec) to interact with the console. When the Interactive Services Detection service is running the user will be prompted, when a service attempts to interact with the console, to switch to a secure desktop to interact with the service's GUI.

There's some discussion on Stackoverflow that gets down into the details of the architecture a bit.

A better way to handle this would be to have the user run a process that listens for some type of interprocess communication and acts accordingly. If you wanted to get really, really hackish you could run a simple VBScript or Powershell script that sat in the background, when the user logged-on, and waited for a file / registry entry / TCP connection / etc and then took action. You could cobble something like that together as a proof-of-concept pretty quickly.

Solution 2

As Evan said, this is purposefully made to be difficult to do, because it's so easy to exploit and use maliciously.

But you can do it, if you have enough dedication and you're willing to get your hands dirty.

I created a very robust, reliable Windows Service that runs on machines in a production environment, that executes GUI applications in the security context of logged on users under specific conditions, so that the application just appears right before the user's eyes... so my point is it is possible to do it in a robust and reliable way.

The key is the CreateProcessAsUser API function from Advapi32.dll.

And in order to use that function, you must be able to "steal" the primary access token of a logged on user. And to do that, you can use the WTSQueryUserToken API function from Wtsapi32.dll.

One of the restrictions of using the aforementioned API is that it may only be called from within the security context of Local System -- which is why PsExec.exe -si 2 \\Test-CLI-01 calc.exe works for you -- because the -s switch in there tells PsExec to run as Local System.

This is naturally one of those times when your Windows service would execute as Local System, since you require it if you want to use WTSQueryUserToken. Let it be stated that running a program or service as Local System is otherwise generally a bad idea, because Local System has unlimited access to the machine on which it's executing, so if a hacker exploits a flaw in your program, he or she can use that exploit to cause your code to take some action with all the security privileges that the Local System security context confers. (i.e., all of them.)

This is also where extreme caution must be exercised by the programmer, because it's your responsibility to dispose of these security tokens and not leak them. You (or a bad actor) could obviously use the security tokens for nefarious purposes if they are not handled properly.

Some helpful examples, taken from some C# that I wrote a while back:

Getting a user's primary access token:

WTSQueryUserToken((uint)session.SessionId, out userPrimaryAccessToken) 

Using that primary access token to start a process in their session, as that user:

CreateProcessAsUser(userPrimaryAccessToken, null, cmdLine, ref saProcessAttributes, ref saThreadAttributes, false, 0, IntPtr.Zero, null, ref si, out pi)

These are just Windows API calls... you could replicate it in any language that you wish.

Solution 3

thx for you tips and hints - what I did is the following

function startChromeRemotely($client) {
    $desktopSession = query user /server:$client | Select-String -Pattern Active
    $desktopSessionId = ($desktopSession -split '\s+')[3]
    .\PsExec.exe -i $desktopSessionId -d \\$client -u someuser-p somepassword "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
}

So basically what I do is to first get the desktop session of the logged in ("active") user and afterwards "inject" the chrome screen directly into the users session. If executed as domain admin this seems to work fine, but somehow it feels strange/unstable. Can someone tell me why ?

Thx again :)

Share:
11,102

Related videos on Youtube

pinas
Author by

pinas

Updated on September 18, 2022

Comments

  • pinas
    pinas almost 2 years

    since I could not find any proper solution online, I'll ask here.

    I have a bunch of computers where I want to execute a program and show the programs GUI to the currently logged in user. Afaik this is not possible with Powershell (Stop/Start of Windows Services is no problem) and I could not manage to get this done with PsExec.exe.

    My PsExec command looks like this - calc.exe is visible in the TaskManager but I can't see the GUI.

    C:\Users\Administrator.DEV\Desktop>PsExec.exe -i \\TEST-CLI-01 -u localUser -p userPassword -d calc.exe
    

    Short Setup description: Server: MS Server2012R2 Clients: Windows 8.1 Every System is part of the AD Domain.

    Anyone has an Idea how I could achieve this ?

    Thx a lot in advance :)

  • pinas
    pinas over 9 years
    Just out of curiosity - why is this working: PsExec.exe -si 2 \\TEST-CLI-01 calc.exe ?? I run the command as Domain Admin.
  • Michael Haephrati
    Michael Haephrati about 5 years
    Looks like WTSQueryUserToken() fails to obtain a token for the Administrator on Windows Server 2012. I wrote a small POC. It works perfectly on Windows desktop, but on Windows Server 2012, log file shows: Trying session id 1 (Console) user admin token Trying session id 2 (RDP-Tcp#27) user admin token Trying session id 65536 (RDP-Tcp) user admin token Success using system token with set user session id 1 launch SG_WinService with CreateProcessAsUser ... "C:\test\SG_WinService.exe" "ServiceIsLuncher" CreateProcessAsUser(SG_WinService) success. New process ID: 5580