• Stars
    star
    327
  • Rank 128,686 (Top 3 %)
  • Language
    Python
  • License
    MIT License
  • Created over 6 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

PowerShell Remoting Protocol for Python

pypsrp - Python PowerShell Remoting Protocol Client library

Test workflow codecov PyPI version

pypsrp is a Python client for the PowerShell Remoting Protocol (PSRP) service. It allows you to execute PowerShell scripts inside the Python script with a target being remote or some other local process.

This library has a low level API designed to mirror the System.Management.Automation namespace. There are also some helper functions designed to make it easier to do one off scripts and copy/fetch files on the target PSSession.

The old pypsrp namespace only supported WSMan based transports but the psrp namespace supports the following:

  • WSMan - targets only remote Windows hosts
  • SSH - can target both remote Windows and non-Windows hosts
  • Process - can spawn a new local pwsh or powershell process and run code in there
  • Named Pipes - targets a named pipe, like the pwsh remoting pipe, and communicate over that

The WSMan connection supports the following authentication protocols out of the box:

  • Negotiate (Default)
  • Basic
  • Certificate
  • NTLM
  • CredSSP

To support Kerberos the kerberos extras package must be installed.

Requirements

See How to Install for more details

Optional Requirements

The following Python libraries can be installed to add extra features that do not come with the base package:

How to Install

To install pypsrp with all the basic features, run:

pip install pypsrp

Kerberos Authentication

While pypsrp supports Kerberos authentication, it isn't included by default for Linux hosts due to it's reliance on system packages to be present.

To install these packages, depending on your distribution, run one of the following script blocks.

For Debian/Ubuntu

# For Python 2
apt-get install gcc python-dev libkrb5-dev

# For Python 3
apt-get install gcc python3-dev libkrb5-dev

# To add NTLM to the GSSAPI SPNEGO auth run
apt-get install gss-ntlmssp

For RHEL/Centos

yum install gcc python-devel krb5-devel

# To add NTLM to the GSSAPI SPNEGO auth run
yum install gssntlmssp

For Fedora

dnf install gcc python-devel krb5-devel

# To add NTLM to the GSSAPI SPNEGO auth run
dnf install gssntlmssp

For Arch Linux

pacman -S gcc krb5

Once installed you can install the Python packages with

pip install pypsrp[kerberos]

Kerberos also needs to be configured to talk to the domain but that is outside the scope of this page.

SSH Connections

The SSH connection on psrp requires the asyncssh library to be installed.

pip install pypsrp[ssh]

Named Pipe Connections

The Named Pipe connection on psrp requires the psutil library to be installed.

pip install pypsrp[named_pipe]

How to Use

There are 3 main components that are in use within this library:

  • ConnectionInfo: Defines the connection type and connection specific variables
  • RunspacePool: The Runspace Pool contains a pool of pipelines that can be run on the remote target
  • Pipeline: The code to run inside the Runspace Pool

ConnectionInfo

These are the connection info types that are supported by pypsrp

Type Sync Asyncio Mandatory Requirements Optional Requirements
WSManInfo Y Y N/A pypsrp[kerberos] for Kerberos support
ProcessInfo Y Y N/A N/A
SSHInfo N Y pypsrp[ssh] N/A
NamedPipeInfo N Y pypsrp[named_pipe] N/A

The mandatory requirements are requirements that must be installed on top of what pypsrp requires. The optional requirements are requirements to utilise optional features that aren't available by default.

The connection info objects do not store the connections themselves, they just define how a Runspace Pool will connect to the target. This means they can be reused across multiple pools as needed.

The psrp.AsyncOutOfProcConnection and psrp.SyncOutOfProcConnection can also be used to define your own out of process connection type. This is fairly advanced work as it would require an implementation on both the client and server side.

RunspacePool

The Runspace Pool is used to create the connection to the remote target and can host multiple pipelines that run code. A Runspace Pool comes in 2 varieties:

  • psrp.SyncRunspacePool - uses a synchronous connection
  • psrp.AsyncRunspacePool - uses an asyncio based connection

Both of these types must be created with a ConnectionInfo that describes how to connect to the remote PowerShell instance. See the table in ConnectionInfo to see what connections are supported by a syncronous Runspace Pool and an asyncronous Runspace Pool.

import psrp

async def async_rp(conn: psrp.ConnectionInfo) -> None:
    async with psrp.AsyncRunspacePool(conn) as rp:
        ...


def sync_rp(conn: psrp.ConnectionInfo) -> None:
    with psrp.SyncRunspacePool(conn) as rp:
        ...

