0

I'm trying to import a certificate with certutil.exe with a script. This script is launched with a non-admin user, and when reaching Start-Process command, windows UAC asks for admin credentials.

$cmd="certutil.exe -addstore TrustedPeople C:\some_path\myCertif.cer"
$ret = Start-Process powershell -Verb RunAs -ArgumentList $cmd -Wait -PassThru

$ret.exitCode is equal to 1 when I run this code, and no certificate can be found in the store.

However if I run Start-Process without -ArgumentList (a powershell windows open then), and run the certutil.exe command from here , I got no error and the certificate is added to the store as expected.

Where is the difference ?

Telkarion
  • 431
  • 5
  • 12

1 Answers1

2

In the first case you make an error: you invoke a new instance of PowerShell and pass $cmd string which does not represent valid input for Powershell.

You invoke:

powershell.exe certutil.exe -addstore TrustedPeople C:\some_path\myCertif.cer

while PowerShell expects input like this:

powershell.exe .\somescript.ps1

powershell.exe -Command 'some_command'

powershell.exe -EncodedCommand 'base64_command'

... and so on ...

In the second case you just start a new elevated instance of PowerShell, which then lets you successfully import a certificate.

To import a certificate from PowerShell, I'd suggest three solutions based on your example:

1) use certutil.exe directly:

$arguments = '-addstore TrustedPeople C:\some_path\myCertif.cer'
$ret = Start-Process 'certutil.exe' -Verb RunAs -ArgumentList $arguments -Wait -PassThru

2) if for some reason (just in case) you want to invoke another PowerShell instance, you can use Command option like this:

$cmd = 'certutil.exe -addstore TrustedPeople C:\some_path\myCertif.cer'
$ret = Start-Process 'powershell.exe' -Verb RunAs -ArgumentList "-Command $cmd" -Wait -PassThru

3) and finally for the sake of completeness, you can use EncodedCommand:

$cmd = 'certutil.exe -addstore TrustedPeople C:\some_path\myCertif.cer'
$bytes = [Text.Encoding]::Unicode.GetBytes($cmd)
$encodedCommand = [Convert]::ToBase64String($bytes)
$ret = Start-Process 'powershell.exe' -Verb RunAs -ArgumentList "-EncodedCommand $encodedCommand" -Wait -PassThru

Also in my testing environment (Windows 7 Enterprise x64 with PowerShell v.2) I failed to actually get certutil.exe return code using Start-Process cmdlet when running my script with non-elevated privileges. I used .Net functions to solve this problem:

$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = 'certutil.exe'
$pinfo.UseShellExecute = $true
$pinfo.Verb = 'runas'
$pinfo.Arguments = '-addstore TrustedPeople C:\some_path\myCertif.cer'

$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$p.WaitForExit()
Write-Host $p.ExitCode

I hope that's what you need!

Dmitry VS
  • 605
  • 5
  • 14