Skip to main content

Asynchronous Python library for Windows Remote Management

Project description

awinrm

Asynchronous Python library for Windows Remote Management (WinRM)

Python 3.8+ License: MIT

awinrm is a fully asynchronous Python client for Windows Remote Management (WinRM). It allows you to execute commands, run PowerShell scripts, transfer files, and maintain interactive shell sessions on remote Windows machines.

���� Acknowledgements

This project is based on the excellent pywinrm library by Alexey Diyan and contributors. While pywinrm uses synchronous requests and external authentication libraries, awinrm was rewritten to be fully async with httpx and uses asyauth for authentication.

��� Features

  • Fully Asynchronous - Built with asyncio and httpx for non-blocking I/O
  • Pure Python Authentication - NTLM, Kerberos, SPNEGO, and CredSSP are all implemented in pure Python via asyauth and minikerberos (no system dependencies like krb5 or sspi required!)
  • Interactive Shells - Create persistent CMD or PowerShell sessions with keepalive support
  • File Transfer - Upload and download files via base64-encoded PowerShell commands
  • Message Encryption - Automatic GSSAPI message encryption for secure communication over HTTP
  • Graceful Termination - Clean handling of shell exits and session timeouts

���� Installation

pip install awinrm

Dependencies

All dependencies are pure Python:

  • httpx - Async HTTP client
  • asyauth - Pure Python implementation of NTLM, Kerberos, SPNEGO, CredSSP
  • unicrypto - Cryptographic primitives
  • aioconsole - Async console input for interactive shells

���� URL Format

awinrm uses URL-based credential specification (from asyauth). The format is:

<transport>+<auth_type>-<auth_method>://<username>:<password>@<host>:<port>/<path>?<options>

Components

Component Description Examples
transport HTTP or HTTPS http, https
auth_type Authentication protocol ntlm, kerberos, spnego
auth_method Credential type password, nt, aes, ccache
username User (with optional domain) user, DOMAIN\user, user@domain.com
password Password or hash plaintext password or NT hash
host Target hostname or IP 192.168.1.100, server.domain.com
port WinRM port (optional) 5985 (HTTP), 5986 (HTTPS)
options Query parameters dc=, proxytype=, etc.

URL Examples

# NTLM with password (local account)
"http+ntlm-password://administrator:Password123@192.168.1.100"

# NTLM with domain account
"http+ntlm-password://MYDOMAIN\\admin:Password123@server.mydomain.com"

# Kerberos with password
"http+kerberos-password://NORTH\\vagrant:vagrant@winterfell.north.sevenkingdoms.local/?dc=192.168.56.11"

# Kerberos with ccache (ticket cache)
"http+kerberos-ccache://winterfell.north.sevenkingdoms.local/?ccache=/tmp/krb5cc_1000"

# NTLM with NT hash (pass-the-hash)
"http+ntlm-nt://administrator:aad3b435b51404eeaad3b435b51404ee@192.168.1.100"

# HTTPS with NTLM
"https+ntlm-password://admin:secret@secure-server.domain.com:5986"

Query Parameters

Parameter Description
dc=<ip> Domain controller IP (for Kerberos)
ccache=<path> Path to Kerberos credential cache
proxytype=<type> Proxy type (socks5, http)
proxyhost=<host> Proxy hostname
proxyport=<port> Proxy port

���� API Usage

Basic Command Execution

import asyncio
from awinrm import Session

async def main():
    url = "http+ntlm-password://administrator:Password123@192.168.1.100"
    
    async with Session(url) as session:
        # Run a command
        stdout, stderr, return_code = await session.run_cmd('ipconfig', ('/all',))
        print(stdout.decode())
        
        # Run PowerShell
        stdout, stderr, return_code = await session.run_ps('Get-Process | Select-Object -First 5')
        print(stdout.decode())

asyncio.run(main())

Interactive Shell

import asyncio
from awinrm import Session, ShellTerminatedError

