我有一个 go 应用程序在 Windows Server 2022 上以管理员用户身份运行。我需要它来使用更有限的用户帐户启动另一个应用程序。我还需要将其进程分配给作业对象。
我找到了 2 种可能的解决方案,用于以 go 中的另一个用户身份启动应用程序。
其中一种方法是获取用户令牌并将其传递给
SysProcAttr.Token
。我尝试调用 LogonUser 并使用此令牌来启动应用程序,但收到错误Error running app: fork/exec ./main8.exe: A required privilege is not held by the client.
我尝试运行的代码是这样的:
package main
import (
"fmt"
"os/exec"
"syscall"
"unsafe"
"github.com/fourcorelabs/wintoken"
"golang.org/x/sys/windows"
)
var (
advapi32 = windows.NewLazyDLL("advapi32.dll")
logonUserW = advapi32.NewProc("LogonUserW")
)
const (
LOGON32_LOGON_INTERACTIVE = 2
LOGON32_LOGON_NETWORK = 3
LOGON32_LOGON_BATCH = 4
LOGON32_LOGON_SERVICE = 5
LOGON32_LOGON_UNLOCK = 7
LOGON32_LOGON_NETWORK_CLEARTEXT = 8
LOGON32_LOGON_NEW_CREDENTIALS = 9
LOGON32_PROVIDER_DEFAULT = 0
LOGON32_PROVIDER_WINNT35 = 1
LOGON32_PROVIDER_WINNT40 = 2
LOGON32_PROVIDER_WINNT50 = 3
)
// Main entry point
func main() {
mytoken, err := wintoken.OpenProcessToken(0, wintoken.TokenPrimary) //pass 0 for own process
if err != nil {
panic(err)
}
defer mytoken.Close()
fmt.Println("My Token:")
fmt.Println(mytoken.GetPrivileges())
fmt.Println(mytoken.GetIntegrityLevel())
fmt.Println(mytoken.UserDetails())
token, err := LogonUser("limiteduser", "abc")
if err != nil {
fmt.Printf("Error loging user: %s", err)
}
userToken := wintoken.NewToken(windows.Token(token), wintoken.TokenPrimary)
fmt.Println("User Token:")
fmt.Println(userToken.GetPrivileges())
fmt.Println(userToken.GetIntegrityLevel())
fmt.Println(userToken.UserDetails())
cmd := exec.Command("./main8.exe")
cmd.SysProcAttr = &syscall.SysProcAttr{
Token: token,
}
out, err := cmd.CombinedOutput()
if err != nil {
fmt.Printf("Error running app: %s", err)
}
fmt.Println(string(out))
var s string
fmt.Scanln(&s)
}
func LogonUser(username, password string) (syscall.Token, error) {
pUsername, _ := windows.UTF16PtrFromString(username)
pDomain, _ := windows.UTF16PtrFromString(".")
pPassword, _ := windows.UTF16PtrFromString(password)
// var handle windows.Token
handle := uintptr(0)
r, _, err := logonUserW.Call(
uintptr(unsafe.Pointer(pUsername)),
uintptr(unsafe.Pointer(pDomain)),
uintptr(unsafe.Pointer(pPassword)),
uintptr(LOGON32_LOGON_INTERACTIVE),
uintptr(LOGON32_PROVIDER_DEFAULT),
uintptr(unsafe.Pointer(&handle)),
)
if r == 0 {
fmt.Println("logonUserW failed: ", err)
return 0, err
}
return syscall.Token(handle), nil
}
输出为:
My Token:
[SeIncreaseQuotaPrivilege: Disabled SeSecurityPrivilege: Disabled SeTakeOwnershipPrivilege: Disabled SeLoadDriverPrivilege: Disabled SeSystemProfilePrivilege: Disabled SeSystemtimePrivilege: Disabled SeProfileSingleProcessPrivilege: Disabled SeIncreaseBasePriorityPrivilege: Disabled SeCreatePagefilePrivilege: Disabled SeBackupPrivilege: Disabled SeRestorePrivilege: Disabled SeShutdownPrivilege: Disabled SeDebugPrivilege: Disabled SeSystemEnvironmentPrivilege: Disabled SeChangeNotifyPrivilege: Enabled SeRemoteShutdownPrivilege: Disabled SeUndockPrivilege: Disabled SeManageVolumePrivilege: Disabled SeImpersonatePrivilege: Enabled SeCreateGlobalPrivilege: Enabled SeIncreaseWorkingSetPrivilege: Disabled SeTimeZonePrivilege: Disabled SeCreateSymbolicLinkPrivilege: Disabled SeDelegateSessionUserImpersonatePrivilege: Disabled] <nil>
High <nil>
Username: aminuser, Domain: MYDOMAIN, Account Type: 1 <nil>
User Token:
[SeChangeNotifyPrivilege: Enabled SeIncreaseWorkingSetPrivilege: Disabled] <nil>
Medium <nil>
Username: limiteduser, Domain: MYDOMAIN, Account Type: 1 <nil>
Error running app: fork/exec ./main8.exe: A required privilege is not held by the client.
我不知道我错过了什么。我从 https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasuserw 猜测我的应用程序缺少
SE_INCREASE_QUOTA_NAME
和 SE_ASSIGNPRIMARYTOKEN_NAME
权限。但是我如何将这些权限分配给我自己的令牌呢?或者是因为用户令牌缺少 TOKEN_QUERY
、TOKEN_DUPLICATE
和 TOKEN_ASSIGN_PRIMARY
访问权限?我尝试调用 DuplicateTokenEx
来设置这些访问权限,但这没有任何作用。我就是这么称呼的:
err = windows.DuplicateTokenEx(
windows.Token(handle),
// 0x2000000,
windows.STANDARD_RIGHTS_REQUIRED|windows.TOKEN_QUERY|windows.TOKEN_DUPLICATE|windows.TOKEN_IMPERSONATE|windows.TOKEN_ASSIGN_PRIMARY|windows.TOKEN_ADJUST_PRIVILEGES,
nil,
windows.SecurityDelegation,
windows.TokenPrimary,
&token)
另一个解决方案是调用
CreateProcessWithLogonW
,这是在https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasuserw中建议的。这适用于与另一个用户启动进程,但随后无法将进程分配给作业对象,并出现 Access is denied
错误。显然,对于CreateProcessWithLogonW
,该进程似乎是由辅助登录服务创建的,该服务将该进程分配给一个作业,但我无法将其分配给我的作业。
我缺少什么才能让它发挥作用?我还能尝试什么?
我不知道您是否仍然遇到这个问题,但我正在研究类似的事情,发现我需要以本地系统身份运行源进程以确保它具有所需的权限。
我的流程作为服务运行,因此这很容易做到。