2

These are the commands run in a powershell console (Windows 10):

$username = 'Username'
$password = 'Password'
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential $username, $securePassword
Start-Process powershell.exe -Credential $credential -WindowStyle Hidden -ArgumentList "Start-Process cmd.exe -Verb RunAs -ArgumentList 'value'"

These commands work fine except that once you open cmd.exe as administrator via another user, by running this command:

echo %1

It gives me back:

%1

Literally. Instead I expect:

value

What am I doing wrong?

Mario Palumbo
  • 693
  • 8
  • 32
  • 5
    Any reason why you're not doing this instead: `Start-Process cmd.exe -Credential $credential -ArgumentList "echo value" -Verb RunAs`? – Abraham Zinala Oct 26 '21 at 23:40
  • 1
    Because Credential and Runas cannot be in the same command. `Parameter set cannot be resolved using the specified named parameters.` I see upvotes for the comment, so it works for you? – Mario Palumbo Oct 27 '21 at 07:04
  • Can anyone reply to my comment? Does the command proposed to you work? – Mario Palumbo Oct 27 '21 at 14:08
  • 2
    No, they cannot be used together. Not sure why the upvotes on that comment. – codewario Oct 27 '21 at 18:51
  • 1
    @Abraham, while it is certainly tempting to make the simplification you're proposing, it cannot work even _syntactically_, as has been pointed out. It is so tempting that I too initially up-voted your comment (which I cannot undo anymore), along with 4 others. Can I suggest removing the comment, so as to avoid confusion? – mklement0 May 09 '23 at 15:59
  • @Mario, while the absence of `/c` or `/k` preceding a command passed to `cmd.exe` explains why your code doesn't work as intended, I'd expect a different _symptom_, namely the command getting _ignored_ altogether and simply an _interactive session_ being entered. – mklement0 May 09 '23 at 16:01

2 Answers2

4

I just answered a question where the solution can be found using the script I provided there, with a few modifications, and invoking the chain of commands in a particular way:

RunAsProxy.ps1

# First arg should be the script path
$script = $args[0]

# Rest of args should be any script parameters
$scriptArgs = $args[1..$args.Count] -join ' '

$startProcessArgs = @{
  Wait = $true
  Verb = 'RunAs'
  FilePath = 'cmd.exe'
  ArgumentList = "/c ""$script"" $scriptArgs"
}
Start-Process @startProcessArgs

exit $LASTEXITCODE

Then call RunAsProxy.ps1 as follows as the user you want to run as, then elevate:

$command = "Command_you_want_to_run_from_cmd.exe"
$arguments = "any arguments to the program"
Start-Process -Cred $credential powershell.exe "-File ./RunAsProxy.ps1 $command $arguments"

The way this works is pretty much what you attempted, but using a pre-defined script to handle the elevation. But as you discovered you cannot call -Credential and -Verb in the same invocation on Start-Process. So this works more easily than defining the code in-line:

  1. Run RunAsProxy.ps1 as the target user
  2. RunAsProxy.ps1 will run cmd.exe with the provided arguments and elevate the process.

Note that if RunAsProxy.ps1 is not in the current directory you would need to provide the relative or full path to it.

codewario
  • 19,553
  • 20
  • 90
  • 159
0

The only problem with your approach was that you forgot to use /c or /k with cmd.exe in order to make it execute a command on startup:

  • /c: cmd /c $someCommandLine executes the command(s) represented by $someCommandLine and then exits.

    • Note that this means that the console window automatically closes on exit, if the cmd.exe process owns the window (which is the case here);

    • Appending & pause to the command string would keep the window open until the user presses a key, given them a chance to see the output.

  • /k: cmd /k $someCommandLine executes the command(s) and then keeps the session open, i.e. enters an interactive session.

  • Note: If you precede your command(s) with neither /c nor /k, cmd.exe ignores them and simply enters an interactive session (as it does if you execute cmd.exe without arguments altogether).

The assumption is that 'value' in your code is a placeholder for command(s) you want cmd.exe to execute in the elevated session.

Here's a simple example (which uses a here-string to obviate the need to escape quotes embedded in the string) that uses cmd /c to execute a sample command followed by pause to keep the window open until the user presses a key (using /k would be more problematic from a security perspective, as you'd enter an interactive session with admin privileges).

# Note: Prompts for the admin credentials first, then requires confirmation
#       of the UAC prompt when the elevated process is launched by the 
#       nested Start-Process call.
Start-Process -Credential (Get-Credential $username) powershell.exe `
  -WindowStyle Hidden `
  -ArgumentList @'
   Start-Process -Verb RunAs cmd.exe -ArgumentList '/c echo I am %USERNAME% & pause'
'@

Since it isn't readily obvious to others why your code involves two - nested - Start-Process calls, let me provide context:

  • Your code runs an elevated (run-as-admin) command with a different user identity, presumably because the current user cannot request elevation as him/herself, due to not being an administrator.

    • Just to provide the obligatory security warning (I hope your code is just sample code): storing plain-text passwords in scripts is not safe, let alone an administrator's password.
  • Doing so requires two Start-Process calls - one nested inside the other, because the -Credential parameter (requesting a specific user identity) can syntactically not be combined with -Verb RunAs (requesting elevation):

    • The outer one, with -Credential, starts an (invariably) non-elevated process as the target user. This process is a transient, auxiliary process (which is why you're running it hidden with -WindowStyle Hidden).
    • The inner one, from the auxiliary process, with -Verb RunAs, then starts an elevated process (visibly) as that user.

mklement0
  • 382,024
  • 64
  • 607
  • 775