Covenant C2 Deep Dive

by Vince
in Blog
Hits: 8484

The description states:  "Covenant is a .NET command and control framework that aims to highlight the attack surface of .NET, make the use of offensive .NET tradecraft easier, and serve as a collaborative command and control platform for red teamers.  Covenant is an ASP.NET Core, cross-platform application that includes a web-based interface that allows for multi-user collaboration."

I'd previously written about Covenant and while I thought it was interesting, I didn't have a use for it then.  But as my engagements get larger, the ability to logically aggregate endpoints is a necessity and a second look at Covenant got me to really digging around in it.

A couple of points before I jump into it.  First, this is not an A to Z primer.  Second, and more importantly, it is a little buggy from time to time.  The more you play with it, the more you're going to learn the ins and outs.  I've been using it day in and day out for about 45 days.  I understand well enough to know when I should try something again (and perhaps again). 

Not to rehash what I've already written, I'm going to start with the listeners.  We setup a listener on port 80:


Next, we're going to create a PowerShell Launcher.  Note that I'm selecting Net40 -- this is for Windows 10.  Choose Net35 for < Windows 10:


We could embed this shell into a macro in a Word document but for the sake of mocking up a phishing scheme, we're going to use a straight up .hta file laced with our PowerShell Grunt:


On our victim, we open the HTA file and we catch our first shell.  Note that it's in Medium integrity.  Even though our user has local admin privileges, we can't use those privileges due to our current integrity level.


A simple feature in Covenant is the ability to add modify values.  For example -- Grunt information:


When we scroll down, we can modify the name.  I prefer to leave the generated name but I like to see the username as well. 


When we hit enter and we move back to the Grunts page, we see our change:


So let's interact with the session.  If we move to the Interact tab, we can execute the whoami command. 


Some commands are native within Covenant and other commands that are native to Windows can be executed in several ways.  Whoami is a native command to both Covenant and Windows.  In this case, as seen in the screenshot, we can just enter whoami.  If we want to execute that Windows command, we can enter:  shell whoami

We can also do this from the task page.  And we can see a list of command with autocomplete:


There are a number of ways to execute commands and initially, my instinct told me to stay in the interact window.  Truth be told, as you start to see commands fail, you move to tasks.  It's a little cumbersome but they work more often than from the Interact page. 

This is another occurrence when working with Covenant:


Sometimes Grunts fall off.  Sometimes they come back -- I wouldn't count on it though.  If you rename one, it will fall off for a second and then come back.  Sometimes when they fall off, if you execute a command, they come back.  Odds are pretty good though that if they do fall off, they are off for good.  Also recognize that this is just PowerShell running on the system so if it gets killed, the system reboots, or whatever, it's going to stop.

If you recall the screenshot from above, we can drill into this Grunt and select Hide on the lost Grunts.

Moving on...

Ok, so we have our Grunt, it's in Medium Integrity and we want to move this into High Integrity.  Covenant has been really built out with a bunch of other tools that you may or may not recognize.  Mimikatz for example.  Or SharpUp:


We run SharpUp audit and we find that we're a local admin.  I mentioned the Tasks section earlier:


As mentioned above, we need to Bypass UAC and that is built into Covenant:


This is where things get a little buggy from time to time:


That are a number of factors but there are also work arounds.  First thing we can do is start a new Taskmgr process with high integrity and move into that process:


We re-run Bypass UAC and that "usually" works but in this case it did not:


After some back and forth, I eventually get a high integrity Grunt.  We execute the SharpShell command again and get the new process:


We add the task:


Upon execution:


And now we have a High Integrity Grunt:


What's the saying?  One shell = zero shells.  There are a few ways to setup persistence:


This only works on boot so if you lose a shell, it could be a while.  Alternatively, you can setup a scheduled task but then it's going to keep adding Grunts as frequent as you schedule it.  Anyway, when we execute our task:


Excellent, we now have a mechanism in place for getting back on the box if we lose our Grunt.

This is just for show -- there's a Nishang reverse shell that I want to execute:


With the script hosted on our server, we execute the command:


With our handler setup:


We catch our inbound shell:


I realize this is wonky looking for a minute but welcome to hacking.  It eventually settles and we get on the box.  And, not great opsec.


I mentioned Mimikatz earlier:


Sometimes this works, sometimes it doesn't, try anyway.  In this case, it works:


We get an NTLM hash:


We crack the hash and we store that away for later use.  We can also use the hash which I will get into shortly. 

My goal is to elevate as high as I can so the idea of Impersonating hasn't actually hit home yet but we can move around with yet another task:


We execute the task:


Anyway, the hash I retrieved earlier -- not that this is a Covenant feature but I jump on the system with wmiexec:


I execute our Grunt script:


We catch another High Integrity Grunt session: 


I'm certain this will get snagged by a/v but I want to show another function:


I create a meterpreter msi file and I want to upload it:


When we execute the task:


Ok, if you're still hanging with me, this is where I get excited.  Some of this stuff is cool but I was starting to feel like Metasploit was dead for anything other than a handler.  Hold that thought...

I create a meterpreter payload, my output is raw, and I convert it to shellcode:


I setup my handler:


We enter the shellcode as a task:


Because it's getting executed in memory, the a/v doesn't pick it up which makes that not really great a/v. 


I found a process running as NT AUTHORITY\SYSTEM and I migrate into it:


Because I am forever going to keep adding Grunts:


And now we have one as SYSTEM:


Honestly, this is still just scratching the surface.  There's so much more that I haven't showed but with 50 images, I think this is a good second look.  I will personally keep using it and hopefully it will only get better.