Embedding external DLLs into Covenant Tasks

Søren Fritzbøger
CSIS TechBlog
Published in
4 min readJan 14, 2020

--

BloodHound meets Covenant

During a recent Red Team assignment, we decided to try out Covenant as our command and control server. Covenant is an open-source C2 server written in .NET. During this assignment we saw a need for deploying the BloodHound collector, SharpHound, to collect information about the Active Directory environment. Unfortunately, Covenant does not implement SharpHound by default, and we therefore decided to implement it as a Covenant Task ourselves.

What is Covenant?

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.

A good overview of how Covenant works and what it does can be found in the original blogpost by the creator Ryan Cobb, Entering a Covenant: .NET Command and Control, and the post about Covenant v0.3 which introduced a new (and awesome) web UI, Covenant: The Usability Update.

In Covenant, every action you run on an infected machine is called a Task, and the shell itself is called a Grunt. A Task is compiled on the C2 server, and the compiled IL(.NET Intermediate Langue) code is then sent to a Grunt for execution. Existing Tasks mostly consist of the excellent .NET SharpSploit project, but a good thing about Covenant is that it is easily extensible, so that you can import your own project sources, compile them on the fly and send them off to a Grunt for execution. In this post, I'll explain how we implemented SharpHound and how to embed external DLLs into Covenant Tasks

The issue with SharpHound and Covenant

By default Covenant has support for three different types of external resources, ReferenceSourceLibraries, EmbeddedResources and AssemblyReferences. Tasks then depend on one/more of these resources, and the resources are then embedded into the compiled Task. This is all well, except for when you want to load external DLLs such as Nuget Packages. If we examine the code that builds Covenant Tasks, we can see that ReferenceSourceLibraries are implemented as MetaDataReference, meaning that the DLL itself is not embedded into the compiled IL code, only the metadata about the DLL. This works very well for standard .NET DLLs, but when we try to run a Task that depends on an external DLL such as Newtonsoft.Json, it fails with an exception similar to this one:

Could not load file or assembly 'Newtonsoft.Json, Version=11.0.0.0,  Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its  dependencies. The system cannot find the file specified.

This was the exact issue we faced when trying to add a SharpHound Task that depends on a number of external DLLs such as Newtonsoft.Json.

So what do you do?

Well, first you ask for advice in the official #covenant channel on the BloodHound Slack Workspace, where Ryan Cobb and many others are very active and helpful! Unfortunately, this was not an issue others had dealt with before, so we had to come up with our own solution, which we hope will help others.

Solution

The way we solved this issue is by adding the DLL as an EmbeddedResource directly to the compiled Task. For SharpHoundthe following EmbeddedResources are needed. We need to add the EmbeddedResource straight to a task. As it is right now, they cannot be added to ReferenceSourceLibrary, so if you had multiple Tasks that needed the same DLLs you have to repeat the code below.

Now we have a Task with all the necessary third party DLLs embedded, such that they are transferred with the Task to the Grunt when executed. The next step is treating the individual EmbeddedResources as Assemblies that our .NET code can use. This method of embedding DLLs as resources, is something that Jeffrey Richter explained very well and his solution is perfect for this issue. If we implement this technique to load the assemblies at runtime, we end up with the following Task for running SharpHound from a Grunt

Conflicts between Covenant and SharpHound

In the testing phase we discovered the two following issues:

  1. SharpZibLib gets a wrong constant for DefaultCodePage
  2. The class name Task in GruntTasks conflicts with System.Threading.Tasks.Task

To solve the first one we, as you can see in the Task code, set the Code Page constant to 850, which seems to work fine during tests. The other issue is a bit bigger, as it required us to rename all instances of Task to System.Threading.Tasks.Task in the SharpHound source code. The issue here is that Covenant tasks have to use the classname Task. This could most likely also be solved by using a custom AppDomain, but we did not test this.

And that concludes this short article on how to embed external DLLs into your Tasks. We did not open a pull request to the Covenant git repository, as the Task requires modification to the source code of SharpHound, but an implementation can be seen in the following commit from our fork (The commit also includes an UAC bypass Task if you're interested in that sort of thing). If you have any questions you can contact the author on Twitter @fritzboger or ask away on the #covenant BloodHound Slack channel!

References

--

--