Both the sync and async Runspace Pool contain the same methods and functionality, the main difference is that most operations on the async pool are coroutines that need to be awaited.

Pipeline

A Pipeline is used to execute a command or script on the Runspace Pool it is associated with. There are 4 types of pipelines that can be used:

  • psrp.AsyncPowerShell - runs a PowerShell command through asyncio
  • psrp.SyncPowerShell - runs a PowerShell command through synchronous code
  • psrp.AsyncCommandMetaPipeline - gets command metadata on the Runspace Pool through asyncio
  • psrp.SyncCommandMetaPipeline - gets command metadata on the Runspace Pool through syncronous code

The PowerShell pipeline is the commonly used pipeline that can run PowerShell commands, statements, and/or scripts.

Examples

Running PowerShell script

import psrp

async def async_rp(conn: psrp.ConnectionInfo) -> None:
    async with psrp.AsyncRunspacePool(conn) as rp:
        ps = psrp.AsyncPowerShell(rp)
        ps.add_script('echo "hi"')
        output = await ps.invoke()

        print(output)


def sync_rp(conn: psrp.ConnectionInfo) -> None:
    with psrp.SyncRunspacePool(conn) as rp:
        ps = psrp.SyncPowerShell(rp)
        ps.add_script('echo "hi"')
        output = ps.invoke()

        print(output)

This will run a PowerShell script and print out the output from that script. The output from invoke() is a list of PowerShell objects that are output from the remote pipeline.

Run a PowerShell command

import psrp

async def async_rp(conn: psrp.ConnectionInfo) -> None:
    async with psrp.AsyncRunspacePool(conn) as rp:
        ps = psrp.AsyncPowerShell(rp)
        ps.add_command("Get-Process").add_command("Select-Object").add_parameter("Property", "Name")
        ps.add_statement()
        ps.add_command("Get-Service").add_argument("audiosrc")
        output = await ps.invoke()

        print(output)


def sync_rp(conn: psrp.ConnectionInfo) -> None:
    with psrp.SyncRunspacePool(conn) as rp:
        ps = psrp.AsyncPowerShell(rp)
        ps.add_command("Get-Process").add_command("Select-Object").add_parameter("Property", "Name")
        ps.add_statement()
        ps.add_command("Get-Service").add_argument("audiosrc")
        output = ps.invoke()

        print(output)

This will run the PowerShell command Get-Process | Select-Object -Property Name; Get-Service audiosrv. Each command in a statement are piped and parameters/arguments are added to the last command. The statement will run as a separate line/statement in the script.

Copy a file to the remote host

import psrp

def copy_file(conn: psrp.ConnectionInfo) -> None:
    psrp.copy_file(conn, "/tmp/test.txt", r"C:\temp\test.txt")

Copies a local file to the remote PowerShell session.

Note: There is no asyncio analogue for this operation due to a lack of asyncio file libraries in the stdlib.

Fetches a file from the remote host

import psrp

def fetch_file(conn: psrp.ConnectionInfo) -> None:
    psrp.fetch_file(conn, r"C:\temp\test.txt", "/tmp/test.txt")

Fetches a remote file to the local filesystem.

Note: There is no asyncio analogue for this operation due to a lack of asyncio file libraries in the stdlib.

Run script with high level API

import psrp

async def async_invoke_ps(conn: psrp.ConnectionInfo, script: str) -> None:
    out, streams, had_errors = await psrp.async_invoke_ps(script)

    print(f"OUTPUT: {out}")
    if had_errors:
        errors = [str(e) for e in streams.error]
        print(f"ERROR: {errors}")



async def invoke_ps(conn: psrp.ConnectionInfo, script: str) -> None:
    out, streams, had_errors = psrp.invoke_ps(script)

    print(f"OUTPUT: {out}")
    if had_errors:
        errors = [str(e) for e in streams.error]
        print(f"ERROR: {errors}")

Uses the high level API to execute a PowerShell script and print out any errors that are returned.

Authenticating with Exchange Online

This shows you how to connect against Exchange Online with the Python MSAL library.

import hashlib

import msal
import psrp

from cryptography.hazmat.primitives.serialization import (
    Encoding,
    NoEncryption,
    PrivateFormat,
)
from cryptography.hazmat.primitives.serialization.pkcs12 import (
    load_key_and_certificates,
)

