Run commands on a remote Windows host using SMB/RPC
pypsexec is a Python library that enables running commands on a remote Windows host via SMB/RPC, similar to the popular PsExec tool. It achieves this by deploying and using a bundled PAExec executable on the remote target. The library is currently at version 0.3.0, with the last major release in October 2021, suggesting a maintenance release cadence.
Common errors
-
Access Denied when trying to run python.exe -c "command"
cause The Python executable might be from the Windows Store ('WindowsApps' directory) or another location with restricted permissions, or the user's PATH environment variable is not correctly configured for remote execution.fixEnsure `python.exe` is a standard installation and its path is accessible to the remote user. You might need to specify the full path to `python.exe` or adjust the remote system's PATH environment variable. -
Cannot get stdout/stderr for long-running commands, output is None.
cause You are likely running the command with `asynchronous=True` or `interactive=True`, which explicitly suppresses stdout/stderr capture in `pypsexec`.fixDo not set `asynchronous=True` or `interactive=True` if you require `stdout` and `stderr` output. For long-running tasks where output is needed, you might need a different approach (e.g., redirecting output to a file and fetching it via SMB). -
GUI applications (e.g., notepad.exe) do not launch or are not visible on the remote desktop.
cause By default, `pypsexec` runs processes in a non-interactive session (session 0). GUI applications typically require an interactive user session to display.fixTo launch GUI applications, you need to use `interactive=True` and potentially specify an `interactive_session` ID. This is complex as it requires an active user session and understanding of Windows session management. -
PAExec binary or service remains on the remote host after execution.
cause In cases of critical errors or if cleanup functions are not explicitly called, the temporary PAExec service and binary might not be removed.fixAlways wrap your `pypsexec` operations in a `try...finally` block to ensure `c.remove_service()` and `c.disconnect()` are called, even if an error occurs. -
Windows Defender or other AV flags PAExec as malicious.
cause While `PAExec` (the underlying executable) is generally considered legitimate, some antivirus solutions might flag it due to its nature of remote execution, especially if its hash is known or if used in suspicious contexts.fixThis is often a false positive. You may need to create an exclusion for the PAExec binary on the remote host or consider compiling your own PAExec binary from source if permitted.
Warnings
- breaking Starting from v0.2.0, processes are run as the native architecture's bitness (e.g., 64-bit on a 64-bit OS) by default. Previously, it always ran as a 32-bit process. Applications relying on 32-bit paths must set `wow64=True` in `run_executable()` to restore the old behavior.
- breaking Version 0.3.0 dropped support for Python 2.7 and 3.5. The new minimum Python version is 3.6. However, its core dependency, `smbprotocol`, now explicitly requires Python 3.9+ for full functionality.
- gotcha SMB encryption (`encrypt=True`) is only supported on Windows versions with SMB 3.x.x+ (e.g., Windows 8 / Server 2012 and newer). For older systems like Windows 7, Server 2008, or Server 2008 R2, `encrypt` must be set to `False` to avoid connection issues.
- gotcha When running commands asynchronously (`asynchronous=True`) or interactively (`interactive=True`), `pypsexec` does not return `stdout` or `stderr`. These will always be `None` in the returned tuple.
- gotcha The remote Windows host requires SMB to be running, the `ADMIN$` share to be enabled with read/write access, and the connecting user must be an administrator on the host. UAC filtering of logon tokens can prevent connections.
Install
-
pip install pypsexec
Imports
- Client
from pypsexec.client import Client
Quickstart
import os
from pypsexec.client import Client
hostname = os.environ.get('PSEXEC_HOST', 'your_windows_host')
username = os.environ.get('PSEXEC_USERNAME', 'your_username')
password = os.environ.get('PSEXEC_PASSWORD', 'your_password')
if not all([hostname, username, password]):
print("Please set PSEXEC_HOST, PSEXEC_USERNAME, PSEXEC_PASSWORD environment variables.")
exit(1)
c = Client(hostname, username=username, password=password, encrypt=True)
try:
c.connect()
c.create_service()
# Run 'whoami.exe /all' command
stdout, stderr, rc = c.run_executable('whoami.exe', arguments='/all')
print(f"STDOUT:\n{stdout.decode('utf-8') if stdout else ''}")
print(f"STDERR:\n{stderr.decode('utf-8') if stderr else ''}")
print(f"Return Code: {rc}")
finally:
c.remove_service()
c.disconnect()