跳转到主要内容

使用SMB/RPC在远程Windows主机上运行命令

项目描述

Python PsExec库

Test workflow codecov PyPI version License

这个库可以通过Python在远程Windows主机上运行命令。这意味着它可以在任何有Python的主机上运行,不需要任何二进制文件或特定的操作系统。它使用SMB/RPC以类似于流行PsExec工具的方式执行命令。更多关于这个工具的信息可以在 这篇博客文章 中找到。

发送到服务器的可执行包装器基于 PAExec 库。PAExec是一个免费、可重新分配的开源库,与微软的 PsExec 应用程序等价。这个程序以二进制形式存储在这个包中,用于运行远程服务并启动进程执行。

我要感谢Power Admin的开发者创建这个库,因为它使得这个库比原本要简单得多。

功能

使用pypsexec,您可以像使用PsExec一样运行远程Windows主机的命令。目前,您可以使用pypsexec执行以下操作;

  • 以特定本地或域用户或当前用户运行
  • 以本地SYSTEM账户运行
  • 以交互式进程运行
  • 指定交互式进程应运行的会话
  • 指定用户令牌的运行级别,最高限制
  • 设置进程的优先级
  • 为远程进程设置超时
  • 通过stdin管道向运行中的进程发送输入
  • 设置进程可以运行的处理器

更多信息

虽然这些信息对您使用此库不是必需的,但它可以帮助人们了解底层发生了什么。当运行命令时,此库会执行以下步骤;

  • 与主机建立SMB连接
  • 将PAExec二进制文件复制到远程主机的ADMIN$共享
  • 使用RPC将Windows服务管理器绑定到打开的IPC$
  • SYSTEM账户创建并启动一个Windows服务来运行复制的二进制文件
  • 连接到服务创建的PAExec命名管道
  • 通过管道将进程详细信息发送到PAExec服务
  • 向PAExec服务发送请求,根据发送的设置启动进程
  • 连接到新创建进程的stdout、stderr、stdin管道(如果不是交互式或异步)
  • 读取stdout/stderr管道,直到进程完成
  • 获取新进程的返回码
  • 停止并删除PAExec服务
  • ADMIN$共享中删除PAExec二进制文件
  • 断开SMB连接

如果进程失败,PAExec服务和二进制文件可能不会从主机中删除,可能需要手动操作。这只有在发生严重错误或清理函数没有被调用的情况下才会发生。

默认情况下,发送到和从服务器发送的数据被加密,以阻止人们在网络中窃听您的数据。不幸的是,这使用了SMB加密,该加密是在SMB 3.x方言中添加的,因此运行Windows 7、Server 2008或Server 2008 R2的主机将无法使用加密。

这意味着在这些较老版本的Windows上通过线路发送的所有数据都可供读取这些数据包的人查看。进程的任何输入或输出都通过这些数据包进行,因此通过网络发送的秘密信息将不会被加密。PAExec通过在run_executable中设置的设置执行简单的XOR混淆来尝试降低这种风险,使其不是明文,但可以由了解协议的人解码。

要求

要安装pypsexec,只需运行

pip install pypsexec

这将下载所需的软件包,并准备您的Python环境以进行操作。

默认情况下,pypsexec支持使用NTLM身份验证对Windows主机进行身份验证,但域环境中的用户可以利用Kerberos身份验证来增加安全性。Kerberos库是可选安装,可以与以下方式一起安装;

# for Debian/Ubuntu/etc:
sudo apt-get install gcc python-dev libkrb5-dev
pip install smbprotocol[kerberos]

# for RHEL/CentOS/etc:
sudo yum install gcc python-devel krb5-devel krb5-workstation python-devel
pip install smbprotocol[kerberos]

远程主机要求

此包的目标是能够在尽可能少的设置下在纯远程Windows主机上运行可执行文件。不幸的是,根据使用的操作系统版本和类型,仍然需要一些设置才能正常运行。pypsexec在主机上需要以下内容;

  • SMB必须在Windows端口上运行并可以从Python主机读取
  • 必须启用ADMIN$共享,并允许配置的用户进行读写访问
  • 上述通常意味着配置的用户是Windows主机的管理员
  • 主机上至少需要SMB 2(Server 2008和更高版本)
  • 连接用户具有完整的登录令牌,不会被UAC过滤
  • 如果连接到localhost并且已安装pywin32,则必须以具有管理员权限的用户运行脚本

防火墙设置

默认情况下,Windows会阻止SMB端口445,在pypsexec连接到主机之前需要将其打开。为此,请运行以下命令之一:

# PowerShell (Windows 8 and Server 2012 or Newer)
Set-NetFirewallRule -Name FPS-SMB-In-TCP -Enabled True

# CMD (All OS's)
netsh advfirewall firewall set rule name="File and Printer Sharing (SMB-In)" dir=in new enable=Yes

这将打开用于SMB的445端口的入站流量。

用户账户控制