def get_msal_token(
    organization: str,
    client_id: str,
    pfx_path: str,
    pfx_password: str | None,
) -> str:
    private_key, main_cert, add_certs = load_key_and_certificates(
        pfx,
        pfx_password.encode("utf-8") if pfx_password else None,
        None
    )
    assert private_key is not None
    assert main_cert is not None
    key = private_key.private_bytes(
        Encoding.PEM,
        PrivateFormat.PKCS8,
        NoEncryption(),
    ).decode()

    cert_thumbprint = hashlib.sha1()
    cert_thumbprint.update(main_cert.public_bytes(Encoding.DER))

    app = msal.ConfidentialClientApplication(
        authority=f"https://login.microsoftonline.com/{organization}",
        client_id=client_id,
        client_credential={
            "private_key": key,
            "thumbprint": cert_thumbprint.hexdigest().upper(),
        },
    )

    result = app.acquire_token_for_client(scopes=[
        "https://outlook.office365.com/.default"
    ])
    if err := result.get("error", None):
        msg = f"Failed to get MSAL token {err} - {result['error_description']}"
        raise Exception(msg)

    return f"{result['token_type']} {result['access_token']}"


def main() -> None:
    tenant_id = "00000000-0000-0000-0000-000000000000"

    # This is the ID of the Application Role to authenticate as
    client_id = "00000000-0000-0000-0000-000000000000"

    msal_token = get_msal_token(
        "test.onmicrosoft.com",
        client_id,
        "exchange.pfx",
        "cert-password"
    )

    conn_info = psrp.WSManInfo(
        server="https://outlook.office365.com/PowerShell-LiveId?BasicAuthToOAuthConversion=true",
        auth="basic",
        username=f"OAuthUser@{tenant_id}",
        password=msal_token,
        configuration_name="Microsoft.Exchange",
    )

    with psrp.SyncRunspacePool(conn_info) = rp:
        ps = psrp.SyncPowerShell(rp)
        ps.add_command("Get-Mailbox")
        print(ps.invoke())

Logging

This library takes advantage of the Python logging configuration and messages are logged to the following named loggers

  • psrp.* - The loggers for code under the psrp namespace
  • pypsrp.* - The loggers for the code under the legacy pypsrp namespace

Note: DEBUG contains a lot of information and will output all the messages sent to and from the client. This can have the side effect of leaking sensitive information and should only be used for debugging purposes.

Testing

Any changes are more than welcome in pull request form, you can run the current test suite with tox like so;

# make sure tox is installed
pip install tox

# run the tox suite
tox

# or run the test manually for the current Python environment
python -m pytest tests/tests_psrp -v --cov psrp --cov-report term-missing

A lot of the tests either simulate a remote Windows host but you can also run a lot of them against a real Windows host. To do this, set the following environment variables before running the tests;

  • PYPSRP_SERVER: The hostname or IP of the remote host to test WSMan with
  • PYPSRP_USERNAME: The username to use with WSMan
  • PYPSRP_PASSWORD: The password to use with WSMan
  • PYPSRR_PORT: The port to connect with over WSMan (default: 5985)
  • PYPSRP_AUTH: The authentication protocol to auth with (default: negotiate)
  • PYPSRP_SSH_SERVER: The hostname or IP of the remote host to test SSH with
  • PYPSRP_SSH_USERNAME: The username to use with SSH
  • PYPSRP_SSH_PASSWORD: The password to use with SSH
  • PYPSRP_SSH_KEY_PATH: The path to a private key to use with SSH
  • PYPSRP_SSH_IS_WINDOWS: The remote SSH target is a Windows host

More Repositories

1

smbprotocol

Python SMBv2 and v3 Client
Python
293
star
2

ansible-windows

Scripts that are useful for using Ansible with Windows - not affiliated with Ansible in any way
PowerShell
196
star
3

packer-windoze

Packer templates to create Windows vagrant box images
PowerShell
165
star
4

pypsexec

Remote Windows execution like PsExec on Python
Python
113
star
5

PSOpenAD

Cross-platform PowerShell module alternative to Microsoft's Active Directory module
C#
101
star
6

PSDetour

Windows Detour Hooking in PowerShell
C#
71
star
7

dpapi-ng

Python DPAPI NG Decryptor for non-Windows Platforms
Python
55
star
8

PowerShell-AnsibleVault

Ansible Vault cmdlets for use in PowerShell
PowerShell
52
star
9

pyspnego

Python SPNEGO authentication library
Python
52
star
10

ntlm-auth

Calculates NTLM Authentication codes
Python
51
star
11

PSToml

PowerShell TOML Parser and Writer
PowerShell
44
star
12

PSPrivilege

Manage process privileges and adjust Windows rights/privileges in PowerShell
C#
39
star
13

LocalKdc

Info on how to use Kerberos KDC on a non-domain joined host
C#
37
star
14

ProcessEx

Exposes the Windows Process creation Win32 functions in PowerShell
C#
33
star
15

