commit 581e189e2a14baffef66ca5a5abda67a015a5504 Author: Ido Veltzman <62358580+Idov31@users.noreply.github.com> Date: Sun Sep 25 10:48:32 2022 +0300 Init Co-Authored-By: Yxel <47823818+janoglezcampos@users.noreply.github.com> diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..796f0d2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode +bin/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b1d6652 --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +BSD 2-Clause License + +Copyright (c) 2022, Ido Veltzman, Yxel +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..19d47ff --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +# Cronos + +![c](https://img.shields.io/badge/C-00599C?style=for-the-badge&logo=c&logoColor=white) ![assembly](https://img.shields.io/badge/ASSEMBLY-ED8B00?style=for-the-badge&logo=Assembly&logoColor=white) ![windows](https://img.shields.io/badge/Windows-0078D6?style=for-the-badge&logo=windows&logoColor=white) + +## This project was co-authored by + +![idov31](https://img.shields.io/badge/Idov31-FF1B2D?style=for-the-badge&logo=Idov31&logoColor=white) ![YXEL](https://img.shields.io/badge/YXEL-7D4698?style=for-the-badge&logo=YXEL&logoColor=white) + +## Description + +PoC for a new sleep obfuscation technique (based on [Ekko](https://github.com/Cracked5pider/Ekko)) leveraging waitable timers to RC4 encrypt the current process and change the permissions from RW to RX to evade memory scanners. + +A more detailed explanation will be available in the blog post (COMING SOON). + +![POC](img/poc.png) + +## Usage + +To use it, all you have to do is to include Cronos in your project and use it like so: + +```c +#include "Cronos.h" + +int main() { + int timesToExecute = 1337; + int seconds = 10; + + for (int i = 0; i < timesToExecute; i++) { + CronosSleep(seconds); + + // YOUR CODE HERE! + } +} +``` + +## Setup + +To compile it you will need: + +- [NASM](https://www.nasm.us/) +- [make](https://stackoverflow.com/questions/32127524/how-to-install-and-use-make-in-windows) +- [VisualStudio Compiler](https://developer.microsoft.com/en-US/windows/downloads/windows-sdk/) + +After you have all of the above, navigate to the project's directory and build it with the makefile, the EXE will be in the bin directory. + +## Contributors + +Thanks a lot to those people that contributed to this project: + +- [Orca](https://github.com/ORCx41) + +## Resources + +- [Ekko](https://github.com/Cracked5pider/Ekko) +- [DeathSleep](https://github.com/janoglezcampos/DeathSleep) +- [Waitable Timers](https://learn.microsoft.com/en-us/windows/win32/sync/using-a-waitable-timer-with-an-asynchronous-procedure-call) diff --git a/headers/Cronos.h b/headers/Cronos.h new file mode 100644 index 0000000..0cb0c84 --- /dev/null +++ b/headers/Cronos.h @@ -0,0 +1,29 @@ +#ifndef CRONUS_H +#define CRONUS_H + +#include +#include + +// Macros +#define TIMER_SLEEP 2000 +#define InitializeTimerMs(ft, sec) \ + { \ + (ft)->HighPart = (DWORD)(((ULONGLONG) - ((sec) * 1000 * 10 * 1000)) >> 32); \ + (ft)->LowPart = (DWORD)(((ULONGLONG) - ((sec) * 1000 * 10 * 1000)) & 0xffffffff); \ + } + +// Struct definitions. +typedef struct _CRYPT_BUFFER { + DWORD Length; + DWORD MaximumLength; + PVOID Buffer; +} CRYPT_BUFFER, *PCRYPT_BUFFER, DATA_KEY, *PDATA_KEY, CLEAR_DATA, * PCLEAR_DATA, CYPHER_DATA, * PCYPHER_DATA; + + +// Functions. +typedef NTSTATUS(WINAPI* tSystemFunction032)(PCRYPT_BUFFER pData, PDATA_KEY pKey); + +extern void QuadSleep(PVOID, PVOID, PVOID, PVOID); +void CronosSleep(int ticks); + +#endif \ No newline at end of file diff --git a/headers/Utils.h b/headers/Utils.h new file mode 100644 index 0000000..ee9b7cd --- /dev/null +++ b/headers/Utils.h @@ -0,0 +1,15 @@ +#ifndef UTILS_H +#define UTILS_H + +#include "Cronos.h" +#include + +#define SIZE_MODULE_LIST 2 +#define MAX_MODULE_NAME 100 + +BOOL bCompare (const BYTE *pData, const BYTE *bMask, const char *szMask); +DWORD_PTR findPattern (DWORD_PTR dwAddress, DWORD dwLen, PBYTE bMask, PCHAR szMask); +DWORD_PTR findInModule (LPCSTR moduleName, PBYTE bMask, PCHAR szMask); +PVOID findGadget (PBYTE hdrParserFuncB, PCHAR hdrParserFunctMask); + +#endif \ No newline at end of file diff --git a/img/poc.png b/img/poc.png new file mode 100644 index 0000000..fe9836a Binary files /dev/null and b/img/poc.png differ diff --git a/makefile b/makefile new file mode 100644 index 0000000..d08628c --- /dev/null +++ b/makefile @@ -0,0 +1,21 @@ +CC = cl + +ASM_IN = src/asm/rop.asm +ASM_OUT = bin/rop.obj +HEADERS = headers +BIN_FOLDER = bin +BIN_NAME = Cronos.exe + +all: clean compile_asm compile clean_obj + +compile_asm: + nasm -f win64 $(ASM_IN) -o bin/rop.obj + +compile: + $(CC) src/*.c $(ASM_OUT) /I $(HEADERS) /Fo:$(BIN_FOLDER)\ /Fe: $(BIN_FOLDER)\$(BIN_NAME) + +clean_obj: + del /Q $(BIN_FOLDER)\*.obj + +clean: + del /Q $(BIN_FOLDER)\* diff --git a/src/Cronos.c b/src/Cronos.c new file mode 100644 index 0000000..7921317 --- /dev/null +++ b/src/Cronos.c @@ -0,0 +1,148 @@ +#include "Cronos.h" +#include "Utils.h" + +void CronosSleep(int sleepTime) { + HANDLE hProtectionRWTimer; + HANDLE hProtectionRWXTimer; + HANDLE hEncryptionTimer; + HANDLE hDecryptionTimer; + HANDLE hDummyThreadTimer; + + LARGE_INTEGER protectionRWDueTime; + LARGE_INTEGER protectionRWXDueTime; + LARGE_INTEGER encryptionDueTime; + LARGE_INTEGER decryptionDueTime; + LARGE_INTEGER dummyDueTime; + + PVOID rcxGadget; + PVOID rdxGadget; + PVOID shadowFixerGadget; + + CONTEXT ctxDummyThread = { 0 }; + CONTEXT ctxProtectionRW = { 0 }; + CONTEXT ctxProtectionRWX = { 0 }; + CONTEXT ctxEncryption = { 0 }; + CONTEXT ctxDecryption = { 0 }; + + PVOID NtContinue = NULL; + tSystemFunction032 SystemFunction032 = NULL; + + PVOID ImageBase = NULL; + DWORD ImageSize = 0; + DWORD oldProtect = 0; + CRYPT_BUFFER Image = { 0 }; + DATA_KEY Key = { 0 }; + CHAR keyBuffer[16] = { 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }; + + // Load systemfunction032. + HMODULE hAdvapi32 = LoadLibraryA("Advapi32.dll"); + HMODULE hNtdll = GetModuleHandleA("Ntdll.dll"); + + if (hAdvapi32 == 0 || hNtdll == 0) + return; + + SystemFunction032 = (tSystemFunction032) GetProcAddress(hAdvapi32, "SystemFunction032"); + NtContinue = GetProcAddress(hNtdll, "NtContinue"); + + // Getting the image base. + ImageBase = GetModuleHandleA(NULL); + ImageSize = ((PIMAGE_NT_HEADERS)((DWORD_PTR)ImageBase + ((PIMAGE_DOS_HEADER)ImageBase)->e_lfanew))->OptionalHeader.SizeOfImage; + + // Initializing the image and key for SystemFunction032. + Key.Buffer = keyBuffer; + Key.Length = Key.MaximumLength = 16; + + Image.Buffer = ImageBase; + Image.Length = Image.MaximumLength = ImageSize; + + // Creating the waitable timers. + hProtectionRWTimer = CreateWaitableTimerW(NULL, TRUE, L"ProtectionRWTimer"); + hProtectionRWXTimer = CreateWaitableTimerW(NULL, TRUE, L"ProtectionRWXTimer"); + hEncryptionTimer = CreateWaitableTimerW(NULL, TRUE, L"EncryptionTimer"); + hDecryptionTimer = CreateWaitableTimerW(NULL, TRUE, L"DecryptionTimer"); + hDummyThreadTimer = CreateWaitableTimerW(NULL, TRUE, L"DummyTimer"); + + if (hProtectionRWTimer == 0 || hProtectionRWXTimer == 0 || + hEncryptionTimer == 0 || hDecryptionTimer == 0 || hDummyThreadTimer == 0) { + + printf("[ - ] Failed to create waitable timers: %d", GetLastError()); + FreeLibrary(hAdvapi32); + return; + } + + InitializeTimerMs(&dummyDueTime, 0); + + // Capture apc context. + if (!SetWaitableTimer(hDummyThreadTimer, &dummyDueTime, 0, (PTIMERAPCROUTINE) RtlCaptureContext, &ctxDummyThread, FALSE)) { + printf("[ - ] Failed to capture context: %d", GetLastError()); + goto CleanUp; + } + SleepEx(INFINITE, TRUE); + + // Creating the contexts. + memcpy(&ctxProtectionRW, &ctxDummyThread, sizeof(CONTEXT)); + memcpy(&ctxEncryption, &ctxDummyThread, sizeof(CONTEXT)); + memcpy(&ctxDecryption, &ctxDummyThread, sizeof(CONTEXT)); + memcpy(&ctxProtectionRWX, &ctxDummyThread, sizeof(CONTEXT)); + + // VirtualProtect( ImageBase, ImageSize, PAGE_READWRITE, &OldProtect ); + ctxProtectionRW.Rsp -= (8 + 0x150); + ctxProtectionRW.Rip = (DWORD_PTR) VirtualProtect; + ctxProtectionRW.Rcx = (DWORD_PTR) ImageBase; + ctxProtectionRW.Rdx = ImageSize; + ctxProtectionRW.R8 = PAGE_READWRITE; + ctxProtectionRW.R9 = (DWORD_PTR) &oldProtect; + + ctxEncryption.Rsp -= (8 + 0xF0); + ctxEncryption.Rip = (DWORD_PTR) SystemFunction032; + ctxEncryption.Rcx = (DWORD_PTR) &Image; + ctxEncryption.Rdx = (DWORD_PTR) &Key; + + ctxDecryption.Rsp -= (8 + 0x90); + ctxDecryption.Rip = (DWORD_PTR) SystemFunction032; + ctxDecryption.Rcx = (DWORD_PTR) &Image; + ctxDecryption.Rdx = (DWORD_PTR) &Key; + + ctxProtectionRWX.Rsp -= (8 + 0x30); + ctxProtectionRWX.Rip = (DWORD_PTR) VirtualProtect; + ctxProtectionRWX.Rcx = (DWORD_PTR) ImageBase; + ctxProtectionRWX.Rdx = ImageSize; + ctxProtectionRWX.R8 = PAGE_EXECUTE_READWRITE; + ctxProtectionRWX.R9 = (DWORD_PTR) &oldProtect; + + InitializeTimerMs(&protectionRWDueTime, 0 ); + InitializeTimerMs(&encryptionDueTime, 1 ); + InitializeTimerMs(&decryptionDueTime, sleepTime-1 ); + InitializeTimerMs(&protectionRWXDueTime, sleepTime); + + // Getting the gadgets for the sleepex rop. + rcxGadget = findGadget((PBYTE) "\x59\xC3", "xx"); + rdxGadget = findGadget((PBYTE) "\x5A\xC3", "xx"); + shadowFixerGadget = findGadget((PBYTE) "\x48\x83\xC4\x20\x5F\xC3", "xxxxxx"); + + if(rcxGadget == 0 || rdxGadget == 0 || shadowFixerGadget == 0) { + printf("[!] Error finding gadget\n"); + goto CleanUp; + } + + // Setting the timers. + if (!SetWaitableTimer(hDecryptionTimer, &decryptionDueTime, 0, NtContinue, &ctxDecryption, FALSE) || + !SetWaitableTimer(hProtectionRWXTimer, &protectionRWXDueTime, 0, NtContinue, &ctxProtectionRWX, FALSE) || + !SetWaitableTimer(hProtectionRWTimer, &protectionRWDueTime, 0, NtContinue, &ctxProtectionRW, FALSE) || + !SetWaitableTimer(hEncryptionTimer, &encryptionDueTime, 0, NtContinue, &ctxEncryption, FALSE)) + { + printf("[ - ] Failed to SetWaitableTimer: %d", GetLastError()); + goto CleanUp; + } + + // Executing the code. + QuadSleep(rcxGadget, rdxGadget, shadowFixerGadget, (PVOID) SleepEx); + +CleanUp: + CloseHandle(hDummyThreadTimer); + CloseHandle(hDecryptionTimer); + CloseHandle(hProtectionRWXTimer); + CloseHandle(hProtectionRWTimer); + CloseHandle(hEncryptionTimer); + FreeLibrary(hAdvapi32); +} \ No newline at end of file diff --git a/src/Main.c b/src/Main.c new file mode 100644 index 0000000..36ddf0d --- /dev/null +++ b/src/Main.c @@ -0,0 +1,9 @@ +#include "Cronos.h" + +int main() { + for (int i = 0; i < 4; i++) { + printf("\n[ + ] Sleeping\n"); + CronosSleep(10); + printf("[ + ] Code executed!\n"); + } +} \ No newline at end of file diff --git a/src/Utils.c b/src/Utils.c new file mode 100644 index 0000000..7c40805 --- /dev/null +++ b/src/Utils.c @@ -0,0 +1,74 @@ +#include "Utils.h" + +BOOL bCompare(const BYTE *pData, const BYTE *bMask, const char *szMask) +{ + for (; *szMask; ++szMask, ++pData, ++bMask) + if (*szMask == 'x' && *pData != *bMask) + return FALSE; + + return TRUE; +} + +DWORD_PTR findPattern(DWORD_PTR dwAddress, DWORD dwLen, PBYTE bMask, PCHAR szMask) +{ + for (DWORD i = 0; i < dwLen; i++) + if (bCompare((PBYTE)(dwAddress + i), bMask, szMask)) + return (DWORD_PTR)(dwAddress + i); + + return 0; +} + +DWORD_PTR findInModule(LPCSTR moduleName, PBYTE bMask, PCHAR szMask) +{ + PIMAGE_DOS_HEADER ImageBase = (PIMAGE_DOS_HEADER)GetModuleHandleA(moduleName); + PIMAGE_NT_HEADERS imageNTHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)ImageBase + ImageBase->e_lfanew); + DWORD_PTR section_offset = (DWORD_PTR)ImageBase + ImageBase->e_lfanew + sizeof(IMAGE_NT_HEADERS); + PIMAGE_SECTION_HEADER text_section = (PIMAGE_SECTION_HEADER)(section_offset); + DWORD_PTR dwAddress = findPattern((DWORD_PTR)ImageBase + text_section->VirtualAddress, text_section->SizeOfRawData, bMask, szMask); + return dwAddress; +} + +PVOID findGadget(PBYTE hdrParserFuncB, PCHAR hdrParserFunctMask) +{ + HANDLE hProcess; + BOOL result; + HMODULE *moduleList; + DWORD bytesNeeded; + DWORD nModules = 0; + LPSTR moduleName = NULL; + DWORD_PTR ptr = 0; + // PBYTE hdrParserFuncB = (PBYTE)"\x48\x89\x22\xc3"; + + hProcess = GetCurrentProcess(); + + moduleList = malloc(SIZE_MODULE_LIST * sizeof(HMODULE)); + result = EnumProcessModules(hProcess, moduleList, SIZE_MODULE_LIST * sizeof(HMODULE), &bytesNeeded); + if (bytesNeeded > SIZE_MODULE_LIST * sizeof(HMODULE)) + { + moduleList = realloc(moduleList, bytesNeeded); + result = EnumProcessModules(hProcess, moduleList, bytesNeeded, &bytesNeeded); + } + + if (!result) + goto end; + for (int iModule = 1; iModule < (bytesNeeded / sizeof(HMODULE)); iModule++) + { + moduleName = malloc(MAX_MODULE_NAME * sizeof(CHAR)); + if (GetModuleFileNameExA(hProcess, moduleList[iModule], moduleName, MAX_MODULE_NAME * sizeof(CHAR)) == 0) + goto end; + ptr = findInModule(moduleName, hdrParserFuncB, hdrParserFunctMask); + if (ptr) + { + break; + } + } +end: + if (moduleList) + free(moduleList); + + if (moduleName) + free(moduleName); + if (hProcess) + CloseHandle(hProcess); + return (PVOID)ptr; +} diff --git a/src/asm/rop.asm b/src/asm/rop.asm new file mode 100644 index 0000000..6aff8fb --- /dev/null +++ b/src/asm/rop.asm @@ -0,0 +1,59 @@ +[BITS 64] + +GLOBAL QuadSleep +EXTERN SleepEx + +[SECTION .text] + +QuadSleep: + sub rsp, 0x28 ;0x28 + mov r10, end + push r10 ;0x30 + push r9 ;0x38 + + mov r10, 0xFFFFFFFF + push r10 ;first arg ;0x40 + push rcx ;0x48 + mov r10, 1 + push r10 ;second arg ;0x50 + push rdx ;0x58 + + lea r10, [rdx + 1] + push r10 ;0x60 + + sub rsp, 0x28 ;0x88 + push r8 ;0x90 + push r9 ;0x98 + mov r10, 0xFFFFFFFF + push r10 ;first arg ;0xA0 + push rcx ;0xA8 + mov r10, 1 + push r10 ;second arg ;0xB0 + push rdx ;0xB8 + + lea r10, [rdx + 1] + push r10 ;0xC0 + + sub rsp, 0x28 ;0xE8 + push r8 ;0xF0 + push r9 ;0xF8 + mov r10, 0xFFFFFFFF + push r10 ;first arg ;0x100 + push rcx ;0x108 + mov r10, 1 + push r10 ;second arg ;0x110 + push rdx ;0x118 + + lea r10, [rdx + 1] + push r10 ;0x120 + + sub rsp, 0x28 ;0x148 + push r8 ;0x150 + mov rcx, 0xFFFFFFFF ;first arg + mov rdx, 1 ;second arg + + jmp r9 + +end: + add rsp, 0x28 + ret