在某些情况下,用户账户控制会过滤任何远程登录令牌并限制其可用的权限。这会导致pypsexec出现问题,并在尝试与远程SCMR API交互时出现ACCESS_IS_DENIED错误消息。这种限制在多种不同场景下得到实施,要使pypsexec正常工作,可以执行以下操作之一:

  • 在域环境中,使用属于本地Administrators组的任何域账户
  • 如果LocalAccountTokenFilterPolicy设置为1,则可以使用属于本地Administrators组的任何本地账户
    • 这意味着任何远程登录令牌都不会被过滤,并将拥有该用户的全部权限
    • 默认情况下,此设置未定义,需要创建
    • 这仅影响远程令牌,任何本地令牌/进程仍将按常规限制
  • 使用在Windows安装时创建的内置本地管理员账户(SID S-1-5-21-*-500
    • 英文安装的内置管理员账户通常称为Administrator,但它可以被重命名
    • 此账户在Windows桌面的变体中通常默认禁用,例如Windows 7、8.1、10
    • AdminApprovalMode设置为Enabled时,这将不会起作用。AdminApprovalMode默认不启用
  • 如果EnableLUA设置为Disabled,则可以使用属于本地Administrators组的任何本地账户
    • LocalAccountTokenFilterPolicy选项不同,这会影响本地令牌和本地生成的进程
    • 这实际上禁用了任何管理员账户的UAC,应避免使用

要将LocalAccountTokenFilterPolicy设置为允许远程登录上的完整令牌,请运行以下PowerShell命令;

$reg_path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
$reg_prop_name = "LocalAccountTokenFilterPolicy"

$reg_key = Get-Item -Path $reg_path
$reg_prop = $reg_key.GetValue($reg_prop_name)
if ($null -ne $reg_prop) {
    Remove-ItemProperty -Path $reg_path -Name $reg_prop_name
}

New-ItemProperty -Path $reg_path -Name $reg_prop_name -Value 1 -PropertyType DWord

要获取内置管理员(SID S-1-5-21-*-500)的名称,可以运行以下PowerShell命令;

Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$principal_context = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext([System.DirectoryServices.AccountManagement.ContextType]::Machine)
$user_principal = New-Object -TypeName System.DirectoryServices.AccountManagement.UserPrincipal($principal_context)
$searcher = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalSearcher($user_principal)
$users = $searcher.FindAll() | Where-Object { $_.Sid -like "*-500" }
$users[0].Name

最后的手段是禁用任何本地管理员账户的UAC。同样,应避免这样做,因为还有其他选项可用,并且这将降低Windows主机的安全性,但要执行此操作,可以运行以下PowerShell命令;

$reg_path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
$reg_prop_name = "EnableLUA"

$reg_key = Get-Item -Path $reg_path
$reg_prop = $reg_key.GetValue($reg_prop_name)
if ($null -ne $reg_prop) {
    Remove-ItemProperty -Path $reg_path -Name $reg_prop_name
}

New-ItemProperty -Path $reg_path -Name $reg_prop_name -Value 0 -PropertyType DWord

更改EnableLUA设置后,Windows主机需要重新启动才能生效策略。

示例

以下是如何使用此库运行命令的示例

from pypsexec.client import Client

# creates an encrypted connection to the host with the username and password
c = Client("hostname", username="username", password="password")

# set encrypt=False for Windows 7, Server 2008
c = Client("hostname", username="username", password="password", encrypt=False)

# if Kerberos is available, this will use the default credentials in the
# credential cache
c = Client("hostname")

# you can also tell it to use a specific Kerberos principal in the cache
# without a password
c = Client("hostname", username="username@DOMAIN.LOCAL")

c.connect()
try:
    c.create_service()

    # After creating the service, you can run multiple exe's without
    # reconnecting

    # run a simple cmd.exe program with arguments
    stdout, stderr, rc = c.run_executable("cmd.exe",
                                          arguments="/c echo Hello World")

    # run whoami.exe as the SYSTEM account
    stdout, stderr, rc = c.run_executable("whoami.exe", use_system_account=True)

    # run command asynchronously (in background), the rc is the PID of the spawned service
    stdout, stderr, rc = c.run_executable("longrunning.exe",
                                          arguments="/s other args",
                                          asynchronous=True)

    # run whoami.exe as a specific user
    stdout, stderr, rc = c.run_executable("whoami",
                                          arguments="/all",
                                          username="local-user",
                                          password="password",
                                          run_elevated=True)
finally:
    c.remove_service()
    c.disconnect()

在发生致命失败的情况下,此项目可能在C:\Windows留下一些PAExec有效载荷,或者服务仍然安装。由于这些名称是唯一的,它们可能会随着时间的推移而积累。可以手动删除,也可以使用pypsexec一次性清理所有内容。为此,请运行

from pypsexec.client import Client

c = Client("server", username="username", password="password")
c.connect()
c.cleanup()  # this is where the magic happens
c.disconnect()

脚本将删除任何匹配C:\Windows\PAExec-*的文件以及任何匹配PAExec-*的服务。对于单独的运行,应继续使用remove_service()函数。

客户端选项

在创建主要的pypsexec Client对象时,可以设置一些配置选项来控制进程。这些参数是;

  • server:需要设置,是连接到的服务器或IP地址
  • username:连接时使用的用户名。如果已安装python-gssapi并且已在本地凭据缓存中授予票据,则可以是None
  • password:用户名username的密码。如果已安装python-gssapi并且为指定的用户授予了票据,则可以是None
  • 端口号:在连接到服务器时覆盖默认的端口号445
  • 加密:是否加密消息,默认为True。服务器2008、2008 R2和Windows 7宿主机不支持SMB加密,需要将其设置为False

运行可执行文件选项

调用run_executable时,有多个kwargs可以定义远程进程的工作方式。这些参数包括:

  • executable:(字符串) 要运行的程序路径
  • arguments:(字符串) 程序参数
  • processors:(列表) 进程可以运行的处理器编号列表
  • asynchronous:(布尔值) 不等待进程完成就返回。函数返回的rc是异步进程的PID,默认为False
  • load_profile:(布尔值) 加载用户配置文件,默认为True
  • interactive_session:(整数) 当interactive=True时,显示交互式进程的会话ID,默认为0
  • interactive:(布尔值) 以交互式进程运行进程。当True时,stdout和stderr缓冲区将为None,默认为False
  • run_elevated:(布尔值) 当定义了username时,将使用提升权限,默认False
  • run_limited:(布尔值) 当定义了username时,将在有限权限下运行进程,默认False
  • username:(字符串) 用于在不同于认证SMB会话的用户下运行进程
  • password:(字符串) username的密码
  • use_system_account:(布尔值) 以NT AUTHORITY\SYSTEM的身份运行进程
  • working_dir:(字符串) 进程的工作目录,默认为C:\Windows\System32
  • show_ui_on_win_logon:(布尔值) 当use_system_account=True时,在Winlogon安全桌面显示UI,默认False
  • priority:(pypsexec.ProcessPriority) 进程的优先级,默认NORMAL_PRIORITY_CLASS
  • remote_log_path:(字符串) 在远程主机上的路径,用于记录PAExec服务的详细信息
  • timeout_seconds:(整数) 进程可以运行的最大时间,默认为0(无超时)
  • stdout:(pipe.OutputPipe) 一个实现pipe.OutputPipe的类,它控制如何处理和返回stdout输出,默认返回stdout的字节字符串。当interactive=Trueasynchronous=True时将被忽略
  • stderr:(pipe.OutputPipe) 一个实现pipe.OutputPipe的类,它控制如何处理和返回stderr输出,默认返回stderr的字节字符串。当interactive=Trueasynchronous=True时将被忽略
  • stdin:(字节/生成器) 一个字节字符串或生成器,产生要发送到stdin管道的字节字符串,不与interactive=Trueasynchronous=True一起使用
  • wow64:(布尔值) 设置为True,在64位系统上以32位模式运行可执行文件。在32位系统上不执行任何操作,默认False

日志记录

此库使用内置的Python日志库,可用于查找pypsexec进程中的情况。日志消息记录到pypsexec命名的记录器以及pypsexec.*,其中*pypsexec目录中的每个Python脚本。

通过代码启用脚本中的日志记录的一种方法是,在要使用的脚本顶部添加以下内容;

import logging

logger = logging.getLogger("pypsexec")
logger.setLevel(logging.DEBUG)  # set to logging.INFO if you don't want DEBUG logs
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - '
                              '%(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)

这些日志通常在调试问题时非常有用,因为它们提供了对正在进行的操作和可能出错的地方的更详细的步骤快照。调试级别还会打印出客户端发送的每个SMB数据包的易于阅读的字符串,但此级别可能非常冗长。

测试

在此模块中,您需要先安装一些预置要求。这可以通过运行以下命令来完成;

pip install -r requirements-test.txt

# you can also run tox by installing tox
pip install tox

从那里开始运行基本测试;

py.test -v --cov pypsexec --cov-report term-missing

# or with tox
tox

有一些额外的测试,只有在设置了某些环境变量时才会运行。要运行这些测试,请设置以下变量;

  • PYPSEXEC_SERVER:Windows主机的主机名或IP地址
  • PYPSEXEC_USERNAME:用于认证的用户名
  • PYPSEXEC_PASSWORDPYPSEXEC_USERNAME的密码

然后,您可以使用这些环境变量直接运行 toxpy.test 来运行集成测试。

未来

我未来有兴趣添加的一些功能包括

  • 添加一个Python脚本,可以调用它来运行像 PsExec.exe 这样的即兴命令

项目详情


下载文件

下载您平台的文件。如果您不确定选择哪个,请了解有关 安装包 的更多信息。

源代码分发

pypsexec-0.3.0.tar.gz (157.1 kB 查看哈希值)

上传时间 源代码

构建分发

pypsexec-0.3.0-py2.py3-none-any.whl (151.7 kB 查看哈希值)

上传时间 Python 2 Python 3

由以下支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面