Windows: ManagementObject Arbitrary .NET Serialization RCE

Issue description

Windows: ManagementObject Arbitrary .NET Serialization RCE
Platform: .NET 4.6, Powershell 4. Tested between Server 2016 and Windows 10 Anniversary Edition
Class: Remote Code Execution
Summary:
Accessing a compromised WMI server over DCOM using System.Management classes or the Powershell Get-WmiObject Cmdlet can lead to the server running arbitrary code on the calling machine leading to RCE.
Description:
The dangers of using .NET for DCOM are well know, the SRD blog made a post (https://blogs.technet.microsoft.com/srd/2014/10/14/more-details-about-cve-2014-4073-elevation-of-privilege-vulnerability/) which explicitly said it shouldn’t be used between trust boundaries. Presumably people took this to mean implementing servers, but it’s also a risk if a .NET DCOM client connects to an untrusted endpoint. This is due to the IManagedObject interface which will automatically force a client to deserialize an untrusted BinaryFormatter stream which is known bad.
One common use of DCOM in the .NET framework is for WMI access. The old classes in the System.Management namespace are still accessible (even though technically supersceded by Cim classes) and in powershell they act as the backend for Get-WmiObject and family. Through inspection it’s clear that a number of places the client querys for IManagedObject (for example on the IWbemServices object returned from IWbemLevel1Login::NTLMLogin method) and would be vulnerable. If this interface is being queried it means that a .NET client is trying to create an RCW and will try and create a local copy of a remote serializable object.
Therefore in corporate scenarios where some central system is using WMI over DCOM for management and analysis of running systems (and the management code is using the old .NET/PS classes to do the calls) a compromised machine which replaces the WMI service with its own malicious one could get arbitrary code execution on the monitoring machine. As this is typically going to be a higher privileged account (due to the requirements of DCOM access) it probably makes it more serious.
Looking at the network traffic the initial CreateInstance call on the remote activator is only using CONNECT level authentication. This means that it might also be possible to MITM (or Man-At-The-Side) a .NET WMI client and send it back a malicious COM objref to get it to communicate with the attacker's server.
Of course ideally no one would do this, or use the old style .NET and PS commands. But I’m sure there are networks out there which do so.
Proof of Concept:
I’ve provided a PoC as a C# project. You’ll need to also set up some machines to test this out. I’ve tested it in a simple environment of a Server 2016 server acting as a DC and a Windows 10 client. The serialized stream is tailored specifically for 4.6, I don’t know if it works anywhere else.
1) Compile the C# project and copy the binary to c:\service\FakeWmiServer.exe on the Client machine.
2) Run the following commands in admin Powershell on the client machine to configure the WMI service and add the server executable to the firewall.
New-NetFirewallRule -DisplayName FAKEWMI -Enabled True -Profile Any -Direction Inbound -Program C:\service\FakeWMIService.exe -Protocol Tcp -LocalPort Any -RemotePort Any -LocalAddress Any -RemoteAddress Any
New-NetFirewallRule -DisplayName FAKEWMI -Enabled True -Profile Any -Direction Outbound -Program C:\service\FakeWMIService.exe -Protocol Tcp -LocalPort Any -RemotePort Any -LocalAddress Any -RemoteAddress Any
sc.exe config winmgmt binPath= c:\service\FakeWMIService.exe type= own
Restart-Service winmgmt -Force
3) On the server start powershell.
4) On the server execute the PS command “Get-WmiObject -Class Win32_Process -ComputerName hostname” replacing hostname with the address of the client.
Expected Result:
WMI connection fails.
Observed Result:
A copy of CMD and Notepad is executed on the server in the context of the calling user.
This bug is subject to a 90 day disclosure deadline. If 90 days elapse without a broadly available patch, then the bug report will automatically become visible to the public.