mirror of
https://github.com/maxDcb/C2Core
synced 2026-06-08 15:48:01 +00:00
108a370807
* CommandSpecs * CommandSpecs * feat(command-specs): add simple module specs * listModule * AssemblyExec * AssemblyExecTests * Minor * Inject * InjectTests * Spec modules * Folder layout * upload & download * Chisel Minidump Powershell & Script * CoffLoader DotnetExec /KerberosUseTicket PsExec PwSh ScreenShot * add artifact_filters * Minor * stabilisation * Fixes * manual test * manual test * manual test * manual test * manual test * manual test * manual test * manual test * ScreenShot png * socks5 hostname * Maj for AI * minor
212 lines
8.4 KiB
C++
212 lines
8.4 KiB
C++
#include "../Beacon.hpp"
|
|
#include "../../modules/ModuleCmd/CommonCommand.hpp"
|
|
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
class BeaconTestProxy : public Beacon {
|
|
public:
|
|
using Beacon::initConfig;
|
|
using Beacon::cmdToTasks;
|
|
using Beacon::taskResultsToCmd;
|
|
using Beacon::execInstruction;
|
|
using Beacon::runTasks;
|
|
#if defined(C2CORE_BUILD_TESTS) || defined(C2CORE_BUILD_FUNCTIONAL_TESTS)
|
|
using Beacon::addTestListener;
|
|
#endif
|
|
|
|
void checkIn() override {}
|
|
|
|
void pushResult(const C2Message& msg) { m_taskResult.push(msg); }
|
|
C2Message popResult()
|
|
{
|
|
C2Message msg = m_taskResult.front();
|
|
m_taskResult.pop();
|
|
return msg;
|
|
}
|
|
size_t resultCount() const { return m_taskResult.size(); }
|
|
size_t taskCount() const { return m_tasks.size(); }
|
|
const std::string& arch() const { return m_arch; }
|
|
};
|
|
|
|
class FakeListener : public Listener {
|
|
public:
|
|
FakeListener()
|
|
: Listener("0.0.0.0", "4444", ListenerTcpType)
|
|
{
|
|
m_listenerHash = "child-listener";
|
|
m_metadata = R"({"1":"tcp","2":"0.0.0.0","3":"4444"})";
|
|
}
|
|
};
|
|
|
|
namespace {
|
|
const std::string kConfig = R"({"xorKey":"key","ModulesConfig":{}})";
|
|
|
|
bool expect(bool condition, const std::string& message)
|
|
{
|
|
if (!condition)
|
|
{
|
|
std::cerr << "[FAIL] " << message << std::endl;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string buildProcessArch()
|
|
{
|
|
#if defined(_M_IX86) || defined(__i386__)
|
|
return "x86";
|
|
#elif defined(_M_X64) || defined(__x86_64__)
|
|
return "x64";
|
|
#elif defined(_M_ARM64) || defined(__aarch64__)
|
|
return "arm64";
|
|
#elif defined(_M_ARM) || defined(__arm__)
|
|
return "arm";
|
|
#else
|
|
return "unknown";
|
|
#endif
|
|
}
|
|
}
|
|
|
|
int main()
|
|
{
|
|
bool ok = true;
|
|
|
|
{
|
|
BeaconTestProxy b;
|
|
ok &= expect(b.initConfig(kConfig), "initConfig should parse xor key");
|
|
ok &= expect(b.arch() == buildProcessArch(), "beacon arch should report process architecture");
|
|
}
|
|
{
|
|
BeaconTestProxy b;
|
|
ok &= expect(!b.initConfig("not json"), "initConfig should reject malformed JSON");
|
|
ok &= expect(!b.initConfig(R"({"xorKey":"k"})"), "initConfig should reject incomplete config");
|
|
}
|
|
{
|
|
BeaconTestProxy b;
|
|
ok &= expect(b.initConfig(kConfig), "initConfig should accept base config");
|
|
ok &= expect(b.cmdToTasks("not_base64"), "cmdToTasks should tolerate malformed input");
|
|
ok &= expect(b.taskCount() == 0, "cmdToTasks should not enqueue malformed tasks");
|
|
}
|
|
{
|
|
BeaconTestProxy b;
|
|
ok &= expect(b.initConfig(kConfig), "initConfig should accept base config for results");
|
|
C2Message msg;
|
|
msg.set_instruction("TEST");
|
|
msg.set_returnvalue("OK");
|
|
b.pushResult(msg);
|
|
std::string out;
|
|
ok &= expect(b.taskResultsToCmd(out), "taskResultsToCmd should serialize queued results");
|
|
ok &= expect(!out.empty(), "serialized task results should not be empty");
|
|
ok &= expect(b.resultCount() == 0, "taskResultsToCmd should clear result queue");
|
|
}
|
|
#if defined(C2CORE_BUILD_TESTS) || defined(C2CORE_BUILD_FUNCTIONAL_TESTS)
|
|
{
|
|
BeaconTestProxy b;
|
|
b.addTestListener(std::make_unique<FakeListener>());
|
|
|
|
ok &= expect(!b.runTasks(), "listener poll should keep beacon running");
|
|
ok &= expect(b.resultCount() == 1, "listener poll should queue one proof of life");
|
|
|
|
C2Message poll = b.popResult();
|
|
ok &= expect(poll.instruction() == ListenerPollCmd, "listener poll instruction mismatch");
|
|
ok &= expect(poll.data() == R"({"1":"tcp","2":"0.0.0.0","3":"4444"})", "listener poll should carry metadata in data");
|
|
ok &= expect(poll.returnvalue() == "child-listener", "listener poll should carry listener hash in return value");
|
|
}
|
|
#endif
|
|
{
|
|
BeaconTestProxy b;
|
|
C2Message sleepMsg;
|
|
sleepMsg.set_instruction(SleepCmd);
|
|
sleepMsg.set_cmd("2");
|
|
C2Message sleepRet;
|
|
ok &= expect(!b.execInstruction(sleepMsg, sleepRet), "sleep command should keep beacon running");
|
|
ok &= expect(sleepRet.returnvalue() == "2000ms", "sleep command should convert seconds to ms");
|
|
|
|
C2Message zeroSleep;
|
|
zeroSleep.set_instruction(SleepCmd);
|
|
zeroSleep.set_cmd("0");
|
|
C2Message zeroRet;
|
|
ok &= expect(!b.execInstruction(zeroSleep, zeroRet), "zero sleep should keep beacon running");
|
|
ok &= expect(zeroRet.returnvalue() == "0ms", "zero sleep should be accepted");
|
|
|
|
C2Message badSleep;
|
|
badSleep.set_instruction(SleepCmd);
|
|
badSleep.set_cmd("abc");
|
|
C2Message badRet;
|
|
ok &= expect(!b.execInstruction(badSleep, badRet), "bad sleep should keep beacon running");
|
|
ok &= expect(badRet.returnvalue() == CmdStatusFail, "bad sleep should fail cleanly");
|
|
|
|
C2Message partialSleep;
|
|
partialSleep.set_instruction(SleepCmd);
|
|
partialSleep.set_cmd("1abc");
|
|
C2Message partialRet;
|
|
ok &= expect(!b.execInstruction(partialSleep, partialRet), "partial sleep should keep beacon running");
|
|
ok &= expect(partialRet.returnvalue() == CmdStatusFail, "partial sleep should fail cleanly");
|
|
|
|
C2Message negativeSleep;
|
|
negativeSleep.set_instruction(SleepCmd);
|
|
negativeSleep.set_cmd("-1");
|
|
C2Message negativeRet;
|
|
ok &= expect(!b.execInstruction(negativeSleep, negativeRet), "negative sleep should keep beacon running");
|
|
ok &= expect(negativeRet.returnvalue() == CmdStatusFail, "negative sleep should fail cleanly");
|
|
|
|
C2Message endMsg;
|
|
endMsg.set_instruction(EndCmd);
|
|
C2Message endRet;
|
|
ok &= expect(b.execInstruction(endMsg, endRet), "end command should stop beacon");
|
|
ok &= expect(endRet.returnvalue() == CmdStatusSuccess, "end command should return success");
|
|
|
|
C2Message badListenerPort;
|
|
badListenerPort.set_instruction(ListenerCmd);
|
|
badListenerPort.set_cmd(StartCmd + " " + ListenerTcpType + " 0.0.0.0 notaport");
|
|
C2Message badListenerRet;
|
|
ok &= expect(!b.execInstruction(badListenerPort, badListenerRet), "bad listener port should keep beacon running");
|
|
ok &= expect(badListenerRet.errorCode() == ERROR_PORT_FORMAT, "bad listener port should be rejected");
|
|
|
|
C2Message zeroListenerPort;
|
|
zeroListenerPort.set_instruction(ListenerCmd);
|
|
zeroListenerPort.set_cmd(StartCmd + " " + ListenerTcpType + " 0.0.0.0 0");
|
|
C2Message zeroListenerRet;
|
|
ok &= expect(!b.execInstruction(zeroListenerPort, zeroListenerRet), "zero listener port should keep beacon running");
|
|
ok &= expect(zeroListenerRet.errorCode() == ERROR_PORT_FORMAT, "zero listener port should be rejected");
|
|
}
|
|
{
|
|
BeaconTestProxy b;
|
|
C2Message hostSocksInit;
|
|
hostSocksInit.set_instruction(Socks5Cmd);
|
|
hostSocksInit.set_cmd(InitCmd);
|
|
hostSocksInit.set_data("host:");
|
|
hostSocksInit.set_args("80");
|
|
hostSocksInit.set_pid(7);
|
|
C2Message hostSocksRet;
|
|
|
|
ok &= expect(!b.execInstruction(hostSocksInit, hostSocksRet), "hostname socks init failure should keep beacon running");
|
|
ok &= expect(hostSocksRet.instruction() == Socks5Cmd, "hostname socks init should preserve instruction");
|
|
ok &= expect(hostSocksRet.cmd() == InitCmd, "hostname socks init should preserve command");
|
|
ok &= expect(hostSocksRet.pid() == 7, "hostname socks init should preserve tunnel id");
|
|
ok &= expect(hostSocksRet.data() == "fail:connect", "empty hostname should fail cleanly");
|
|
|
|
C2Message invalidSocksInit;
|
|
invalidSocksInit.set_instruction(Socks5Cmd);
|
|
invalidSocksInit.set_cmd(InitCmd);
|
|
invalidSocksInit.set_data("not-a-number");
|
|
invalidSocksInit.set_args("80");
|
|
C2Message invalidSocksRet;
|
|
|
|
ok &= expect(!b.execInstruction(invalidSocksInit, invalidSocksRet), "invalid socks init failure should keep beacon running");
|
|
ok &= expect(invalidSocksRet.data() == "fail:invalid_destination", "invalid socks destination should fail cleanly");
|
|
}
|
|
{
|
|
BeaconTestProxy b;
|
|
C2Message msg;
|
|
msg.set_instruction("UNKNOWN");
|
|
C2Message ret;
|
|
ok &= expect(!b.execInstruction(msg, ret), "unknown module should keep beacon running");
|
|
ok &= expect(ret.returnvalue() == CmdModuleNotFound, "unknown module should report module not found");
|
|
}
|
|
|
|
return ok ? 0 : 1;
|
|
}
|