1

I have an .hta application that accepts two command line arguments. Executing the application on the remote machine via command line works just like the following

(command line - cmd)Example:

C:\Users\<user>\Desktop>MSI-BUILDER.hta "MSI_APP" "D:\APP\15.9.98"

But when using my desktop trying to execute the same command on the remote machine via PSEXEC I see the application running in task manager but nothing happens.

The first steps were assigning the command line arguments to variables in PowerShell:

$arg1 = "MSI_APP"
$arg2 = "D:\APP\15.9.98"

and executing this command in PowerShell fails (no errors, just hangs):

.\psexec -s -i \\Srv2012 cmd /c "start /i "MSI-BUILDER" "C:\Users\<user>\Desktop\MSI-BUILDER.hta" $arg1 $arg2"

The application launches just in task manager and I'm assuming in the background but I don't see any changes, so it might just be hanging...

Im not 100% sure if my syntax in this matter is correct for launching an HTA via powershell with command-line arguments.

Also is there a way or switch that I can actually see the application launch and do its thing after remotely executing the code, for testing purposes?

mklement0
  • 382,024
  • 64
  • 607
  • 775
demo7up
  • 530
  • 5
  • 27
  • 2
    "is there a way or switch that I can actually see the application launch and do its thing after remotely executing the code" - No. A remotely executed application will not be visible to the interactive user's GUI on a remote machine. This is by design for security reasons. – Bill_Stewart Sep 17 '19 at 18:16
  • 1
    @Bill_Stewart: `psexec` _can_ remotely launch applications visibly in a locally logged-on user's desktop session, namely via the `-i` option - see [the docs](https://learn.microsoft.com/en-us/sysinternals/downloads/psexec). – mklement0 Sep 17 '19 at 21:32
  • Open up a Command Prompt window and enter both `start /?` and `cmd /?` to find out how those particular commands work. You'll note that the `start` command will require a title, an empty one will do, `start "" ...`, you'll also see a section under `cmd /?` which explains about how it handles doublequotes with `/C`. However, you shouldn't need to run your HTML application via `cmd.exe` or use its internal `start` command at all. – Compo Sep 17 '19 at 23:56
  • 1
    @mklement0 Thanks for the notice. It can do this because it installs a service on the remote machine and then invokes the executable in the console (or a specified) session. – Bill_Stewart Sep 18 '19 at 13:39

2 Answers2

2

The invocation of your *.hta file can be simplified, by passing its path and arguments directly to mshta.exe (this also prevents a console window from appearing briefly if you launch via cmd/ c):

# From PowerShell (-accepteula omitted for brevity)
# IMPORTANT: You must use the *.hta file's FULL PATH.
.\psexec \\SERVER -i -s mshta.exe "D:\path\toapp\app.hta" $arg1 $arg2

By default, psexec runs synchronously, i.e. it doesn't return until the remote process exits, which is necessary if you want to know the process' exit code.[1]

In your case, the process doesn't exit until the HTA application window closes, so I assume that yours automatically closes when invoked with command-line arguments - if it doesn't, psexec wouldn't return until the interactive user closes the window when invoked with -i, or not at all without -i.

In cases where you need asynchronous execution, use the -d option; this makes psexec return after successfully launching the target process, without waiting for it to exit. Since you then by definition cannot know the true exit code of the launched process, all that psexec will tell you is whether the process could be created.

Caveat: Running interactively (-i) with -s, i.e. as the NT AUTHORITY\SYSTEM account, is a security risk - see next section.

However, with proper quoting your original attempt could be made to work too - see the bottom section.


As for:

Also is there a way or switch that I can actually see the application launch?

It is psexec's -i option that makes the specified command run visibly, interactively on the remote target machine, in the session of whatever user happens to be logged on there interactively at the time.[2]

In terms of user identity, psexec generally allows you to launch commands as:

  • either: the SYSTEM account (NT AUTHORITY\SYSTEM) with option -s, which is not recommended with -i, however:

    • NT AUTHORITY\SYSTEM is a highly privileged account that represents the local computer, so its use in an application that the user can control interactively is a a security risk.
  • or: a given, fixed user, with option -u (and -p; see example below)

Note that what you cannot do with -i is to automatically run with the identity of whatever user happens to be logged on to the interactive session on the target computer, as that would be highly problematic from a security perspective. However, explicitly using -u with the same credentials as the currently interactive user (if known) works.

See the documentation for more information.

With -i, it's better to use an account that has only the minimum set of privileges required for the application to function; instead of -s, you would then use the -u (username) and -p (password) parameters; e.g.:

# From PowerShell
.\psexec \\SERVER -i -u user1 -p passwd1 mshta.exe "D:\path\toapp\app.hta" $arg1 $arg2

As for what you tried (in your question):

