8

Golang in Windows: Execute command as another user

 2 years ago
source link: https://blog.davidvassallo.me/2022/06/17/golang-in-windows-execute-command-as-another-user/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Golang in Windows: Execute command as another user

image-1.png?w=648&h=9999

In Windows, there are some (admittedly limited) use cases where you could be interested in executing a command as another user. I came across one such use case during the development of Tutela, but this could be extended to generic uses. Under Windows, most services would need to run using SYSTEM credentials. This is true for the Tutela agent was well, which has a service component that runs using SYSTEM privileges. However, some operations require being run within the context of the user that is currently logged into the computer. Anything to do with the windows manager (explorer.exe) falls under this category.

So how can a golang program, running under SYSTEM privileges, execute a command under USER privileges?

Tokens

Each process runs using a set of “tokens”. These tokens describe the security context of a running process. Searching across the golang windows codebase, there’s an interesting system call:

https://github.com/golang/sys/blob/4f61da869c0cac4042c389774ce3a1627c48b555/windows/security_windows.go#L628

//sys OpenProcessToken(process Handle, access uint32, token *Token) (err error) = advapi32.OpenProcessToken

The documentation for the OpenProcessToken says:

The OpenProcessToken function opens the access token associated with a process.

https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocesstoken

The high level idea

  1. Find a process which is running with user tokens
  2. Call OpenProcessToken to capture those tokens
  3. Somehow use those tokens to run a command

Step 1 is easy, as mentioned before you can typically find “explorer.exe” running with user tokens

Step 2 is easy too if you are familiar with syscalls:

  • Use “OpenProcess” to get a handle to the process ID
  • Use “OpenProcessToken” to get the token

For example:

func getToken(pid int) (syscall.Token, error) {
var err error
var token syscall.Token
handle, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(pid))
if err != nil {
Logger.Warnw("Token Process", "err", err)
}
defer syscall.CloseHandle(handle)
// Find process token via win32
err = syscall.OpenProcessToken(handle, syscall.TOKEN_ALL_ACCESS, &token)
if err != nil {
Log.Warn("Open Token Process", "err", err)
}
return token, err
}

The hidden gem

Now that we have the user tokens, we’re left trying to figure out how to use them in step 3. It turns out that Golang’s own exec package has a semi-hidden parameter:

image.png?w=602

https://pkg.go.dev/os/exec#:~:text=//%20SysProcAttr%20holds%20optional%2C%20operating%20system%2Dspecific%20attributes.%0A%09//%20Run%20passes%20it%20to%20os.StartProcess%20as%20the%20os.ProcAttr%27s%20Sys%20field.%0A%09SysProcAttr%20*syscall.SysProcAttr

Tapping SysProcAttr shows some interesting parameters, but not exactly what we are after. However, opening this in an IDE does show something interesting:

image-1.png?w=1024

The description says it all… it’s exactly what we’re after.

Bringing this together into code:

func runAsUser(cmdPath string) (string, string) {
token, err := GetUserToken() //function we defined above
if err != nil {
Log.Warn("Get Token", "err", err)
}
defer token.Close()
cmd := exec.Command(cmdPath)
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
// this is the important bit!
cmd.SysProcAttr = &syscall.SysProcAttr{
Token: token,
}
err = cmd.Run()
return stdout.String(), stderr.String()
}

TL;DR

The golang exec package allows you to pass through windows security tokens to a command struct via the SysProcAttr field, which in windows allows for storing tokens.

Loading...

Related


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK