From e195ebcffd02eb36c6a56ede6d2f5e459c892d62 Mon Sep 17 00:00:00 2001 From: Maxime dcb <40819564+maxDcb@users.noreply.github.com> Date: Thu, 31 Jul 2025 12:52:42 +0200 Subject: [PATCH] Refactor system info modules to use native APIs --- modules/CMakeLists.txt | 5 + modules/EnumerateShares/CMakeLists.txt | 18 ++ modules/EnumerateShares/EnumerateShares.cpp | 140 +++++++++++++++ modules/EnumerateShares/EnumerateShares.hpp | 28 +++ .../tests/testsEnumerateShares.cpp | 25 +++ modules/GetEnv/CMakeLists.txt | 14 ++ modules/GetEnv/GetEnv.cpp | 89 ++++++++++ modules/GetEnv/GetEnv.hpp | 28 +++ modules/GetEnv/tests/testsGetEnv.cpp | 26 +++ modules/IpConfig/CMakeLists.txt | 14 ++ modules/IpConfig/IpConfig.cpp | 135 ++++++++++++++ modules/IpConfig/IpConfig.hpp | 28 +++ modules/IpConfig/tests/testsIpConfig.cpp | 25 +++ modules/Netstat/CMakeLists.txt | 14 ++ modules/Netstat/Netstat.cpp | 150 ++++++++++++++++ modules/Netstat/Netstat.hpp | 28 +++ modules/Netstat/tests/testsNetstat.cpp | 25 +++ modules/Whoami/CMakeLists.txt | 14 ++ modules/Whoami/Whoami.cpp | 167 ++++++++++++++++++ modules/Whoami/Whoami.hpp | 28 +++ modules/Whoami/tests/testsWhoami.cpp | 25 +++ 21 files changed, 1026 insertions(+) create mode 100644 modules/EnumerateShares/CMakeLists.txt create mode 100644 modules/EnumerateShares/EnumerateShares.cpp create mode 100644 modules/EnumerateShares/EnumerateShares.hpp create mode 100644 modules/EnumerateShares/tests/testsEnumerateShares.cpp create mode 100644 modules/GetEnv/CMakeLists.txt create mode 100644 modules/GetEnv/GetEnv.cpp create mode 100644 modules/GetEnv/GetEnv.hpp create mode 100644 modules/GetEnv/tests/testsGetEnv.cpp create mode 100644 modules/IpConfig/CMakeLists.txt create mode 100644 modules/IpConfig/IpConfig.cpp create mode 100644 modules/IpConfig/IpConfig.hpp create mode 100644 modules/IpConfig/tests/testsIpConfig.cpp create mode 100644 modules/Netstat/CMakeLists.txt create mode 100644 modules/Netstat/Netstat.cpp create mode 100644 modules/Netstat/Netstat.hpp create mode 100644 modules/Netstat/tests/testsNetstat.cpp create mode 100644 modules/Whoami/CMakeLists.txt create mode 100644 modules/Whoami/Whoami.cpp create mode 100644 modules/Whoami/Whoami.hpp create mode 100644 modules/Whoami/tests/testsWhoami.cpp diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index 9a51a0f..7b9bfc9 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -41,3 +41,8 @@ add_subdirectory(DotnetExec) add_subdirectory(PwSh) add_subdirectory(Shell) +add_subdirectory(GetEnv) +add_subdirectory(Whoami) +add_subdirectory(Netstat) +add_subdirectory(IpConfig) +add_subdirectory(EnumerateShares) diff --git a/modules/EnumerateShares/CMakeLists.txt b/modules/EnumerateShares/CMakeLists.txt new file mode 100644 index 0000000..7be922f --- /dev/null +++ b/modules/EnumerateShares/CMakeLists.txt @@ -0,0 +1,18 @@ +include_directories(../) +add_library(EnumerateShares SHARED EnumerateShares.cpp) +set_property(TARGET EnumerateShares PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded") +if(UNIX) + target_link_libraries(EnumerateShares smbclient) +endif() +add_custom_command(TARGET EnumerateShares POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy + $ "${CMAKE_SOURCE_DIR}/Release/Modules/$") + +if(WITH_TESTS) + add_executable(testsEnumerateShares tests/testsEnumerateShares.cpp EnumerateShares.cpp) + if(UNIX) + target_link_libraries(testsEnumerateShares smbclient) + endif() + add_custom_command(TARGET testsEnumerateShares POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy + $ "${CMAKE_SOURCE_DIR}/Tests/$") + add_test(NAME testsEnumerateShares COMMAND "${CMAKE_SOURCE_DIR}/Tests/$") +endif() diff --git a/modules/EnumerateShares/EnumerateShares.cpp b/modules/EnumerateShares/EnumerateShares.cpp new file mode 100644 index 0000000..6f7097e --- /dev/null +++ b/modules/EnumerateShares/EnumerateShares.cpp @@ -0,0 +1,140 @@ +#include "EnumerateShares.hpp" +#include "Common.hpp" + +#include +#ifdef _WIN32 +#include +#include +#pragma comment(lib, "netapi32.lib") +#else +#include +#endif + +using namespace std; + +constexpr std::string_view moduleName = "enumerateShares"; +constexpr unsigned long long moduleHash = djb2(moduleName); + +#ifdef _WIN32 +__declspec(dllexport) EnumerateShares* EnumerateSharesConstructor() +{ + return new EnumerateShares(); +} +#else +__attribute__((visibility("default"))) EnumerateShares* EnumerateSharesConstructor() +{ + return new EnumerateShares(); +} +#endif + +EnumerateShares::EnumerateShares() +#ifdef BUILD_TEAMSERVER + : ModuleCmd(std::string(moduleName), moduleHash) +#else + : ModuleCmd("", moduleHash) +#endif +{ +} + +EnumerateShares::~EnumerateShares() +{ +} + +std::string EnumerateShares::getInfo() +{ + std::string info; +#ifdef BUILD_TEAMSERVER + info += "enumerateShares:\n"; + info += "List available SMB shares.\n"; +#endif + return info; +} + +int EnumerateShares::init(std::vector& splitedCmd, C2Message& c2Message) +{ + std::string host; + if(splitedCmd.size() > 1) + host = splitedCmd[1]; + c2Message.set_instruction(splitedCmd[0]); + c2Message.set_cmd(host); + return 0; +} + +int EnumerateShares::process(C2Message& c2Message, C2Message& c2RetMessage) +{ + std::string host = c2Message.cmd(); + std::string out = runEnum(host); + c2RetMessage.set_instruction(c2Message.instruction()); + c2RetMessage.set_cmd(host); + c2RetMessage.set_returnvalue(out); + return 0; +} + +std::string EnumerateShares::runEnum(const std::string& host) +{ +#ifdef _WIN32 + std::string result; + std::wstring wserver; + if(!host.empty()) + wserver = L"\\\\" + std::wstring(host.begin(), host.end()); + LPBYTE buf = nullptr; + DWORD read = 0, total = 0, resume = 0; + NET_API_STATUS status = NetShareEnum(host.empty()? NULL : (LPWSTR)wserver.c_str(), 1, &buf, MAX_PREFERRED_LENGTH, &read, &total, &resume); + if(status == NERR_Success || status == ERROR_MORE_DATA) + { + PSHARE_INFO_1 info = (PSHARE_INFO_1)buf; + for(DWORD i=0; i0) u[0]='\0'; if(plen>0) p[0]='\0'; }; + SMBCCTX* ctx = smbc_new_context(); + if(!ctx) return result; + smbc_setOptionUseKerberos(ctx, 0); + smbc_setOptionFallbackAfterKerberos(ctx, 1); + smbc_setFunctionAuthDataWithContext(ctx, auth_fn); + if(!smbc_init_context(ctx)) + { + smbc_free_context(ctx, 1); + return result; + } + smbc_set_context(ctx); + std::string url = "smb://" + (host.empty()? std::string("") : host); + int dir = smbc_opendir(url.c_str()); + if(dir >= 0) + { + struct smbc_dirent* ent; + while((ent = smbc_readdir(dir)) != nullptr) + { + if(ent->smbc_type == SMBC_FILE_SHARE) + { + result += ent->name; + result += '\n'; + } + } + smbc_closedir(dir); + } + smbc_free_context(ctx, 1); + if(result.empty()) + result = "Enumeration failed or no shares"; + return result; +#endif +} + diff --git a/modules/EnumerateShares/EnumerateShares.hpp b/modules/EnumerateShares/EnumerateShares.hpp new file mode 100644 index 0000000..72bdb41 --- /dev/null +++ b/modules/EnumerateShares/EnumerateShares.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "ModuleCmd.hpp" + +class EnumerateShares : public ModuleCmd +{ +public: + EnumerateShares(); + ~EnumerateShares(); + + std::string getInfo(); + + int init(std::vector& splitedCmd, C2Message& c2Message); + int process(C2Message& c2Message, C2Message& c2RetMessage); + int osCompatibility() + { + return OS_LINUX | OS_WINDOWS; + } + +private: + std::string runEnum(const std::string& host); +}; + +#ifdef _WIN32 +extern "C" __declspec(dllexport) EnumerateShares* EnumerateSharesConstructor(); +#else +extern "C" __attribute__((visibility("default"))) EnumerateShares* EnumerateSharesConstructor(); +#endif diff --git a/modules/EnumerateShares/tests/testsEnumerateShares.cpp b/modules/EnumerateShares/tests/testsEnumerateShares.cpp new file mode 100644 index 0000000..e52e298 --- /dev/null +++ b/modules/EnumerateShares/tests/testsEnumerateShares.cpp @@ -0,0 +1,25 @@ +#include "../EnumerateShares.hpp" + +bool testEnumerateShares(); + +int main() +{ + bool res; + std::cout << "[+] testEnumerateShares" << std::endl; + res = testEnumerateShares(); + if (res) + std::cout << "[+] Sucess" << std::endl; + else + std::cout << "[-] Failed" << std::endl; + return !res; +} + +bool testEnumerateShares() +{ + std::unique_ptr mod = std::make_unique(); + std::vector cmd = {"enumerateShares"}; + C2Message msg, ret; + mod->init(cmd, msg); + mod->process(msg, ret); + return !ret.returnvalue().empty(); +} diff --git a/modules/GetEnv/CMakeLists.txt b/modules/GetEnv/CMakeLists.txt new file mode 100644 index 0000000..cfa2fb5 --- /dev/null +++ b/modules/GetEnv/CMakeLists.txt @@ -0,0 +1,14 @@ +include_directories(../) +add_library(GetEnv SHARED GetEnv.cpp) +set_property(TARGET GetEnv PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded") +target_link_libraries(GetEnv ) +add_custom_command(TARGET GetEnv POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy + $ "${CMAKE_SOURCE_DIR}/Release/Modules/$") + +if(WITH_TESTS) + add_executable(testsGetEnv tests/testsGetEnv.cpp GetEnv.cpp) + target_link_libraries(testsGetEnv ) + add_custom_command(TARGET testsGetEnv POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy + $ "${CMAKE_SOURCE_DIR}/Tests/$") + add_test(NAME testsGetEnv COMMAND "${CMAKE_SOURCE_DIR}/Tests/$") +endif() diff --git a/modules/GetEnv/GetEnv.cpp b/modules/GetEnv/GetEnv.cpp new file mode 100644 index 0000000..388584b --- /dev/null +++ b/modules/GetEnv/GetEnv.cpp @@ -0,0 +1,89 @@ +#include "GetEnv.hpp" +#include "Common.hpp" + +#include +#ifdef _WIN32 +#include +#else +extern char **environ; +#endif + +using namespace std; + +constexpr std::string_view moduleName = "getEnv"; +constexpr unsigned long long moduleHash = djb2(moduleName); + +#ifdef _WIN32 +__declspec(dllexport) GetEnv* GetEnvConstructor() +{ + return new GetEnv(); +} +#else +__attribute__((visibility("default"))) GetEnv* GetEnvConstructor() +{ + return new GetEnv(); +} +#endif + +GetEnv::GetEnv() +#ifdef BUILD_TEAMSERVER + : ModuleCmd(std::string(moduleName), moduleHash) +#else + : ModuleCmd("", moduleHash) +#endif +{ +} + +GetEnv::~GetEnv() +{ +} + +std::string GetEnv::getInfo() +{ + std::string info; +#ifdef BUILD_TEAMSERVER + info += "getEnv:\n"; + info += "List environment variables.\n"; +#endif + return info; +} + +int GetEnv::init(std::vector& splitedCmd, C2Message& c2Message) +{ + c2Message.set_instruction(splitedCmd[0]); + return 0; +} + +int GetEnv::process(C2Message& c2Message, C2Message& c2RetMessage) +{ + std::string out = listEnv(); + c2RetMessage.set_instruction(c2Message.instruction()); + c2RetMessage.set_returnvalue(out); + return 0; +} + +std::string GetEnv::listEnv() +{ + std::string result; +#ifdef _WIN32 + LPCH env = GetEnvironmentStringsA(); + if(!env) + return "Could not retrieve environment"; + for(LPCH var = env; *var; var += strlen(var) + 1) + { + result += var; + result += "\n"; + } + FreeEnvironmentStringsA(env); +#else + if(!environ) + return result; + for(char **p = environ; *p; ++p) + { + result += *p; + result += "\n"; + } +#endif + return result; +} + diff --git a/modules/GetEnv/GetEnv.hpp b/modules/GetEnv/GetEnv.hpp new file mode 100644 index 0000000..5d12c7d --- /dev/null +++ b/modules/GetEnv/GetEnv.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "ModuleCmd.hpp" + +class GetEnv : public ModuleCmd +{ +public: + GetEnv(); + ~GetEnv(); + + std::string getInfo(); + + int init(std::vector& splitedCmd, C2Message& c2Message); + int process(C2Message& c2Message, C2Message& c2RetMessage); + int osCompatibility() + { + return OS_LINUX | OS_WINDOWS; + } + +private: + std::string listEnv(); +}; + +#ifdef _WIN32 +extern "C" __declspec(dllexport) GetEnv* GetEnvConstructor(); +#else +extern "C" __attribute__((visibility("default"))) GetEnv* GetEnvConstructor(); +#endif diff --git a/modules/GetEnv/tests/testsGetEnv.cpp b/modules/GetEnv/tests/testsGetEnv.cpp new file mode 100644 index 0000000..c55f3cd --- /dev/null +++ b/modules/GetEnv/tests/testsGetEnv.cpp @@ -0,0 +1,26 @@ +#include "../GetEnv.hpp" + +bool testGetEnv(); + +int main() +{ + bool res; + std::cout << "[+] testGetEnv" << std::endl; + res = testGetEnv(); + if (res) + std::cout << "[+] Sucess" << std::endl; + else + std::cout << "[-] Failed" << std::endl; + + return !res; +} + +bool testGetEnv() +{ + std::unique_ptr mod = std::make_unique(); + std::vector cmd = {"getEnv"}; + C2Message msg, ret; + mod->init(cmd, msg); + mod->process(msg, ret); + return !ret.returnvalue().empty(); +} diff --git a/modules/IpConfig/CMakeLists.txt b/modules/IpConfig/CMakeLists.txt new file mode 100644 index 0000000..14e2b0e --- /dev/null +++ b/modules/IpConfig/CMakeLists.txt @@ -0,0 +1,14 @@ +include_directories(../) +add_library(IpConfig SHARED IpConfig.cpp) +set_property(TARGET IpConfig PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded") +target_link_libraries(IpConfig ) +add_custom_command(TARGET IpConfig POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy + $ "${CMAKE_SOURCE_DIR}/Release/Modules/$") + +if(WITH_TESTS) + add_executable(testsIpConfig tests/testsIpConfig.cpp IpConfig.cpp) + target_link_libraries(testsIpConfig ) + add_custom_command(TARGET testsIpConfig POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy + $ "${CMAKE_SOURCE_DIR}/Tests/$") + add_test(NAME testsIpConfig COMMAND "${CMAKE_SOURCE_DIR}/Tests/$") +endif() diff --git a/modules/IpConfig/IpConfig.cpp b/modules/IpConfig/IpConfig.cpp new file mode 100644 index 0000000..8555b7b --- /dev/null +++ b/modules/IpConfig/IpConfig.cpp @@ -0,0 +1,135 @@ +#include "IpConfig.hpp" +#include "Common.hpp" + +#include +#ifdef _WIN32 +#include +#include +#include +#pragma comment(lib, "iphlpapi.lib") +#pragma comment(lib, "ws2_32.lib") +#else +#include +#include +#include +#include +#endif + +using namespace std; + +constexpr std::string_view moduleName = "ipConfig"; +constexpr unsigned long long moduleHash = djb2(moduleName); + +#ifdef _WIN32 +__declspec(dllexport) IpConfig* IpConfigConstructor() +{ + return new IpConfig(); +} +#else +__attribute__((visibility("default"))) IpConfig* IpConfigConstructor() +{ + return new IpConfig(); +} +#endif + +IpConfig::IpConfig() +#ifdef BUILD_TEAMSERVER + : ModuleCmd(std::string(moduleName), moduleHash) +#else + : ModuleCmd("", moduleHash) +#endif +{ +} + +IpConfig::~IpConfig() +{ +} + +std::string IpConfig::getInfo() +{ + std::string info; +#ifdef BUILD_TEAMSERVER + info += "ipConfig:\n"; + info += "Show local IP configuration.\n"; +#endif + return info; +} + +int IpConfig::init(std::vector& splitedCmd, C2Message& c2Message) +{ + c2Message.set_instruction(splitedCmd[0]); + return 0; +} + +int IpConfig::process(C2Message& c2Message, C2Message& c2RetMessage) +{ + std::string out = runIpconfig(); + c2RetMessage.set_instruction(c2Message.instruction()); + c2RetMessage.set_returnvalue(out); + return 0; +} + +std::string IpConfig::runIpconfig() +{ +#ifdef _WIN32 + std::string result; + ULONG size = 0; + GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, NULL, &size); + std::vector buf(size / sizeof(IP_ADAPTER_ADDRESSES) + 1); + PIP_ADAPTER_ADDRESSES addr = reinterpret_cast(buf.data()); + if(GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, addr, &size) == NO_ERROR) + { + for(auto p = addr; p; p = p->Next) + { + result += p->FriendlyName; result += "\n"; + for(PIP_ADAPTER_UNICAST_ADDRESS unicast = p->FirstUnicastAddress; unicast; unicast = unicast->Next) + { + char ip[INET6_ADDRSTRLEN]; + void* sa = &((struct sockaddr_in*)unicast->Address.lpSockaddr)->sin_addr; + int family = unicast->Address.lpSockaddr->sa_family; + if(family == AF_INET) + { + inet_ntop(AF_INET, sa, ip, sizeof(ip)); + } + else if(family == AF_INET6) + { + sa = &((struct sockaddr_in6*)unicast->Address.lpSockaddr)->sin6_addr; + inet_ntop(AF_INET6, sa, ip, sizeof(ip)); + } + else + continue; + result += " " ; + result += ip; + result += "\n"; + } + } + } + return result; +#else + std::string result; + struct ifaddrs* ifa = nullptr; + if(getifaddrs(&ifa) == 0) + { + for(auto p = ifa; p; p = p->ifa_next) + { + if(!p->ifa_addr) continue; + int family = p->ifa_addr->sa_family; + char host[INET6_ADDRSTRLEN]; + if(family == AF_INET) + { + inet_ntop(AF_INET, &((struct sockaddr_in*)p->ifa_addr)->sin_addr, host, sizeof(host)); + } + else if(family == AF_INET6) + { + inet_ntop(AF_INET6, &((struct sockaddr_in6*)p->ifa_addr)->sin6_addr, host, sizeof(host)); + } + else + continue; + result += p->ifa_name; result += " "; result += host; result += "\n"; + } + freeifaddrs(ifa); + } + return result; +#endif +} + diff --git a/modules/IpConfig/IpConfig.hpp b/modules/IpConfig/IpConfig.hpp new file mode 100644 index 0000000..c6d3780 --- /dev/null +++ b/modules/IpConfig/IpConfig.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "ModuleCmd.hpp" + +class IpConfig : public ModuleCmd +{ +public: + IpConfig(); + ~IpConfig(); + + std::string getInfo(); + + int init(std::vector& splitedCmd, C2Message& c2Message); + int process(C2Message& c2Message, C2Message& c2RetMessage); + int osCompatibility() + { + return OS_LINUX | OS_WINDOWS; + } + +private: + std::string runIpconfig(); +}; + +#ifdef _WIN32 +extern "C" __declspec(dllexport) IpConfig* IpConfigConstructor(); +#else +extern "C" __attribute__((visibility("default"))) IpConfig* IpConfigConstructor(); +#endif diff --git a/modules/IpConfig/tests/testsIpConfig.cpp b/modules/IpConfig/tests/testsIpConfig.cpp new file mode 100644 index 0000000..f36a6d6 --- /dev/null +++ b/modules/IpConfig/tests/testsIpConfig.cpp @@ -0,0 +1,25 @@ +#include "../IpConfig.hpp" + +bool testIpConfig(); + +int main() +{ + bool res; + std::cout << "[+] testIpConfig" << std::endl; + res = testIpConfig(); + if (res) + std::cout << "[+] Sucess" << std::endl; + else + std::cout << "[-] Failed" << std::endl; + return !res; +} + +bool testIpConfig() +{ + std::unique_ptr mod = std::make_unique(); + std::vector cmd = {"ipConfig"}; + C2Message msg, ret; + mod->init(cmd, msg); + mod->process(msg, ret); + return !ret.returnvalue().empty(); +} diff --git a/modules/Netstat/CMakeLists.txt b/modules/Netstat/CMakeLists.txt new file mode 100644 index 0000000..59e5420 --- /dev/null +++ b/modules/Netstat/CMakeLists.txt @@ -0,0 +1,14 @@ +include_directories(../) +add_library(Netstat SHARED Netstat.cpp) +set_property(TARGET Netstat PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded") +target_link_libraries(Netstat ) +add_custom_command(TARGET Netstat POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy + $ "${CMAKE_SOURCE_DIR}/Release/Modules/$") + +if(WITH_TESTS) + add_executable(testsNetstat tests/testsNetstat.cpp Netstat.cpp) + target_link_libraries(testsNetstat ) + add_custom_command(TARGET testsNetstat POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy + $ "${CMAKE_SOURCE_DIR}/Tests/$") + add_test(NAME testsNetstat COMMAND "${CMAKE_SOURCE_DIR}/Tests/$") +endif() diff --git a/modules/Netstat/Netstat.cpp b/modules/Netstat/Netstat.cpp new file mode 100644 index 0000000..04fa3e5 --- /dev/null +++ b/modules/Netstat/Netstat.cpp @@ -0,0 +1,150 @@ +#include "Netstat.hpp" +#include "Common.hpp" + +#include +#ifdef _WIN32 +#include +#include +#include +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#else +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +using namespace std; + +constexpr std::string_view moduleName = "netstat"; +constexpr unsigned long long moduleHash = djb2(moduleName); + +#ifdef _WIN32 +__declspec(dllexport) Netstat* NetstatConstructor() +{ + return new Netstat(); +} +#else +__attribute__((visibility("default"))) Netstat* NetstatConstructor() +{ + return new Netstat(); +} +#endif + +Netstat::Netstat() +#ifdef BUILD_TEAMSERVER + : ModuleCmd(std::string(moduleName), moduleHash) +#else + : ModuleCmd("", moduleHash) +#endif +{ +} + +Netstat::~Netstat() +{ +} + +std::string Netstat::getInfo() +{ + std::string info; +#ifdef BUILD_TEAMSERVER + info += "netstat:\n"; + info += "Show active network connections.\n"; +#endif + return info; +} + +int Netstat::init(std::vector& splitedCmd, C2Message& c2Message) +{ + c2Message.set_instruction(splitedCmd[0]); + return 0; +} + +int Netstat::process(C2Message& c2Message, C2Message& c2RetMessage) +{ + std::string out = runNetstat(); + c2RetMessage.set_instruction(c2Message.instruction()); + c2RetMessage.set_returnvalue(out); + return 0; +} + +std::string Netstat::runNetstat() +{ +#ifdef _WIN32 + std::string result; + DWORD size = 0; + GetExtendedTcpTable(nullptr, &size, TRUE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0); + std::vector buf(size); + if(GetExtendedTcpTable(buf.data(), &size, TRUE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0) == NO_ERROR) + { + auto table = reinterpret_cast(buf.data()); + for(DWORD i=0; idwNumEntries; ++i) + { + char local[INET_ADDRSTRLEN]; + char remote[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &table->table[i].dwLocalAddr, local, sizeof(local)); + inet_ntop(AF_INET, &table->table[i].dwRemoteAddr, remote, sizeof(remote)); + result += "TCP "; + result += local; result += ':'; result += std::to_string(ntohs((u_short)table->table[i].dwLocalPort)); + result += " -> "; + result += remote; result += ':'; result += std::to_string(ntohs((u_short)table->table[i].dwRemotePort)); + result += " State:"; result += std::to_string(table->table[i].dwState); + result += " PID:"; result += std::to_string(table->table[i].dwOwningPid); + result += "\n"; + } + } + return result; +#else + std::string result; + auto parse = [&](const std::string& path, const char* proto, bool ipv6) + { + std::ifstream f(path); + if(!f) return; + std::string line; + std::getline(f, line); // skip header + while(std::getline(f, line)) + { + std::istringstream iss(line); + std::string sl, local, remote, st; + iss >> sl >> local >> remote >> st; + auto decodeAddr = [&](const std::string& in)->std::string + { + size_t pos = in.find(':'); + std::string iphex = in.substr(0,pos); + std::string porthex = in.substr(pos+1); + unsigned port = std::stoul(porthex, nullptr, 16); + char buf[INET6_ADDRSTRLEN]; + if(ipv6) + { + struct in6_addr a{}; + for(int i=0;i<16;i++) + a.s6_addr[15-i] = std::stoi(iphex.substr(i*2,2),nullptr,16); + inet_ntop(AF_INET6, &a, buf, sizeof(buf)); + } + else + { + struct in_addr a{}; + a.s_addr = htonl(std::stoul(iphex, nullptr, 16)); + inet_ntop(AF_INET, &a, buf, sizeof(buf)); + } + return std::string(buf)+":"+std::to_string(port); + }; + result += proto; result += " "; + result += decodeAddr(local); result += " -> "; + result += decodeAddr(remote); result += " State:"; result += st; + result += "\n"; + } + }; + parse("/proc/net/tcp", "TCP", false); + parse("/proc/net/udp", "UDP", false); + parse("/proc/net/tcp6", "TCP6", true); + parse("/proc/net/udp6", "UDP6", true); + return result; +#endif +} + diff --git a/modules/Netstat/Netstat.hpp b/modules/Netstat/Netstat.hpp new file mode 100644 index 0000000..da510f1 --- /dev/null +++ b/modules/Netstat/Netstat.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "ModuleCmd.hpp" + +class Netstat : public ModuleCmd +{ +public: + Netstat(); + ~Netstat(); + + std::string getInfo(); + + int init(std::vector& splitedCmd, C2Message& c2Message); + int process(C2Message& c2Message, C2Message& c2RetMessage); + int osCompatibility() + { + return OS_LINUX | OS_WINDOWS; + } + +private: + std::string runNetstat(); +}; + +#ifdef _WIN32 +extern "C" __declspec(dllexport) Netstat* NetstatConstructor(); +#else +extern "C" __attribute__((visibility("default"))) Netstat* NetstatConstructor(); +#endif diff --git a/modules/Netstat/tests/testsNetstat.cpp b/modules/Netstat/tests/testsNetstat.cpp new file mode 100644 index 0000000..6b66a3e --- /dev/null +++ b/modules/Netstat/tests/testsNetstat.cpp @@ -0,0 +1,25 @@ +#include "../Netstat.hpp" + +bool testNetstat(); + +int main() +{ + bool res; + std::cout << "[+] testNetstat" << std::endl; + res = testNetstat(); + if (res) + std::cout << "[+] Sucess" << std::endl; + else + std::cout << "[-] Failed" << std::endl; + return !res; +} + +bool testNetstat() +{ + std::unique_ptr mod = std::make_unique(); + std::vector cmd = {"netstat"}; + C2Message msg, ret; + mod->init(cmd, msg); + mod->process(msg, ret); + return !ret.returnvalue().empty(); +} diff --git a/modules/Whoami/CMakeLists.txt b/modules/Whoami/CMakeLists.txt new file mode 100644 index 0000000..150089b --- /dev/null +++ b/modules/Whoami/CMakeLists.txt @@ -0,0 +1,14 @@ +include_directories(../) +add_library(Whoami SHARED Whoami.cpp) +set_property(TARGET Whoami PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded") +target_link_libraries(Whoami ) +add_custom_command(TARGET Whoami POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy + $ "${CMAKE_SOURCE_DIR}/Release/Modules/$") + +if(WITH_TESTS) + add_executable(testsWhoami tests/testsWhoami.cpp Whoami.cpp) + target_link_libraries(testsWhoami ) + add_custom_command(TARGET testsWhoami POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy + $ "${CMAKE_SOURCE_DIR}/Tests/$") + add_test(NAME testsWhoami COMMAND "${CMAKE_SOURCE_DIR}/Tests/$") +endif() diff --git a/modules/Whoami/Whoami.cpp b/modules/Whoami/Whoami.cpp new file mode 100644 index 0000000..33c722d --- /dev/null +++ b/modules/Whoami/Whoami.cpp @@ -0,0 +1,167 @@ +#include "Whoami.hpp" +#include "Common.hpp" + +#include +#include +#ifdef _WIN32 +#include +#endif + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#endif + +using namespace std; + +constexpr std::string_view moduleName = "whoami"; +constexpr unsigned long long moduleHash = djb2(moduleName); + +#ifdef _WIN32 +__declspec(dllexport) Whoami* WhoamiConstructor() +{ + return new Whoami(); +} +#else +__attribute__((visibility("default"))) Whoami* WhoamiConstructor() +{ + return new Whoami(); +} +#endif + +Whoami::Whoami() +#ifdef BUILD_TEAMSERVER + : ModuleCmd(std::string(moduleName), moduleHash) +#else + : ModuleCmd("", moduleHash) +#endif +{ +} + +Whoami::~Whoami() +{ +} + +std::string Whoami::getInfo() +{ + std::string info; +#ifdef BUILD_TEAMSERVER + info += "whoami:\n"; + info += "Print current user information.\n"; +#endif + return info; +} + +int Whoami::init(std::vector& splitedCmd, C2Message& c2Message) +{ + c2Message.set_instruction(splitedCmd[0]); + return 0; +} + +int Whoami::process(C2Message& c2Message, C2Message& c2RetMessage) +{ + std::string out = getInfoString(); + c2RetMessage.set_instruction(c2Message.instruction()); + c2RetMessage.set_returnvalue(out); + return 0; +} + +std::string Whoami::getInfoString() +{ + std::string result; +#ifdef _WIN32 + char name[256]; + DWORD size = sizeof(name); + if(GetUserNameA(name, &size)) + { + result += "User: "; + result += name; + result += "\n"; + } + + HANDLE token = NULL; + if(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) + { + DWORD len = 0; + GetTokenInformation(token, TokenGroups, nullptr, 0, &len); + std::vector buf(len); + if(GetTokenInformation(token, TokenGroups, buf.data(), len, &len)) + { + PTOKEN_GROUPS groups = reinterpret_cast(buf.data()); + result += "Groups:\n"; + for(DWORD i = 0; i < groups->GroupCount; ++i) + { + char gname[256]; + char gdomain[256]; + DWORD gnlen = sizeof(gname); + DWORD gdlen = sizeof(gdomain); + SID_NAME_USE use; + if(LookupAccountSidA(NULL, groups->Groups[i].Sid, gname, &gnlen, gdomain, &gdlen, &use)) + { + if(gdlen) + { + result += " - "; + result += gdomain; + result += "\\"; + result += gname; + } + else + { + result += " - "; + result += gname; + } + } + else + { + LPSTR sidStr = nullptr; + if(ConvertSidToStringSidA(groups->Groups[i].Sid, &sidStr)) + { + result += " - "; + result += sidStr; + LocalFree(sidStr); + } + } + result += "\n"; + } + } + CloseHandle(token); + } +#else + struct passwd* pw = getpwuid(geteuid()); + if(pw) + { + result += "User: "; + result += pw->pw_name; + result += "\nUID: "; + result += std::to_string(pw->pw_uid); + result += " GID: "; + result += std::to_string(pw->pw_gid); + result += "\n"; + } + int ngroups = getgroups(0, nullptr); + if(ngroups > 0) + { + std::vector groups(ngroups); + getgroups(ngroups, groups.data()); + result += "Groups: "; + for(int i=0;igr_name; + else + result += std::to_string(groups[i]); + if(i+1& splitedCmd, C2Message& c2Message); + int process(C2Message& c2Message, C2Message& c2RetMessage); + int osCompatibility() + { + return OS_LINUX | OS_WINDOWS; + } + +private: + std::string getInfoString(); +}; + +#ifdef _WIN32 +extern "C" __declspec(dllexport) Whoami* WhoamiConstructor(); +#else +extern "C" __attribute__((visibility("default"))) Whoami* WhoamiConstructor(); +#endif diff --git a/modules/Whoami/tests/testsWhoami.cpp b/modules/Whoami/tests/testsWhoami.cpp new file mode 100644 index 0000000..10f2ab1 --- /dev/null +++ b/modules/Whoami/tests/testsWhoami.cpp @@ -0,0 +1,25 @@ +#include "../Whoami.hpp" + +bool testWhoami(); + +int main() +{ + bool res; + std::cout << "[+] testWhoami" << std::endl; + res = testWhoami(); + if (res) + std::cout << "[+] Sucess" << std::endl; + else + std::cout << "[-] Failed" << std::endl; + return !res; +} + +bool testWhoami() +{ + std::unique_ptr mod = std::make_unique(); + std::vector cmd = {"whoami"}; + C2Message msg, ret; + mod->init(cmd, msg); + mod->process(msg, ret); + return !ret.returnvalue().empty(); +}