Windows Remote Management (WinRM) is Microsoft’s implementation of the WS-Management (WSMan) protocol, which is used for exchanging management data between machines that support it. WSMan, in the case of Windows, supplies this data from WMI and transmits them in the form of SOAP messages. More info here. Why is any of this important to you?
Well, say you have a high-context shell on a single system and known good administrative creds to other systems, but you are afraid to use traditional lateral movement techniques (such as Metasploit’s PSExec or Empire’s invoke_wmi) as you suspect you will get busted by their shiny new $NextGenAV. More and more AVs are catching previously hard-to-detect techniques as well as commands that rely fundamentally on an encoded PowerShell command (powershell.exe -enc…).
So why another article on WinRM?
This article is about the practical outworking of WinRM via a single meterpreter session (much of this carries over into any other exploit kit that you can achieve an interactive shell with). Metasploit modules like auxiliary/scanner/winrm/winrm_cmd are great, but they just sent the POST request to WSMan, and don’t do any of the pre-work necessary to get WinRM configured on the client or target systems. Additionally, you will be more effective as a pentester if you understand what your tools are doing and how the underlying technology works. You will also be in a better position to think on the fly and troubleshoot when things don’t work. =)
So, to summarize:
- Execute code on a target domain-joined Windows system in a stealthy(er) way
- You have a working meterpreter payload that won’t get flagged by AV
- You are in a high-context shell
- You have administrative credentials to your target
- You have the necessary routing to the target setup via your session
- You see WinRM open on your target (tcp: 5985 (HTTP) or 5986 (HTTPS))
For purposes of this article, we have an administrative meterpreter shell on our victim machine, LAB-WIN10, and we have administrative credentials to our target machine: LAB-2012-DC2 (192.168.2.211). As I’m sure you guessed, the operating systems are Windows 10 & 2012 R2, respectively. Sure, we could just fire PSExec and hope for the best, but that will likely get you caught. Let’s try to be a bit more stealthy. =)
We’ve also confirmed that WinRM is open on our target (in this case, just HTTP on 5985 is configured):
Note, if WinRM is closed on both ports, you could use the auxiliary/admin/smb/psexec_command module to run “winrm qc –q” which will quietly enable it. Again, greater likelihood of getting caught.
Next, we confirm the available WinRM authentication types:
Ok, good. Multiple authentication options. Finally, confirm our creds work against the target:
Sweet! We should be able to execute commands using WinRM!
Whaaa? Why no worky?
Not So Fast….
What happened? Well, it just so happens that the default installation of WinRM is not totally insecure (even running winrm quickconfig). By default, it does not process unencrypted messages that were sent to it. As you can tell, it also doesn’t send back a polite “please encrypt this” message either. You just get a raw 500 error. I opened an issue on Metasploit’s github page regarding this and was informed that, pertaining to WinRM, Metasploit currently does not support Kerberos or encrypted communications, meaning you’re likely out of luck if trying to connect to a WinRM instance on Server 2012, which sets AllowUnencrypted to False by default.
Quick note to defenders: If you see event id 1048 in your WinRM > Analytical log, that might just be an unstealthy bad guy:
So we know that WinRM does in fact allow remote commands to be sent to it (hence the name); they just have to be authenticated and encrypted. This is where understanding our native tools can help greatly. To invoke our friends at Offensive Security, every pentest has a “try harder” moment (unless you haven’t patched MS17-010 :-/), so let’s keep going!
PowerShell provides some great cmdlets that allow us to remotely connect to WSMan. Additionally, you won’t be invoking any low-level functions such as Add-Type that might get you flagged.
To begin, let’s see if we can establish a connection to our target using Connect-WSMan:
So this failed, but a helpful error was provided. This says default authentication can’t be used with an IP address unless HTTPS and credentials are used. In this case, HTTPS is closed, so what do we do? We try the target computer name explicitly so Kerberos can be properly utilized (with the expectation that it will fail because we don’t have access as our current user, LAB\bobadmin):
Ok, failed as expected. However, the Connect-WSMan cmdlet also allows us to pass a PowerShell credential object, meaning we can pass credentials explicitly. To make this easier, let’s load the Meterpreter PowerShell extension:
Next, we are going to drop into a PowerShell shell and perform the following tasks:
- Set up a PowerShell credential object using our password for LAB\Bill-DA.
- Attempt to connect to the WSMan instance on our remote target.
- Display the available WSMan containers after our connection attempt. If everything goes well, we should see an instance for our target machine:
Awesome! We now have a remote connection to our target’s WSMan instance. From here on out, you can use Get-Item and Set-Item to enumerate and change WinRM’s parameters on your target.
Yes, you could change the AllowUnencrypted attribute if you so desire, but I’ll leave that to you to figure out. The good news is that you don’t need to change anything to remotely execute PowerShell on your target. The Invoke-Command cmdlet is here to help!
Note we are still using our credential object $c that we set above.
Outstanding. From here you can pass in any PowerShell code you want on the target system.