在Python脚本中以不同的用户身份(非管理员)运行Windows程序

4

使用以下代码:

class ImpersonateWin32Sec(object):

    def __init__(self, domain, username, password):
        self.username = username
        self.password = password
        self.domain = domain
        self.handle = None

    def __enter__(self):
        self.handle = win32security.LogonUser(self.username, self.domain, self.password, win32con.LOGON32_LOGON_INTERACTIVE, win32con.LOGON32_PROVIDER_DEFAULT)
        win32security.ImpersonateLoggedOnUser(self.handle)

    def __exit__(self, *args):
        win32security.RevertToSelf()
        self.handle.Close()

with ImpersonateWin32Sec("domain", "altuser", "password"):
    prc = subprocess.Popen(cmdLine, cwd = "C:\\Temp", stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, env = env, shell = shell, creationflags = CREATE_NO_WINDOW)
    stdOut, stdErr = prc.communicate(timeout = 60)

我试图以不同的用户身份运行程序。但是它似乎只有部分地工作,因为某些命令会失败。以下3个命令显示某些环境仍然属于原始用户:

os.getlogin() # altuser
getpass.getuser() # origuser   --> WRONG!!!
win32api.GetUserName() # altuser

我的代码有什么问题/缺陷?我找到了一些信息,说ImpersonateLoggedOnUser为用户创建模拟标记,而不是主要标记?这可能是问题所在吗?如果是,如何获得主要标记?非常感谢提供代码示例。
敬礼,马丁

1
模拟设置当前线程中的模拟令牌。此令牌将被 GetUserName(由 os.getlogin 调用)用于从关联的登录会话中查询用户名。getpass.getuser 仅从进程环境中获取 USERNAME 环境变量。模拟对此没有影响。获取用户的环境变量需要 ExpandEnvironmentStringsForUser - Eryk Sun
1
创建进程时,系统始终使用进程的主要令牌,而不使用线程模拟令牌。以另一个用户身份创建进程需要使用CreateProcessAsUser。但是,使用CreateProcessWithLogonW会更容易,它通过将请求发送到Secondary Logon服务来实现。在这种情况下,CREATE_NO_WINDOW将被忽略,因为Secondary Logon服务使用标志CREATE_NEW_CONSOLE创建进程。 - Eryk Sun
1个回答

5

最终我找到了如何做到这一点。这是相当复杂的,我不得不从几个示例中合并代码(其中一些是用C写的)。下面的示例适用于以Network ServiceSystem用户身份执行时。无论是在用户会话还是会话0中执行都没有关系。

以下是代码:

import os
import msvcrt
import win32security
import win32con
import win32pipe
import win32process
import win32api
import win32net
import win32file
import win32event
import win32profile
import win32service


GENERIC_ACCESS = win32con.GENERIC_READ | win32con.GENERIC_WRITE | win32con.GENERIC_EXECUTE | win32con.GENERIC_ALL

WINSTA_ALL = (win32con.WINSTA_ACCESSCLIPBOARD  | win32con.WINSTA_ACCESSGLOBALATOMS | \
win32con.WINSTA_CREATEDESKTOP    | win32con.WINSTA_ENUMDESKTOPS      | \
win32con.WINSTA_ENUMERATE        | win32con.WINSTA_EXITWINDOWS       | \
win32con.WINSTA_READATTRIBUTES   | win32con.WINSTA_READSCREEN        | \
win32con.WINSTA_WRITEATTRIBUTES  | win32con.DELETE                   | \
win32con.READ_CONTROL            | win32con.WRITE_DAC                | \
win32con.WRITE_OWNER)

DESKTOP_ALL = (win32con.DESKTOP_CREATEMENU      | win32con.DESKTOP_CREATEWINDOW  | \
win32con.DESKTOP_ENUMERATE       | win32con.DESKTOP_HOOKCONTROL   | \
win32con.DESKTOP_JOURNALPLAYBACK | win32con.DESKTOP_JOURNALRECORD | \
win32con.DESKTOP_READOBJECTS     | win32con.DESKTOP_SWITCHDESKTOP | \
win32con.DESKTOP_WRITEOBJECTS    | win32con.DELETE                | \
win32con.READ_CONTROL            | win32con.WRITE_DAC             | \
win32con.WRITE_OWNER)


