skelsec/awinrm

GitHub: skelsec/awinrm

纯 Python 异步 WinRM 客户端,支持多种认证方式和交互式远程管理 Windows 主机

Stars: 19 | Forks: 1

# awinrm **用于 Windows 远程管理 (WinRM) 的异步 Python 库** [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) `awinrm` 是一个完全异步的 Python Windows 远程管理 (WinRM) 客户端。它允许你在远程 Windows 机器上执行命令、运行 PowerShell 脚本、传输文件以及维护交互式 Shell 会话。 ## ✨ 功能特性 - **完全异步** - 基于 `asyncio` 和 `httpx` 构建,实现非阻塞 I/O - **纯 Python 认证** - NTLM、Kerberos、SPNEGO 和 CredSSP 均通过 `asyauth` 和 `minikerberos` 以纯 Python 实现(无需 `krb5` 或 `sspi` 等系统依赖!) - **交互式 Shell** - 创建支持 keepalive 的持久化 CMD 或 PowerShell 会话 - **文件传输** - 通过 base64 编码的 PowerShell 命令上传和下载文件 - **消息加密** - 自动进行 GSSAPI 消息加密,确保通过 HTTP 通信的安全性 - **优雅终止** - 干净地处理 Shell 退出和会话超时 ## 📦 安装 ``` pip install awinrm ``` ### 依赖项 所有依赖项均为纯 Python: - `httpx` - 异步 HTTP 客户端 - `asyauth` - NTLM、Kerberos、SPNEGO、CredSSP 的纯 Python 实现 - `unicrypto` - 密码学原语 - `aioconsole` - 用于交互式 Shell 的异步控制台输入 ## 🔗 URL 格式 `awinrm` 使用基于 URL 的凭据规范(来自 `asyauth`)。格式如下: ``` +-://:@:/? ``` ### 组成部分 | Component | Description | Examples | |-----------|-------------|----------| | `transport` | HTTP 或 HTTPS | `http`, `https` | | `auth_type` | 认证协议 | `ntlm`, `kerberos`, `spnego` | | `auth_method` | 凭据类型 | `password`, `nt`, `aes`, `ccache` | | `username` | 用户名(可选域名) | `user`, `DOMAIN\user`, `user@domain.com` | | `password` | 密码或哈希 | 明文密码或 NT 哈希 | | `host` | 目标主机名或 IP | `192.168.1.100`, `server.domain.com` | | `port` | WinRM 端口(可选) | `5985` (HTTP), `5986` (HTTPS) | | `options` | 查询参数 | `dc=`, `proxytype=`, 等 | ### URL 示例 ``` # 带密码的 NTLM(本地账户) "http+ntlm-password://administrator:Password123@192.168.1.100" # 带密码的 NTLM(域账户) "http+ntlm-password://MYDOMAIN\\admin:Password123@server.mydomain.com" # 带密码的 Kerberos "http+kerberos-password://NORTH\\vagrant:vagrant@winterfell.north.sevenkingdoms.local/?dc=192.168.56.11" # 带 ccache 的 Kerberos(票据缓存) "http+kerberos-ccache://winterfell.north.sevenkingdoms.local/?ccache=/tmp/krb5cc_1000" # 带 NT hash 的 NTLM(pass-the-hash) "http+ntlm-nt://administrator:aad3b435b51404eeaad3b435b51404ee@192.168.1.100" # 带 NTLM 的 HTTPS "https+ntlm-password://admin:secret@secure-server.domain.com:5986" ``` ### 查询参数 | Parameter | Description | |-----------|-------------| | `dc=` | 域控制器 IP (用于 Kerberos) | | `ccache=` | Kerberos 凭据缓存路径 | | `proxytype=` | 代理类型 (`socks5`, `http`) | | `proxyhost=` | 代理主机名 | | `proxyport=` | 代理端口 | ## 📖 API 用法 ### 基本命令执行 ``` 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()) ``` ### 交互式 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()) ``` ### 文件传输 ``` 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') ``` ### 认证类型 ``` # SPNEGO(自动协商 NTLM 或 Kerberos) async with Session(url, authtype='spnego') as session: ... # CredSSP(允许凭据委派 / 双跳) async with Session(url, authtype='credssp') as session: ... ``` ### Shell 配置 ``` 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: ... ``` ### 自定义 HTTP 传输 用于高级用例(代理、自定义 SSL 等): ``` import httpx transport = httpx.AsyncHTTPTransport( verify=False, http2=True, ) async with Session(url, transport=transport, verify=False) as session: ... ``` ### 异常处理 ``` 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}") ``` ## 🛠️ 命令行工具 ### awinrm-runcmd 在远程主机上执行单个命令: ``` # 基本用法 awinrm-runcmd 'http+ntlm-password://admin:pass@192.168.1.100' 'ipconfig /all' # 带详细输出 awinrm-runcmd -v 'http+ntlm-password://admin:pass@192.168.1.100' 'whoami' # 使用 CredSSP awinrm-runcmd -a credssp 'http+ntlm-password://admin:pass@192.168.1.100' 'hostname' ``` ### awinrm-cmdshell 交互式远程 Shell: ``` # CMD shell awinrm-cmdshell -s cmd 'http+ntlm-password://admin:pass@192.168.1.100' # PowerShell shell(默认) 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' # 带详细日志 awinrm-cmdshell -v 'http+kerberos-password://DOMAIN\\user:pass@server/?dc=dc.domain.com' ``` ### awinrm-authcheck 测试认证而无需执行命令: ``` # 测试 NTLM awinrm-authcheck 'http+ntlm-password://admin:pass@192.168.1.100' # 测试 Kerberos awinrm-authcheck 'http+kerberos-password://DOMAIN\\user:pass@server/?dc=dc.domain.com' # 详细模式 awinrm-authcheck -v 'http+ntlm-password://admin:pass@192.168.1.100' ``` ## ⚙️ WinRM 服务器配置 ### 启用 WinRM(在 Windows 目标机上) ``` # 快速设置(HTTP + NTLM) winrm quickconfig -q # 启用 CredSSP(用于凭据委派) Enable-WSManCredSSP -Role Server -Force # 检查当前配置 winrm get winrm/config ``` ### 防火墙 WinRM 使用: - **端口 5985** 用于 HTTP - **端口 5986** 用于 HTTPS ``` # 允许 WinRM 通过防火墙 New-NetFirewallRule -Name "WinRM-HTTP" -DisplayName "WinRM (HTTP)" -Enabled True -Direction Inbound -Protocol TCP -LocalPort 5985 -Action Allow ``` ## 🔐 安全说明 1. **在生产环境中使用 HTTPS** - HTTP 会以可恢复的格式传输凭据 2. **优先使用 Kerberos** - 比 NTLM 更安全,支持委派 3. **消息加密是自动的** - 即使通过 HTTP,NTLM/Kerberos 也会加密消息负载 4. **CredSSP 允许委派** - 当你需要从远程主机访问网络资源时使用 ## 📚 API 参考 ### Session | Method | Description | |--------|-------------| | `run_cmd(command, args)` | 执行命令,返回 (stdout, stderr, rc) | | `run_ps(script)` | 执行 PowerShell 脚本 | | `create_shell(shell_type, ...)` | 创建交互式 Shell | | `create_powershell(...)` | PowerShell Shell 的便捷方法 | | `upload_file(local, remote, callback)` | 上传文件到远程 | | `download_file(remote, local, callback)` | 从远程下载文件 | | `file_exists(path)` | 检查远程文件是否存在 | | `get_file_size(path)` | 获取远程文件大小 | ### WinRMShell | Property/Method | Description | |-----------------|-------------| | `is_terminated` | 如果 Shell 已退出则为 True | | `return_code` | 退出代码(如果已终止) | | `send_input(data)` | 发送字节到 Shell stdin | | `read_output()` | 读取可用的 stdout/stderr | | `wait_for_termination()` | 阻塞直到 Shell 退出 | | `close()` | 关闭 Shell | ### 异常 | Exception | Description | |-----------|-------------| | `WinRMError` | 所有 WinRM 错误的基类异常 | | `WinRMTransportError` | HTTP 层级错误 (4xx, 5xx) | | `ShellTerminatedError` | Shell 已退出(包含 exit_code) | | `ShellNotFoundError` | Shell 在服务器上不再存在 | | `AuthenticationError` | 认证失败 (401) | | `WinRMOperationTimeoutError` | 操作超时(可重试) | ## 📄 许可证 MIT License - 详情见 [LICENSE](LICENSE)。
标签:Asyncio, Awesome, CredSSP, CTF学习, DNS枚举, GSSAPI加密, Httpx, IPv6, Kerberos认证, NTLM认证, OpenCanary, PE 加载器, PowerShell, Python, SPNEGO, Windows远程管理, WinRM, 交互式Shell, 内网渗透, 异步编程, 文件传输, 无后门, 横向移动, 系统管理, 纯Python实现, 编程规范, 网络信息收集, 网络安全工具, 自动化运维, 计算机取证, 运行时操纵, 远程命令执行, 远程控制, 逆向工具