Actually Using Covenant C2 and Not Just Installing It!

Actually Using Covenant C2 and Not Just Installing It!

This is not going to be one of those posts about how to setup and install Covenant. Seriously, there are too many of them.  Covenant might be on of the easiest C2 frameworks to get setup and running currently.  During a recent redteam I was deploying Covenant C2 and there were many resources on the 4 commands required to setup and run Covenant.  However, when I started to dig into the interaction more I found that many of the blog posts stopped just after the install process.  Which is what has fueled this blog post.  So the next person who goes searching for some examples on how to use Covenant will hopefully find this post and will find it helpful. Even though I hope you continue to read this blog post, it is also imperative to encourage the age old tradition of telling those hoping to use Covenant; RTFM!  https://github.com/cobbr/Covenant/wiki .  The wiki will answer many of the questions, but I will be the first to say it's not the most complete wiki ever. Hopefully, the documentation will be improved as development continues.

Alright I know I just complained about Covenant setup blogs, but it's so easy I can put it into one single code block.

git clone --recurse-submodules https://github.com/cobbr/Covenant
cd Covenant/Covenant
dotnet build
dotnet run

Alright that might be a slight exaggeration, because you will need to have dotnet core version 2.2 specifically to run this!  But you can simply download that from here: https://dotnet.microsoft.com/download/dotnet-core/2.2 .  If that's not enough help just try the Google search "How to install Covenant C2"  you will have links for days!

At this point of the blog post I'm going to assume that you have the C2 up and running.  After running the command dotnet run the screenshot below should be similar to what you are seeing on your terminal.  If you see that the C2 is now listening you can simply open your browser and navigate to the address https://127.0.0.1:7443.

If this is the first time you are setting up Covenant then you might need to create a initial user like the screenshot below demonstrates.

One of the first pages that you might notice is the Dashboard page.  This will contain an overview of the information collected during the engagement.  Such as the remote target machines, also known as Grunts.  The Dashboard looks a bit lonely.  Let's change that.

The first step always will be starting a new listener.  It should be noted that the listener being deployed here is different than the listener running for the Covenant web UI.  This listener will be used for all the reverse connections that will be coming in.  Covenant supports listener profiles much like the way Cobalt Strike allows you to customize the network traffic to blend in. However, for this demonstration the default profile will be suitable.

Once clicking the blue Bootstrap button "Create" you will be taken to the configuration page where you can change different facets of the listener.  For example, you can leave the default randomized Name field or you can change it to something that you would prefer.  You can change the port to make sure there are no conflicts in the case that you might also be running a webserver on port 80.  To ensure that your payloads are generated correct enter a ConnectAddress as your routable address.  That could mean your externally facing address if you are hosting Covenant on a cloud resource.  Which, since we are on the topic, I highly recommend deploying the listener behind a HTTP redirect instance.  That way if your IP address gets burned by those pesky blue teamers you can easily switch up your HTTP redirector and not re-deploy your whole Covenant C2.  

Once you have customized the listener to your liking click the Create button.  This should show you the randomized ID along with the listener type.  If you were to start multiple listeners then each would be shown here on this page.  

We cannot just trust the web UI that the network service was started.  We can check to make sure the listener is up and running on the command line by running a netstat -pantu | grep LIST to view all listening ports.  Cool!  our new listener on port 80 is listening on all addresses.  

The next vocabulary word you must understand in Covenant is that of Launchers.   Launchers are use to generate, host, and download binaries, scripts, and one-liners to launch new Grunts.  Basically Launchers are meant to get remote access on the target machines.  The screenshot below shows the Launchers page with all the different options of getting your Grunt payload to the remote machine.  Of course you could always use an external method such as CrackMapExec, but Covenant provides some slick ways to get the launchers to the machine.  