def runAsDomainUser(domainName, userName, password, cmdLine, maxWait):
    # maxWait = Maximum execution time in ms
    userGroupSid = win32security.LookupAccountName(domainName, userName)[0]
    # Login as domain user and create new session
    userToken = win32security.LogonUser(userName, domainName, password,
                                        win32con.LOGON32_LOGON_INTERACTIVE,
                                        win32con.LOGON32_PROVIDER_DEFAULT)
    rc = win32api.GetLastError()
    if userToken is None or (rc != 0):
        return -1, "", "LogonUser failed with RC=%d!" % rc
    profileDir = win32profile.GetUserProfileDirectory(userToken)
    tokenUser = win32security.GetTokenInformation(userToken, win32security.TokenUser)

    # Set access rights to window station
    hWinSta = win32service.OpenWindowStation("winsta0", False, win32con.READ_CONTROL | win32con.WRITE_DAC )
    # Get security descriptor by winsta0-handle
    secDescWinSta = win32security.GetUserObjectSecurity(hWinSta, win32security.OWNER_SECURITY_INFORMATION
                                                                 | win32security.DACL_SECURITY_INFORMATION
                                                                 | win32con.GROUP_SECURITY_INFORMATION)
    # Get DACL from security descriptor
    daclWinSta = secDescWinSta.GetSecurityDescriptorDacl()
    if daclWinSta is None:
        # Create DACL if not exisiting
        daclWinSta = win32security.ACL()
    # Add ACEs to DACL for specific user group
    daclWinSta.AddAccessAllowedAce(win32security.ACL_REVISION_DS, GENERIC_ACCESS, userGroupSid)
    daclWinSta.AddAccessAllowedAce(win32security.ACL_REVISION_DS, WINSTA_ALL, userGroupSid)
    # Set modified DACL for winsta0
    win32security.SetSecurityInfo(hWinSta, win32security.SE_WINDOW_OBJECT, win32security.DACL_SECURITY_INFORMATION,
                                  None, None, daclWinSta, None)

    # Set access rights to desktop
    hDesktop = win32service.OpenDesktop("default", 0, False, win32con.READ_CONTROL
                                                             | win32con.WRITE_DAC
                                                             | win32con.DESKTOP_WRITEOBJECTS
                                                             | win32con.DESKTOP_READOBJECTS)
    # Get security descriptor by desktop-handle
    secDescDesktop = win32security.GetUserObjectSecurity(hDesktop, win32security.OWNER_SECURITY_INFORMATION
                                                                   | win32security.DACL_SECURITY_INFORMATION
                                                                   | win32con.GROUP_SECURITY_INFORMATION )
    # Get DACL from security descriptor
    daclDesktop = secDescDesktop.GetSecurityDescriptorDacl()
    if daclDesktop is None:
        #create DACL if not exisiting
        daclDesktop = win32security.ACL()
    # Add ACEs to DACL for specific user group
    daclDesktop.AddAccessAllowedAce(win32security.ACL_REVISION_DS, GENERIC_ACCESS, userGroupSid)
    daclDesktop.AddAccessAllowedAce(win32security.ACL_REVISION_DS, DESKTOP_ALL, userGroupSid)
    # Set modified DACL for desktop
    win32security.SetSecurityInfo(hDesktop, win32security.SE_WINDOW_OBJECT, win32security.DACL_SECURITY_INFORMATION,
                                  None, None, daclDesktop, None)

    # Setup stdin, stdOut and stderr
    secAttrs = win32security.SECURITY_ATTRIBUTES()
    secAttrs.bInheritHandle = 1
    stdOutRd, stdOutWr = win32pipe.CreatePipe(secAttrs, 0)
    stdErrRd, stdErrWr = win32pipe.CreatePipe(secAttrs, 0)

    ppid = win32api.GetCurrentProcess()
    tmp = win32api.DuplicateHandle(ppid, stdOutRd, ppid, 0, 0, win32con.DUPLICATE_SAME_ACCESS)
    win32file.CloseHandle(stdOutRd)
    stdOutRd = tmp

    environment = win32profile.CreateEnvironmentBlock(userToken, False)

    startupInfo = win32process.STARTUPINFO()
    startupInfo.dwFlags = win32con.STARTF_USESTDHANDLES
    startupInfo.hStdOutput = stdOutWr
    startupInfo.hStdError = stdErrWr

    hPrc = win32process.CreateProcessAsUser(
                            userToken,
                            None,               # appName
                            cmdLine,            # commandLine
                            None,               # processAttributes
                            None,               # threadAttributes
                            1,                  # bInheritHandles
                            win32process.CREATE_NEW_CONSOLE, # dwCreationFlags
                            environment,        # newEnvironment
                            profileDir,         # currentDirectory
                            startupInfo)[0]

    win32file.CloseHandle(stdErrWr)
    win32file.CloseHandle(stdOutWr)
    win32security.RevertToSelf()

    # Wait for process to complete
    stdOutBuf = os.fdopen(msvcrt.open_osfhandle(stdOutRd, 0), "rb")
    stdErrBuf = os.fdopen(msvcrt.open_osfhandle(stdErrRd, 0), "rb")
    win32event.WaitForSingleObject(hPrc, maxWait)
    stdOut = stdOutBuf.read()
    stdErr = stdErrBuf.read()
    rc = win32process.GetExitCodeProcess(hPrc)
    return rc, str(stdOut, "utf-8"), str(stdErr, "utf-8")


if __name__ == "__main__":
    cmdLine = "C:/Windows/System32/cmd.exe"
    domainName = input("Domain: ")
    userName = input("User: ")
    password = input("Password: ")
    print(runAsDomainUser(domainName, userName, password, cmdLine, 60000))

1
不错啊 :) 你是否找到了一种在不使用paexec,psexec的情况下以系统帐户运行的方法? - Orsiris de Jong

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接