Your original command probably would have worked, if you had fixed your quoting problems:

.\psexec \\Srv2012 -s -i cmd /c start /i `"MSI-BUILDER`" "C:\Users\<user>\Desktop\MSI-BUILDER.hta" $arg1 $arg2

To avoid problems with nested quoting, the outer quoting around the command passed to cmd /c has been omitted.

Note how the " chars. that enclose MSI-BUILDER must - unfortunately - be escaped as `" so as to ensure that PowerShell passes them through to psexec and, ultimately, cmd /c:

  • PowerShell uses a double-quoting on demand policy behind the scenes, so that even arguments that you originally specified with quotes end up not double-quoted, if the argument value doesn't contain spaces[3] - therefore, your "MSI-BUILDER" argument ends up getting passed as just MSI-BUILDER - without double-quoting - to psexec.

  • Unfortunately, the cmd-internal start command has an awkward syntax: it always requires the argument that is the window title to be enclosed in "..." on the command line, even if the title is a single word (such as MSI-BUILDER in your case).

  • Therefore, in this particular edge case, the " around the window-title argument for start must be escaped as `" to ensure that they are retained.

As shown above, however, there's a simpler way to invoke your *.hta (not requiring an intermediate cmd.exe process).

As an aside: the start command is superfluous, and setting a title for the transient window that launches the *.hta GUI is pointless, as the window will just flash for an instant.


[1] Note that HTA applications don't support setting exit codes explicitly, unless you employ an elaborate workaround. Without it, the exit code would always be 0 - unless the mshta.exe process crashes.

[2] That psexec is capable of doing so may be surprising; it is made possibly by the following clever technique: psexec connects to the target machine's $ADMIN share (typically, C:\Windows), temporarily extracts a service executable in its own executable image (PSEXESVC.exe) to that location, and starts that executable as local service (PSEXESVC) on the target machine. It is this service that launches the command passed to psexec on invocation, optionally (-i) in the current user's session (visible window station). The calling psexec instance on the origin machine communicates with the service via a named pipe (psexecsvc), which is what enables the calling machine to receive the remote process' console output if -i was not specified; on completion of the command (or, with the -d (don't wait) option, on successfully launching the command), the service is stopped and the service executable is deleted - see this article for more information.

[3] There are other, complex criteria relating to " chars. embedded in the value.

mklement0
  • 382,024
  • 64
  • 607
  • 775
-2

Okay so finally after hours of trying different things this is the command that worked for me

Edited Command:

.\PSEXEC -accepteula -s -i 2 \\SERVER cmd /c "D:\path\toapp\app.hta" $arg1 $arg2 -u username -p password

demo7up
  • 530
  • 5
  • 27
  • 1
    I think you meant `-i` instead of `-h` (which is essentially implied by `-s`); please note the security implications of running an _interactive_ program with `-s`; by using `cmd /c`, a console window will briefly appear on the target machine first. – mklement0 Sep 17 '19 at 22:24
  • 1
    Thanks for updating. `-i` _without a session-ID argument_ is smart and automatically picks the session that is currently the interactive one on the target machine (the session named `console`). Using session _IDs_ such as `2` may work situationally, but the IDs change depending on who logged on first. – mklement0 Sep 19 '19 at 13:34
  • Also, using _both_ `-s` and `-u` + `-p` is contradictory; while `psexec` quietly accepts this combination, it is `-s` that takes effect, regardless of argument order. – mklement0 Sep 19 '19 at 13:45
  • @mklement0 Unfortunately that was my original direction using ```mshta.exe``` but the application would hang and never return a code from PowerShell which i kinda need to continue processing my functions with cmd I get a ```cmd exited on Srv2012 with error code 0.``` which I can return to my php script. Which is more than sufficient – demo7up Sep 19 '19 at 15:48
  • 1
    -d just launches the app and returns successfully regardless of the end result, as for cmd /c it launches a windows waits for the application to close then closes itself returning a true success response. – demo7up Sep 19 '19 at 17:34
  • So the HTA literally accepts two arguments and runs about 25-30 vbs scripts with those arguments once it iterates though the 25-30 scripts its set to ```window.close``` when that happens the cmd window closes because the process terminates. – demo7up Sep 19 '19 at 18:39
  • Thanks for clarifying. Note that HTA applications don't support setting exit codes explicitly, unless you employ [an elaborate workaround](https://stackoverflow.com/a/20823905/45375). Without it, the exit code would always be `0` - unless the `mshta.exe` process _crashes_. To summarize: we're back to square one: make `psexec` invoke `mshta.exe` _directly_ - without `-d` - and it will wait for the exit code, as desired - no need for `cmd /c`. I've updated my answer to contrast the two scenarios (synchronous vs. asynchronous). – mklement0 Sep 19 '19 at 19:10