For this blog post I'm going to concentrate on the PowerShell launcher.  I should mention that this is not OpSec safe.  You should evaluate your dropper methods before running them in a real redteam engagement.  Stay safe and don't get caught!  Since this is just a blog post I'm just going to use the out of the box PowerShell Launcher with Windows Defender turned off for ease of use.  The screenshot below shows the Launcher configuration page for the PowerShell launcher.  You will need to specify a listener to call back to, and you can tweak the other configurations to fit your use case.  Once the launcher is configured appropriately clicking the Generate button will create two separate commands.  The first will be an plaintext string, with a base64 payload inside of the PowerShell command.  The second will be a fully base64 encoded command that will be run by PowerShell using the enc flag.  

Important Note: It should be noted, because it took me too long to figure this out, but Windows PowerShell base64 does not equate to Linux base64.  If you try to base64 encode a command on Linux and paste it into PowerShell it will not run.  That is because PowerShell uses base64 encoding with a UTF-16 format.  The following command might help with generating a base64 string on Windows if you need it:

$command = 'Your PowerShell Command Here'
[Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($command))

Again, since the point of this blog is not bypassing AV I will be running the plaintext PowerShell command with Windows Defender turned off.  The screenshot below shows the PowerShell command being pasted into a cmd.exe prompt.  

If you were paying close attention to notice, Covenant adds an extra flag that says     -Window Hidden this will close the window once run.  If you are having issues with your Grunt returning to your C2 infrastructure consider removing this flag to see if there are any error messages in your PowerShell command.  Once the remote one-liner executes you should be able to navigate to the Grunts page and see that your target machine has successfully checked in.  

If this is your first C2 it should be noted the difference between a C2 and a simple reverse shell.  A reverse shell will create a constant connection between the victim machine and the attacking machine.  This gives you instant access and will return data in a speedy fashion.  However, with a C2 infrastructure typically will operate over a "check-in" system.  In order to be stealthy as possible the C2 will not keep a constant connection, but instead will send single requests asking if there are any new tasks to be performed.  If the C2 control has a task it will return it and the action will be performed returning the results.  If the C2 control has no tasks for the victim machine then the victim will sleep for a defined period of time, repeating the "Check-In" process after the sleep.  

Clicking the hyper link for the Name UID will take you to a information page about the specific Grunt.  It will give you information based on collected data from the initial call back.  There are also three other tabs in each Grunt page.  The Interact, Task, and Tasking pages.  These will serve as the main way to interact with the now controlled victim machine.  

Exploring the first tab the Interact tab will provide a command line like interface that can be used to perform any of the Tasks that are featured.  Using the help command will give you a list of the Tasks that can be performed.  

The screenshot below shows an example of the Task ShellCmd which you can then insert a command into and will be run on the victim machine returning the results to the command line like interface.  

Another interesting Task is the ScreenShot which will give you a screenshot as the target machine is being utilized.  This can be helpful if the target user is performing work on sensitive data that might be on screen during the breach.  Or I have seen during live engagements the ScreenShot Task used to determine if a end-user is aware of the attacker's presence.  

There are more Tasks in Covenant than could be documented in this blog post.  In fact each Task could have a short blog post documenting the features and concerns with running it during a redteam engagement.  This is where Covenant is nice, by clicking the Tasks tab while on the Grunt tab will give you a list of all the Tasks available as well as a description, Reference Source Libraries, Reference Assemblies, and other information that will help determine if it's right for your use-case.  

Clicking the hyperlink to an individual task will open the page and give you even more information regarding the Tasks.  The screenshot below demonstrates that BypassAmsi Task and the information showed about it.  

One of the features I am most excited about within Covenant is the ability to read and edit the code of Tasks directly within the web UI.  This allows for quick understanding of what is expected to be provided for the Task as well as what information might be sent back.  Or if you need to make changes on the fly it is very helpful.  

While talking about Tasks I should also mention that the additional tabs on the Tasks list will provide information about Reference Source Libraries as well as what DLLs and other information is used during the execution of this task.  This can be helpful if you are trying to avoid being caught by the blueteam and are trying to only run OpSec safe tasks.  All Tasks should be reviewed for OpSec before running!

The final tab shown under the individual Grunt ID is the Taskings tab.  This will give you a list of Tasks that were executed on the specified Grunt.  This is helpful if you need to review information returned from a Task or to see if the Task executed successfully.  

