使用Golang通过子进程模拟Windows服务-访问被拒绝

问题描述

我正在尝试作为服务运行时获取登录用户的UPN。我能够以Username格式获得DOMAIN\username个已登录用户的信息,以及该用户SessionID和许多其他信息。

获取UPN,我尝试通过获取访问令牌并使用此目标令牌运行whoami /upn命令来模拟用户。当该服务直接执行此操作时效果很好,但是当该服务的子进程执行此操作时,它不起作用。

这是获取登录用户的UPN的示例。实际上,我不能简单地获取当前用户的UPN,因为该应用程序正在作为服务运行

import (
    "errors"
    "fmt"
    "os"
    "os/exec"
    "strings"
    "syscall"
    "unsafe"

    winapi "github.com/kumako/go-win64api"
    "golang.org/x/sys/windows"
    "qohash.com/qostodian-agent/internal/logging"
)

var (
    modwtsapi32 *windows.LazyDLL = windows.NewLazySystemDLL("wtsapi32.dll")
    modadvapi32 *windows.LazyDLL = windows.NewLazySystemDLL("advapi32.dll")

    procWTSQueryUserToken *windows.LazyProc = modwtsapi32.NewProc("WTSQueryUserToken")
    procDuplicatetokenEx  *windows.LazyProc = modadvapi32.NewProc("DuplicatetokenEx")
)

func GetUserSessions() ([]User,error) {
    var u []User
    users,err := winapi.ListLoggedInUsers()
    if err != nil {
        return u,err
    }

    for _,user := range users {
        var upn string
        var err error

            // UPN Translation doesn't work -> try to impersonate the session and run whoami command
            upn,err= getUpnForLoggedUser(user.SessionID)
            if err!= nil {
                logging.Debugf("Could not get UPN for user %v: \n\t - First attempt: %v \n\t ",user.FullUser(),err)
            } else {
                logging.Debugf("User UPN: %v",upn)
            }
    

        u = append(u,User{
            Username: user.Username,Domain:   user.Domain,Upn:      upn,})
    }

    return u,nil
}

func getUpnForLoggedUser(sessionID uint32) (string,error) {

    userToken,err := duplicateUserTokenFromSessionID(windows.Handle(sessionID))
    if err != nil {
        return "",fmt.Errorf("get duplicate user token for current user session: %s",err)
    }

    winPath,ok := os.LookupEnv("SystemRoot")
    if !ok {
        return "",errors.New("Cannot obtain SystemRoot environment variable")
    }
    cmd := exec.Command(fmt.Sprintf("%s\\system32\\whoami.exe",winPath),"/upn")
    cmd.SysProcAttr = &syscall.SysProcAttr{
        HideWindow: true,Token:      userToken,}
    out,err := cmd.CombinedOutput()
    if err != nil {
        return "",err
    }

    userToken.Close()
    return strings.Trimspace(string(out)),nil
}

func duplicateUserTokenFromSessionID(sessionId windows.Handle) (syscall.Token,error) {
    var (
        impersonationToken windows.Handle = 0
        userToken          syscall.Token  = 0
    )

    if returnCode,_,err := procWTSQueryUserToken.Call(uintptr(sessionId),uintptr(unsafe.Pointer(&impersonationToken))); returnCode == 0 {
        return 0xFFFFFFFF,fmt.Errorf("call native WTSQueryUserToken: %s",err)
    }

    if returnCode,err := procDuplicatetokenEx.Call(uintptr(impersonationToken),uintptr(windows.SecurityImpersonation),uintptr(windows.TokenPrimary),uintptr(unsafe.Pointer(&userToken))); returnCode == 0 {
        return 0xFFFFFFFF,fmt.Errorf("call native DuplicatetokenEx: %s",err)
    }

    if err := windows.CloseHandle(impersonationToken); err != nil {
        return 0xFFFFFFFF,fmt.Errorf("close windows handle used for token duplication: %s",err)
    }

    return userToken,nil
}

当我的主要服务呼叫GetUserSessions时,它可以工作并返回具有已登录UPN的用户

如果此主服务产生一个子进程,则UPN始终为空。调用whoami时,应用程序将获得fork/exec C:\WINDOWS\system32\whoami.exe: Access is denied

我的问题是:

  1. 冒充用户获取用户UPN的好方法吗?经过许多不同的测试后,我发现没有其他方法可以做到这一点
  2. 如果没有其他方法,我该怎么做才能使子进程具有将whoami /upn命令作为另一项服务运行的权限?

谢谢!

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)