Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Powershell Start-Process not work when called from go (golang)?

I wish to run a Windows batch script from go, under a different user to the user running the go program. The user running go has more privileges than the user that should run the batch script.

From go there are several options for executing a process under a different user on Windows, such as writing windows calls directly using the syscall package in go. I have not attempted this yet, but I have tried both using PsExec and also Powershell. Powershell is preferred since it comes installed as standard on Windows 2008 R2.

The following code demonstrates the problem I have. In the following demo, I run a batch script. This batch script calls a Powershell script directly, and then calls it from a go program. The results are different. The Powershell script outputs 3 files, yet when called from go, only outputs 2 files.

For the sake of completeness, I also show how the user was created.

C:\stackoverflow\demo.bat:

::::: create a new user for the demo :::::

:: first create a home directory
mkdir C:\Users\Tom
:: remove Users group
icacls C:\Users\Tom /remove:g Users
:: remove Everyone
icacls C:\Users\Tom /remove:g Everyone
:: create user Tom and set his home directory
net user Tom _Jerry123_ /add /expires:never /passwordchg:no /homedir:C:\Users\Tom /y
:: Give Tom access to his home directory
icacls C:\Users\Tom /grant:r Tom:(CI)F SYSTEM:(CI)F Administrators:(CI)F
:: give him access to Remote Desktop
net localgroup "Remote Desktop Users" /add Tom

::::: now call powershell directly :::::

powershell -command C:\stackoverflow\demo.ps1
:: show which files were created
dir C:\Users\Tom
:: cleanup
del /f /q C:\Users\Tom\*

::::: run the go version to do the same thing :::::

go run C:\stackoverflow\demo.go
:: compare results
dir C:\Users\Tom
:: cleanup
del /f /s /q C:\Users\Tom
rmdir /s /q C:\Users\Tom
:: delete user
net user Tom /delete

C:\stackoverflow\demo.ps1

write-output "test output" | out-file C:\Users\Tom\started.txt
$credentials = New-Object System.Management.Automation.PSCredential -ArgumentList @("Tom",(ConvertTo-SecureString -String "_Jerry123_" -AsPlainText -Force))
Start-Process C:\stackoverflow\whoami.bat -WorkingDirectory C:\stackoverflow -Credential ($credentials) -Wait
write-output "test output" | out-file C:\Users\Tom\finished.txt

C:\stackoverflow\demo.go

package main

import (
    "fmt"
    "os"
    "os/exec"
)

func main() {
    run(exec.Command("PowerShell", "-Command", "C:\\stackoverflow\\demo.ps1"))
}

func run(cmd *exec.Cmd) {
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.Stdin = os.Stdin
    err := cmd.Start()
    if err != nil {
        panic(err)
    }
    err = cmd.Wait()
    if err != nil {
        panic(err)
    }
    fmt.Println("Done")
}

C:\stackoverflow\whoami.bat:

whoami > C:\Users\Tom\whoami.txt

And now, the results - here you see when called from batch script, files started.txt, whoami.txt, finished.txt all get created. When called from go, only started.txt and finished.txt get created. Why is that?

Output:

C:\stackoverflow>demo.bat

C:\stackoverflow>mkdir C:\Users\Tom

C:\stackoverflow>icacls C:\Users\Tom /remove:g Users
processed file: C:\Users\Tom
Successfully processed 1 files; Failed processing 0 files

C:\stackoverflow>icacls C:\Users\Tom /remove:g Everyone
processed file: C:\Users\Tom
Successfully processed 1 files; Failed processing 0 files

C:\stackoverflow>net user Tom _Jerry123_ /add /expires:never /passwordchg:no /homedir:C:\Users\Tom /y
The command completed successfully.


C:\stackoverflow>icacls C:\Users\Tom /grant:r Tom:(CI)F SYSTEM:(CI)F Administrators:(CI)F
processed file: C:\Users\Tom
Successfully processed 1 files; Failed processing 0 files

