Ryan Cobb has just tagged the release of SharpSploit v1.6, which comes with a number of cool changes. The most significant of which includes some very clever Dynamic Invocation functionality that TheWover has blogged about. My contributions were relatively minor and will be the subject of this post.

Enhanced WMI Output

SharpSploit has had a WMIExecute method in the SharpSploit.LateralMovement namespace for as long as I can remember. It took in a target ComputerName, a Command to execute and optional plaintext creds, but it returned a bool.

public static bool WMIExecute(string ComputerName, string Command, string Username = "", string Password = "")

The remaining method looked a bit like this:

try
{
    // do the WMI magic

    Console.WriteLine("Win32_Process Create returned: " + outParams["returnValue"].ToString());
    Console.WriteLine("ProcessID: " + outParams["processId"].ToString());
    return true;
}
catch
{
    Console.Error.WriteLine("WMI Exception:" + e.Message);
    return false;
}

There were a few issues with this, at least from my perspective.

  1. It’s writing to the application’s console.

    We may not have visibility of the console if SharpSploit is being executed in a RAT such as Convenant’s Grunt and if we’ve injected the RAT into a process that doesn’t even have a console, this may crash the process.

  2. There is no validation on the returnValue before returning true.

    Veterans of WMI will know that there are a few return codes that are possible and not all of them indicate successful execution. This is particularly troublesome when built into places like Covenant’s WMI lateral movement task, as the task can report execution was successful when it wasn’t.

To try and improve this, the method was reworked to return a SharpSploitResultList of type WmiResult. WmiResult is a simple class that contains two properties for the actual ReturnValue and ProcessID.

SharpSploitResultList<WmiResult> wmiResult = new SharpSploitResultList<WmiResult>();

try
{
    // do the WMI magic

    wmiResult.Add(new WmiResult
    {
        ReturnValue = outParams["returnValue"].ToString(),
        ProcessID = outParams["processId"].ToString()
    });
}
catch { }

return wmiResult;

And this is how it looks:

C:\>WmiDemo.exe WIN-CJ8120QPH84 C:\Windows\System32\win32calc.exe

ReturnValue  ProcessID
-----------  ---------
0            1196
PS C:\Users\Administrator> hostname
WIN-CJ8120QPH84

PS C:\Users\Administrator> Get-Process -Id 1196

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
    125      13     4792      11804       0.03   1196   0 win32calc

Reverse Port Forwarding

The larger change on my part came with the addition of the SharpSploit.Pivoting namespace and new methods for creating, listing and stopping reverse port forwards. I won’t cover the code in intricate detail - just some usage examples.

Starting

Creating a new reverse port forward is as easy as:

ReversePortForwarding.CreateReversePortForward(8080, "httpbin.org", 80);

Where 8080 is the bind port, httpbin.org is the forward host (IP addresses and domain names are supported) and 80 is the forward port. This method returns a bool.

If successful, you will see port 8080 bound on the host.

C:\>netstat -anp tcp | findstr 8080
  TCP    0.0.0.0:8080           0.0.0.0:0              LISTENING

Listing

You can list current reverse port forwards with:

var list = ReversePortForwarding.GetReversePortForwards();
Console.WriteLine(list);

This returns a SharpSploitResultList of type ReversePortFwdResult. This class contains the BindAddress, BindPort, ForwardAddress and ForwardPort.

BindAddresses  BindPort  ForwardAddress  ForwardPort
-------------  --------  --------------  -----------
0.0.0.0        8080      52.6.108.56     80

This test machine cannot talk to the Internet directly.

PS C:\Users\Administrator> curl http://httpbin.org/base64/SFRUUEJJTiBpcyBhd2Vzb21l
curl : The remote name could not be resolved: 'httpbin.org'

But it can via the reverse port forward.

PS C:\Users\Administrator> $data = curl http://DESKTOP-U3N86EQ:8080/base64/SFRUUEJJTiBpcyBhd2Vzb21l
PS C:\Users\Administrator> $data.RawContent
HTTP/1.1 200 OK
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: 18
Content-Type: text/html; charset=utf-8
Date: Tue, 09 Jun 2020 13:53:48 GMT
Server: gunicorn/19.9.0

HTTPBIN is awesome

Deleting

There are two methods for removing reverse port forwards. FlushReversePortFowards (returns void) will indiscriminately remove all of them and DeleteReversePortForward (returns bool) will remove a single reverse port forward by its bind port.

ReversePortForwarding.DeleteReversePortForward(8080);

Obviously when the reverse port forward is stopped, the port is released.

Conclusion

The updates to WMI are more quality of life changes and should be reflected in the relevant Covenant Tasks soon. I hope that the new reverse port forwarding can help bring some additional capabilities to similar tools that leverage SharpSploit.