PowerShell-Yayaml

A YAML parser and writer that uses an Assembly Load Context on PowerShell 7+
PowerShell
30
star
16

PowerShell-OpenAuthenticode

Cross platform PowerShell implementation of Authenticode signing and verification
C#
28
star
17

ansible-role-win_openssh

Ansible Role to install Win32-OpenSSH on Windows - https://github.com/PowerShell/Win32-OpenSSH
PowerShell
28
star
18

ansibug

Debug Adapter Protocol for Ansible
Python
27
star
19

PSEtw

PowerShell ETW consumer module
C#
27
star
20

exchange-test-environment

A Vagrantfile and Ansible playbook that can be used to setup test environment with an Exchange server host
PowerShell
26
star
21

requests-credssp

An authentication handler for using CredSSP with Python Requests.
Python
21
star
22

PowerShell-ctypes

PowerShell module for ctypes/PInvoke calls
PowerShell
21
star
23

PowerShell-ALC

Example ALC structures to use with in a PowerShell module
PowerShell
20
star
24

SecretManagement.DpapiNG

PowerShell SecretManagement module for DPAPI-NG
PowerShell
18
star
25

pykrb5

Python krb5 API interface
C
16
star
26

PSWSMan

C#
16
star
27

winrm-cert-auth

Details around how to setup WinRM Certificate Authentication for use in Ansible
PowerShell
16
star
28

PSAccessToken

PowerShell Module that can query, edit, and create Windows Access Tokens
PowerShell
13
star
29

ansible-role-win_laps

Ansible Role to install LAPS on Windows - https://technet.microsoft.com/en-us/mt227395.aspx
PowerShell
12
star
30

PSDetour-Hooks

Auditing Hooks for https://github.com/jborean93/PSDetour
PowerShell
12
star
31

RemoteForge

PowerShell Custom PSRemoting Transport Manager
C#
11
star
32

PSGhost

PowerShell Host Methods using Spectre.Console
PowerShell
9
star
33

ansible-win-demos

Collection of demo scripts for Ansible and Windows
PowerShell
8
star
34

psrpcore

Core Library for the PowerShell Remoting Protocol
Python
8
star
35

PSCSharpInvoker

PowerShell module that can invoke C# code without polluting the Type namespace
PowerShell
8
star
36

PSSPI

PowerShell Module for SSPI
C#
7
star
37

PWSExecConn

PowerShell
7
star
38

ansible-role-win_chocolatey_server

Ansible Role to install Chocolatey Server on Windows - https://chocolatey.org/packages/chocolatey.server
PowerShell
7
star
39

sansldap

Python Sans I/O LDAP Library
Python
6
star
40

AdvReg

PowerShell Module for Advanced Registry Functionality
C#
4
star
41

vscode-ansibug

VSCode Extension for Debugging Ansible Playbooks
TypeScript
4
star
42

krb5-fast

Testing of Kerberos FAST on Linux
PowerShell
3
star
43

wsus-environment

Code to set up a WSUS environment
3
star
44

PSIntegrity

Manage the Windows integrity label policies on an object through PowerShell
PowerShell
3
star
45

SudoForge

PowerShell Remote Forge for Sudo
PowerShell
3
star
46

PInvokeHelper

Helper functions for PInvoking in PowerShell
PowerShell
3
star
47

TestBin

Test binary module for PowerShell
PowerShell
2
star
48

ansible-lookup-laps_password

Repo that contains tests for the laps_password Ansible lookup plugin
2
star
49

ansible-jborean93.vscode

POC for VSCode Ansible Debugger
Python
2
star
50

travis-ci-win-ansible

Testing repo for Travis-CI on Windows and Ansible
2
star
51

pyxca

Python Library for MS-XCA Xpress Compression Algorithm
C
2
star
52

brispug-repo

A repo for random things to do with the Brisbane PowerShell User Group
PowerShell
2
star
53

wsman-environment

Code to set up a WSMan environment
PowerShell
2
star
54

python-hyperv

Python API for Hyper-V
2
star
55

sspilib

Python bindings for Windows SSPI
Python
2
star
56

SSHForge

Hyper-V implementaiton for PowerShell RemoteForge
PowerShell
2
star
57

PowerShell-GistHub

GitHub Gist Provider for PowerShell
C#
1
star
58

CoverletTesting

Testing for Code Coverage in CI
PowerShell
1
star
59

PSConfEU-2024

Demos for PSConfEU talk
PowerShell
1
star
60

ProcessVirtualChannel

POC for a Process spawning RDP virtual channel
C#
1
star
61

ansible-content

Repo storing blog posts about Ansible
1
star