C:\stackoverflow>net localgroup "Remote Desktop Users" /add Tom
The command completed successfully.


C:\stackoverflow>powershell -command C:\stackoverflow\demo.ps1

C:\stackoverflow>dir C:\Users\Tom
 Volume in drive C is OSDisk
 Volume Serial Number is CCD6-C9E7

 Directory of C:\Users\Tom

06/18/2015  06:36 AM    <DIR>          .
06/18/2015  06:36 AM    <DIR>          ..
06/18/2015  06:36 AM                28 finished.txt
06/18/2015  06:36 AM                28 started.txt
06/18/2015  06:36 AM                55 whoami.txt
               3 File(s)            111 bytes
               2 Dir(s)  16,489,889,792 bytes free

C:\stackoverflow>del /f /q C:\Users\Tom\*

C:\stackoverflow>go run C:\stackoverflow\demo.go
Done

C:\stackoverflow>dir C:\Users\Tom
 Volume in drive C is OSDisk
 Volume Serial Number is CCD6-C9E7

 Directory of C:\Users\Tom

06/18/2015  06:36 AM    <DIR>          .
06/18/2015  06:36 AM    <DIR>          ..
06/18/2015  06:36 AM                28 finished.txt
06/18/2015  06:36 AM                28 started.txt
               2 File(s)             56 bytes
               2 Dir(s)  16,489,889,792 bytes free

C:\stackoverflow>del /f /s /q C:\Users\Tom
Deleted file - C:\Users\Tom\finished.txt
Deleted file - C:\Users\Tom\started.txt

C:\stackoverflow>rmdir /s /q C:\Users\Tom

C:\stackoverflow>net user Tom /delete
The command completed successfully.


C:\stackoverflow>
like image 217
Peter Moore Avatar asked Jun 18 '15 13:06

Peter Moore


People also ask

How do I run a ps1 script as administrator?

If you need to run a PowerShell script as an administrator, you will need to open PowerShell in administrative mode. To do so, find PowerShell on the Start menu, right click on the PowerShell icon, and then select More | Run as Administrator from the shortcut menu.

How to execute a PowerShell command from cmd?

To start a Windows PowerShell session in a Command Prompt window, type PowerShell . A PS prefix is added to the command prompt to indicate that you are in a Windows PowerShell session. PowerShell.exe -help PowerShell.exe -? PowerShell.exe /?

How do I run as administrator in PowerShell?

Step 1: Open the Command Prompt, and type the PowerShell as a command, then press Enter key. Step 2: Now, the command prompt will turn to Windows PowerShell. Step 3: Type the command start-process PowerShell -verb runas and press "enter" key. Step 4: It will bring up an elevated Windows PowerShell as an administrator.

Is PowerShell a command-line?

Windows PowerShell is a command-line interface for Windows computers. A command-line interface (CLI) is a program for telling your computer to do tasks using typed commands, rather than by clicking pictures on the desktop as in a graphical user interface (GUI).


1 Answers

Fixed it.

  1. The user Tom cannot access the C:\Stackoverflow folder by default, in order for anything to run, I had to give Everyone access to Read/Execute items in that folder, else the start-process failed miserably

  2. Add profilepath to the net user command. This prevented the extra folders being created that I mentioned in a comment:

    net user Tom _Jerry123_ /add /expires:never /passwordchg:no /homedir:C:\Users\Tom /profilepath:C:\Users\Tom /y

  3. Replace the Start-Process line with:

    Start-Process C:\stackoverflow\whoami.bat -WorkingDirectory C:\Users\Tom -Credential ($credentials) -Wait

Why?

With the working directory set to C:\StackOverflow, the command interpreter that is executing the batch file finds the whoami.bat before the builtin, and all kinds of broken happens. Once I moved the working directory to Tom's folder, everything worked exactly as you wanted.

Alternative:

Don't name your batch files the same as builtin commands.

like image 67
Eris Avatar answered Sep 30 '22 15:09

Eris