async def main():
    url = "http+ntlm-password://administrator:Password123@192.168.1.100"
    
    async with Session(url) as session:
        # Create a CMD shell
        async with session.create_shell(shell_type='cmd') as shell:
            # Send commands
            await shell.send_input(b'whoami\r\n')
            
            # Read output
            stdout = await shell.stdout.get()
            print(stdout.decode())
            
            # Check if shell is still running
            if not shell.is_terminated:
                await shell.send_input(b'exit\r\n')

asyncio.run(main())

PowerShell Shell

async with Session(url) as session:
    # Create a PowerShell shell (recommended for most use cases)
    async with session.create_shell(shell_type='powershell') as shell:
        await shell.send_input(b'$PSVersionTable\r\n')
        stdout = await shell.stdout.get()
        print(stdout.decode())

File Transfer

async with Session(url) as session:
    # Upload a file
    await session.upload_file(
        local_path='/tmp/script.ps1',
        remote_path='C:\\temp\\script.ps1',
        progress_callback=lambda sent, total: print(f'{sent}/{total} bytes')
    )
    
    # Download a file
    await session.download_file(
        remote_path='C:\\Windows\\System32\\drivers\\etc\\hosts',
        local_path='/tmp/hosts'
    )
    
    # Check if file exists
    exists = await session.file_exists('C:\\temp\\script.ps1')
    
    # Get file size
    size = await session.get_file_size('C:\\temp\\script.ps1')

Authentication Types

# SPNEGO (auto-negotiates NTLM or Kerberos)
async with Session(url, authtype='spnego') as session:
    ...

# CredSSP (allows credential delegation / double-hop)
async with Session(url, authtype='credssp') as session:
    ...

Shell Configuration

async with Session(url) as session:
    shell = session.create_shell(
        shell_type='powershell',      # 'cmd', 'powershell', or 'pwsh'
        working_directory='C:\\temp',
        env_vars={'MY_VAR': 'value'},
        codepage=65001,               # UTF-8
        keepalive_interval=60,        # Seconds between keepalive pings
        idle_timeout=300,             # Server-side idle timeout
    )
    async with shell:
        ...

Custom HTTP Transport

For advanced use cases (proxies, custom SSL, etc.):

import httpx

transport = httpx.AsyncHTTPTransport(
    verify=False,
    http2=True,
)

async with Session(url, transport=transport, verify=False) as session:
    ...

Exception Handling

from awinrm import (
    Session,
    ShellTerminatedError,
    ShellNotFoundError,
    WinRMError,
    WinRMTransportError,
    AuthenticationError,
)

async with Session(url) as session:
    async with session.create_shell() as shell:
        try:
            await shell.send_input(b'exit\r\n')
            await shell.read_output()
        except ShellTerminatedError as e:
            print(f"Shell exited with code: {e.exit_code}")
        except ShellNotFoundError:
            print("Shell was closed by the server")
        except AuthenticationError:
            print("Authentication failed")
        except WinRMTransportError as e:
            print(f"HTTP error: {e.code}")

������� Command-Line Tools

awinrm-runcmd

Execute a single command on a remote host:

# Basic usage
awinrm-runcmd 'http+ntlm-password://admin:pass@192.168.1.100' 'ipconfig /all'

# With verbose output
awinrm-runcmd -v 'http+ntlm-password://admin:pass@192.168.1.100' 'whoami'

# Using CredSSP
awinrm-runcmd -a credssp 'http+ntlm-password://admin:pass@192.168.1.100' 'hostname'

awinrm-cmdshell

Interactive remote shell:

# CMD shell
awinrm-cmdshell -s cmd 'http+ntlm-password://admin:pass@192.168.1.100'

# PowerShell shell (default)
awinrm-cmdshell 'http+ntlm-password://admin:pass@192.168.1.100'

# PowerShell Core
awinrm-cmdshell -s pwsh 'http+ntlm-password://admin:pass@192.168.1.100'