Important Note: For some reason Covenant does not list these items in chronological order.  However, if you click the CompletionTime tab at the top it will then sort by finished time.  

As with many of the other pages in Covenant clicking the Name ID hyperlink from the Tasking page will allow you to see the information provided, executed, and returned from running a particular Task.  

Now that we have covered the individual interaction pages of the Grunts, let us begin actually utilizing the Tasks and run some commands on our test systems.  The first command I am running for this blog post is the Mimikatz Task.  Note that Mimikatz is not OpSec safe and many incident response teams will be looking for that process. Since we know that monitoring is not an issue for this blog post running Mimikatz will be fine.  The screenshot below shows the Grunt that is running on our target machine and the selected Task is Mimikatz.  

Selecting the Task button showed in the screenshot above will start the Task execution.  Once the Grunt has checked back in with our C2 control the Task executes and the output is returned to the GruntTasking page.  As we can see from the screenshot below it seems that Mimikatz returned an error saying Handle on memory. Seeing this error message makes me think that our current Grunt does not in fact have permissions to execute Mimikatz.  

For demonstration purposes I have opened an elevated command prompt and re-run the Grunt PowerShell Launcher.  This provides an additional Grunt that can elevate to System level privileges.  This can be seen in the screenshot below.  Using the Interact command line like interface I have executed the GetSystem Task which will grant System level privileges to the new Grunt.  

Staying under the Interact tab on the new higher permission Grunt we execute the Mimikatz Task again and see that it does in fact return a memory dump containing passwords and hashes stored in memory.  

After running the above Task navigating back to the GruntTasking page allows us to see the output even if the command line interface is cleared or lost for some reason.  

Using the Mimikatz Task we can demonstrate another feature of Covenant which is that of the Data tab shown on the left hand navigation bar.  The Data tab will contain all important information collected from the Grunt Tasks that have been executed. The screenshot below demonstrates the hashed password for the username admin.  

The Data tab also has other information stored within it such as files that might have been downloaded from the remote machine.  To demonstrate this functionality I have created a text file on the user's desktop titled passwords.txt that just so happens to contain the current user's username and password.  Using the Task Download we can specify the full path to the file and launch the task, shown in the screenshot below.  

The output of the GruntTasking for the Download Task returns an output of a base64 string.  This is because Covenant first base64 encodes the file before sending back across the network.  This helps prevent mangling during transmission as well as chunking potentially.

Moving back into our Data section and the Downloads tab, we can see the file that was downloaded to the C2 infrastructure.  From here you can download the file to your local machine using the web UI.  

Another feature of Covenant that is interesting is the Graph page located in the left navigation bar.  Which shows the current status of the C2 infrastructure as well as what Grunts are remoting back.  This can be helpful if you start spawning Grunts from already existing Grunts.  The middle Grunt can act as a proxy for further reaching devices that might be in a more protected network or that might be duel-homed to different resources.  

Covenant includes features that help facilitate team management of the C2.  You can have multiple users interacting and viewing the C2 and the Grunts associated with it.  Also the users, have varying levels of permissions and should be created for each operator to enable collaboration so that the actions taken can be tracked.  The following are how the permissions work with Covenant C2: Administrator, has the privileges to conduct user management such as creating, editing, and deleting Covenant users.  Administrator users can perform any action on Grunts or within the C2 infrastructure.  The next permission level is the User, which has the ability to conduct operations on active Grunts.  The final permission level is the Listener, which is only used for as you guessed it Listeners.  This account type is mainly meant for utilizing the Covenant API, but it is not needed for operators.  

Finally, navigating back to our Dashboard as mentioned before, has been filled with relevant information during our blog post demonstration.  As you can see both of our Grunts are shown here as well what Listeners are currently running.  

This concludes the bulk of this blog post on interacting and utilizing Covenant C2.  I hope that on your next Internal pentest or redteam operation you can utilize this blog post to quickly get Covenant up and running and shelling machines!  Until next time, keep on Grunting!

Sources and Inspiration

Ryan Villarreal

About Ryan Villarreal