使用不同的用户启动进程并将其附加到作业对象

问题描述 投票:0回答:1

我有一个 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
,该进程似乎是由辅助登录服务创建的,该服务将该进程分配给一个作业,但我无法将其分配给我的作业。

我缺少什么才能让它发挥作用?我还能尝试什么?

go winapi
1个回答
0
投票

我不知道您是否仍然遇到这个问题,但我正在研究类似的事情,发现我需要以本地系统身份运行源进程以确保它具有所需的权限。

我的流程作为服务运行,因此这很容易做到。

© www.soinside.com 2019 - 2024. All rights reserved.