# With verbose logging
awinrm-cmdshell -v 'http+kerberos-password://DOMAIN\\user:pass@server/?dc=dc.domain.com'

awinrm-authcheck

Test authentication without executing commands:

# Test NTLM
awinrm-authcheck 'http+ntlm-password://admin:pass@192.168.1.100'

# Test Kerberos
awinrm-authcheck 'http+kerberos-password://DOMAIN\\user:pass@server/?dc=dc.domain.com'

# Verbose mode
awinrm-authcheck -v 'http+ntlm-password://admin:pass@192.168.1.100'

������ WinRM Server Configuration

Enable WinRM (on the Windows target)

# Quick setup (HTTP + NTLM)
winrm quickconfig -q

# Enable CredSSP (for credential delegation)
Enable-WSManCredSSP -Role Server -Force

# Check current config
winrm get winrm/config

Firewall

WinRM uses:

  • Port 5985 for HTTP
  • Port 5986 for HTTPS
# Allow WinRM through firewall
New-NetFirewallRule -Name "WinRM-HTTP" -DisplayName "WinRM (HTTP)" -Enabled True -Direction Inbound -Protocol TCP -LocalPort 5985 -Action Allow

���� Security Notes

  1. Use HTTPS in production - HTTP transmits credentials in a recoverable format
  2. Prefer Kerberos - More secure than NTLM, supports delegation
  3. Message encryption is automatic - Even over HTTP, NTLM/Kerberos encrypt message payloads
  4. CredSSP allows delegation - Use when you need to access network resources from the remote host

���� API Reference

Session

Method Description
run_cmd(command, args) Execute a command, return (stdout, stderr, rc)
run_ps(script) Execute PowerShell script
create_shell(shell_type, ...) Create interactive shell
create_powershell(...) Convenience method for PowerShell shell
upload_file(local, remote, callback) Upload file to remote
download_file(remote, local, callback) Download file from remote
file_exists(path) Check if remote file exists
get_file_size(path) Get remote file size

WinRMShell

Property/Method Description
is_terminated True if shell has exited
return_code Exit code (if terminated)
send_input(data) Send bytes to shell stdin
read_output() Read available stdout/stderr
wait_for_termination() Block until shell exits
close() Close the shell

Exceptions

Exception Description
WinRMError Base exception for all WinRM errors
WinRMTransportError HTTP-level errors (4xx, 5xx)
ShellTerminatedError Shell has exited (includes exit_code)
ShellNotFoundError Shell no longer exists on server
AuthenticationError Authentication failed (401)
WinRMOperationTimeoutError Operation timed out (retriable)

���� License

MIT License - see LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

awinrm-0.1.0.tar.gz (31.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

awinrm-0.1.0-py3-none-any.whl (30.7 kB view details)

Uploaded Python 3

File details

Details for the file awinrm-0.1.0.tar.gz.

File metadata

  • Download URL: awinrm-0.1.0.tar.gz
  • Upload date:
  • Size: 31.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.10.12

File hashes

Hashes for awinrm-0.1.0.tar.gz
Algorithm Hash digest
SHA256 dc7132c344a6e8d577dd487c048ca1a6353d6a2586a1c39ea982eda8da99dfdb
MD5 682ac83cb44879a9a34ce8307cc7e745
BLAKE2b-256 a4d788f8efec026cb74dfe2227ecf0982868c9f89d6f35d3266a8945a2c669ce

See more details on using hashes here.

File details

Details for the file awinrm-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: awinrm-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 30.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.10.12

File hashes

Hashes for awinrm-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 560f35bac7ded9e732ea78980d923e76a1f3ae3e054379f70b96d5bc6daa1470
MD5 7dfa0887df4245bbd450e97cffa1a9cc
BLAKE2b-256 da23728e5ee593b845843a8d199b9aef83e0083e024327a548240f3951db0f6d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page