Reverse Port Forward added to Covenant

Thiago Mayllart
stolabs
Published in
5 min readJun 30, 2020

Well… it’s been some time since I’ve been playing with Covenant and yeah… this is a very good tool. I’ve tried many other C2’s before, but ended up getting more comfortable with this one. I mean, you got inline C#/Powershell execution, many tools for Active Directory engagements, that pretty cool UI for sharing info with your team and… Roslyn compiler, allowing all those many tasks to be compiled in runtime and sent to the Grunt for execution by that little trick with Assembly.Load.

I simply had most of the things I needed… Scanning for some exploit in the network? Just had to find some powershell implementation of it. UAC Bypass? Let’s do it with powershell! Atacking AD? Pick your weapon in the tasks. But, one thing was always missing: Reverse Port Forward. Of course, you can abuse some connections in many ways and I’m not gonna get into it in this post, but let’s agree, doing it with Port Forward is much easier.

After some time wondering about this function, I just decided code it. And that was not that easy…

Alright, Let’s do this

Okay… so the first thing I had to figure out was how Covenant actually implements its tasks. Fine, Rasta Mouse had a cool post talking about it here: https://rastamouse.me/2019/12/covenant-tasks-101/. Some of the things are outdated since the new release 0.5, but the structure of Task class is just the same: a static class named Task and a static function called Execute. Everything was going fine, but then, I realized how the grunt was executing this code. The task code resides in the C2, when you call a Task, it compiles it and sends the resulting bytes in the connection to the Grunt. This is very useful, you achieve execution in memory of your tasks. The bytes are decompressed and loaded into an Assembly object. By using this object you can access the functions, classes, etc from the original class and that’s exactly what it does right here:

But then comes the problem: the objects are loaded and disposed right after execution, it doesn’t keep the reference after execution and to create a PortForward I had to keep maaany references: the grunt sockets, the target sockets , the read/write threads, the lists of active port forwards.. There was an option: Reflection, but that would probably modify a lot of the original code since it would need to implement the Task interface and I didn’t want that. So.. I decided to code directly in the Grunt code: it means that after the Stage 2 connection it would already come with the PortForward functionality. I agree.. there are ways to solve this problem with Dynamic objects and I might be taking a look on it…

Right, so after this I had to design the communication: I tried to make it very similar to our “portfwd” that many of you have already used. To do this I had to follow some rules: 1) do not bind to any port in the infected machine. 2) the grunt will use same port from your reverse connection to send the traffic from your port forward to your C2. I have to admit: I didn’t follow the second rule since it would need to make big change in the Listeners code and also in the Grunt code and this is actually the reason why it is pretty much“similar” to the portfwd.

The Traffic:

The design of the communication is quite simple:

  1. The C2 binds to a port (the one you want to connect).
  2. The C2 binds to another port, an auxiliary port to this traffic. I could have used the same port from 1., but didn’t feel comfortable to the Grunt connecting to a low number port in the logs (in case you decide to use one of them)…
  3. When you connect to the bound port from 1., the whole chain starts: the C2 starts accepting connections from the Grunt in the auxiliary port. The Grunt connects to it and also connects to the target.

4. Suppose you are targeting a remote desktop service in a machine, let’s say 192.168.1.15, and you bound to port 61234:

a) you connect to 61234 with your favorite RDP tool(mstsc, freerdp, rdesktop, etc).

b) The C2 waits for connections in the auxiliary port from the Grunt and only the Grunt. The Grunt connects to it.

c) The Grunt connects to 3389 in 192.168.1.15.

d) The traffic that gets into the C2 in 61234 is forwarded to the auxiliary Port; the socket in this port sends it to the Grunt and the Grunt sends it to the target.

e) When it receives from the target, it’s just the same but backwards.

5. Some protocols tend to need more ports: when it requires NTLM challenge and you get a “status_more_processing_required”, requiring you to connect again. The Socket class from C# is able to handle it with the help of the design explained above.

This is almost the whole procedure, there is only one thing missing: packet size. Some protocols need you to have the packets “trimmed”. It means that the same way you received it from the client connecting to the bound port in the C2 you send to the target, no extra bytes, no missing bytes.

Some protocols don’t need it, like HTTP, but some like SSH, SMB, RDP will definitely need it. The solution is using the good old approach of sending the size of the packet and allocating dynamic byte arrays for the receiving packets.

And after all this, it actually worked!! Can’t lie I got really surprised when I saw everything just working fine. After that, no more switching between shell’s to load a “portfwd”

Bonus: The Manual

In case anyone wants to use it, select the new Grunt: GruntHTTPPortForward. The Task name is: rportfwd. OBS: It does not work with docker.

You have 4 commands in the Task:

start [bind port] [forward host] [forward port] [ip_to_allow_connections]//Starts a new reverse port forwardstop [bind port]//Stops an existing reverse port forward.flush//Flush all reverse port forwards on an Agent.list//Returns a list of current reverse port forwards.//Example: rportfwd /command:"start 445 192.168.15 445 201.94.112.13"

I decided to add an additional and mandatory parameter: ip_to_allow_connections. This is the IP you want to allow connections to your C2 bound port (e.g. your public IP). Only this IP will be able to connect to the “bind port”.

The Code:

https://github.com/thiagomayllart/Covenant_ReversePortForward

--

--