Offensive

Extending The Covenant: Part 2

Further recent changes that we implemented in Covenant, such as new tasks, modified old tasks and a new graph layout.


This is the third part of a series of blog posts about our recent work with the open source C2 framework Covenant.

Again, three new tasks are added for the community to import and use in their own Covenant instances. Be aware that two of them require administrative privileges on the compromised system. I would also like to shoutout another GitHub repo containing a lot of helpful Covenant tasks that I have only recently stumbled upon. Additionally, the ProcessList task was modified to perform a grep-like matching in the output. The most notable addition however is the new graph layout, switching from Covenant's default layout to a hierarchical one with additional information.

Let's start with the new tasks and what they can be used for during a red team engagement. Sometimes, while looking to find a solution for a problem, you stumble upon a gold mine. That is how I felt when I found malcomvetter's GitHub, containing several small C# snippets that focus on one specific task during post-exploitation. Implementing these snippets into new tasks was quick and easy, which is another reason why I like Covenant. The links to the respective snippets are in the help texts of the tasks.

All the new tasks can be found on our GitHub page: Covenant-Additions

ProcessWatcher

This task requires administrative privileges. It uses WMI to continuously track new process creation events and returns the PID, name, owner and command line arguments for the process (if possible, WMI is too slow at times). This is meant to be used when a system has been compromised to the point that the operator has administrative privileges and is waiting to dump another user's Kerberos ticket or credentials, or to inject into their process etc.

ProcessWatcher_example

While it is possible to have ProcessWatcher run indefinitely by issuing the command "ProcessWatcher 0", it is not advised, as there is a bug in Covenant that when a task is killed, the output pipe seems to be closed for the Grunt. This means that all the tasks that are then run cannot return any output. This is also the case for the KeyLogger and SecurityWatcher tasks and potentially for others too.

ProcessWatcher_help

SecurityWatcher

This task requires administrative privileges. It subscribes to the Windows Event Log to track new Security events and returns information about them to the operator. This includes but is not limited to logon, logoff and privilege use. This is again meant to be used when an operator already has admin privileges on a system and wants to keep an eye out for any interesting security events on the machine. It also helps in tracking down what might have led to a disconnection of the implant.

SecurityWatcher_example

There are a lot of events collected in a short time. In the example above, a notepad process was started with administrative privileges and then the user logged off.

SecurityWatcher_help

ListOpenWindows

Enumerate through all running processes and return the title of the main window. A few processes will not be shown since the main window is hidden (such as explorer.exe), but overall this task allows a quick overview of what the user currently is working on. Since Covenant already offers a screenshot capability, this task is best combined with that to get the window titles and then try to see any interesting information with a screenshot.

ListOpenWindows_example

In the example above, a suspicious text file can be seen. If however the notepad window is in the background, a screenshot would not help me. With the file name from the window title, I would now know what to look for. It's an easy example of course, however sadly not an unlikely one.

ListOpenWindows_help

Let's now look at the modifications done to ProcessList.

ProcessList

Get a list of currently running processes. This task is not a new task, it is already in Covenant. However, we wanted to filter the output to only include a search string like with grep. So now, it is possible to append a search term as a parameter to the command (Covenant allows aliases, "ps" can be used instead of ProcessList). If the parameter is left empty, the default output (all running processes) will be returned.

ProcessList_example_1

All strings of the output can be matched, so it is also possible to filter for specific PIDs, PPIDs, owners, paths etc. The following screenshot takes the PPID of the previously identified notepad process to search for the parent process and its other child processes.

ProcessList_example_2

These were the new and modified tasks. As written at the start, another cool new feature is the new graph layout.

Hierarchical Graph

I wanted to change the graph's layout for some time now since I wanted to see a bit more clearly how my Grunts and listeners are connected. Covenant uses d3's forceSimulation, which seemed overkill to me. I switched it to d3 tree and a simpler node array, where every node just needs a parent node (except the root node called "Covenant", this was included to allow multiple listeners in the tree), an ID and a fill color.

inactive_grunts

The image above shows what my graph currently looks like. The coloring of the nodes is still the same, but the more greyed out ones are Grunts that have a different status than "Active". Grunts that are set to be hidden are not shown in the graph anymore, neither are deleted ones. This can keep the graph somewhat compact.

new_graph_2

Additionally, I wanted to see more information about the Grunt in the details. To allow more information to be shown without requiring a lot of space, I switched from the usual text boxes to pre formatted text.

It is not possible anymore to drag nodes around, they are automatically sorted and should always be in the same spot.

If you want to use the graph with a light theme, line 278 in master.js can be edited to:
halo = "#222", // color of label halo

When adding these changes to an existing Covenant installation, be sure to run dotnet clean before the next dotnet run so the changes to the .razor file are applied.

Further improvements could include panning and zooming and even more helpful information about the objects in the graph. I'm also thinking about how to modify the Dashboard tab to suit our needs a bit better, and a smaller version of the graph could very well be a part of that initial page.

A pull request has been opened on Covenant's GitHub page that proposes these changes to be implemented into the dev branch.

That's it for the moment! Thanks for reading and as always, feel free to send any questions or remarks to research@avantguard.io or contact me in the BloodHoundGang slack (user @jannlemm0913).

Similar posts