Malware Evasion through Injection pt2

In the Previous Episode we started the discussion of Malware Evasion through Injection with some basic PE-Injection Techniques moving from the very simple/ detectable methods to a bit stealthier techniques used by Malware in the Wild.

In this Episode we will continue this discussion with the same incline for each technique from it's basic implementation to more sophisticated variants if available.


DLL-Injection

DLL-Injection used to be the most common Process Injection Method Malware used to inject Malicious Payload into remote processes, it's not the case anymore because it's very easy to detect by AV Scanners. This Method involves having a Malicious File on disk which makes it not stealthy at all, the malicious executable that's responsible for injecting the Malicious DLL either download it by connecting to the C2, or drop it to disk if it exists as a resource in the binary. The Malicious Executable then injects it in a Legitimate Remote Thread it spawns by creating a remote thread from the target process passing it the PATH to the Malicious DLL, the Dropper/Installer then exits leaving the trojaned binary running to do it's nefarious tasks.

=> Spotting DLL-Injection:

Steps taken to implement this technique is straightforward, it involves having a Malicious Executable and a Malicious DLL. On running the Malicious Executable, it performs the following:

  1. Locating a Target Process by querying the running processes, when found, a call to OpenProcess() is made over the target process to obtain a handle to it.

  2. The Malicious Executable will then either Download the Malicious DLL by connecting to the C2, or drop it on disk if it exists as a resource inside the binary.

  3. Allocating a region of memory inside the remote process with the size of the PATH to the Malicious DLL.

  4. Calling a remote thread on the target process with LoadLibraryA() passed as an argument with the PATH to the Malicious DLL.

  5. The Malicious Executable will exit leaving a Trojaned Binary running in the background.

=> Implementing DLL-Injection:

/* Case1: the executable connect to the C2 to download the malicious dll */

// Sample will query running processes looking for a running iexplore.exe
// getting a handle to iexplore.exe
hInternet = InternetOpenA();

// opens a url resource
hUrl = InternetOpenlUrlA(hInternet, lpszUrl, ...);

// _fopen a file to write dll into

// reads file from the connected C2
InternetReadFile(hFile, ...);

/**************************************************/

/* Case2: the executable drops the malicious dll from the resources */

// locating the resource inside the executable and writing it into a file
hRSRC = FindResourceA();
hData = LoadResource(hRSRC, ...);

// write data into a file
hFile = CreateFileA();
hFile = WriteFile(hFile, &Data, ...);

/*************************************************/


// locate target process by querying running processes
hSnapshot = CreateToolhelp32Snapshot();
Process32First(); 
// _strncmp
Process32Last();

// if the target process is located, obtain a handle to it
hProcess = OpenProcessA(..., dwPID);

// allocate a memory for the path to the malicious DLL, write it in target process 
SIZE_OF_MALICIOUS_DLL = _strlen(DLLPATH)
VirtualAllocEx(hProcess, &Address, SIZE_OF_MALICIOUS_DLL, ...);


// write PATH into target process
WriteProcessMemory(hProcess, &Address, &buffer, ...);


// retrieve address of LoadLibraryA
hMod = GetModuleHandle/Ex(lphModName);   //Kernel32.dll
hAddr = GetProcAddress(hMod, lpLoadLibrarya);

// create a thread in the remote target process passing LoadLibraryA with PATH to Malicious DLL
CreateRemoteThread(hProcess, ..., ..., LoadLibrayA, <&allocated_memory>, ...);

Helpful APIs:

this implementation of CreateRemoteThread() is a clear give-away that the sample is performing DLL-Injection

The Famous PoisonIvy RAT been known to use DLL-Injection.


Reflective DLL-Injection

Reflective DLL-Injection is considered more stealthier than regular DLL-Injection, Since DLL-Injection involves having a DLL on disk, Reflective DLL-Injection writes the DLL's Raw Binary into a remote process in an arbitrary memory region directly to avoid storing any file on disk, this technique then doesn't rely on The Windows Loader to load the Injected DLL correctly in memory, but has it's own Custom Loader, this Custom Loader is an exported function of the Injected DLL, and is Position Independent, meaning it can be based / called from anywhere typically called ReflectiveLoader() but can be of any name , So ReflectiveLoader() is called either by shellcode or by a call to CreateRemoteThread() over the target process which is then called the host process passing ReflectiveLoader() to do the Manual Loading which is done by traversing the PEB, figuring out offsets to important Modules' exported functions to correctly load/map the DLL. This way ReflectiveLoader() manages to sneak behind known hooked APIs responsible for loading any binary into memory. After successfully loading the DLL, ReflectiveLoader() will then call it's image's that is correctly mapped now DllMain() returning execution back to it's host process target process and exits.

=> Implementing Reflective DLL-Injection

In order to achieve Reflective DLL-Injection, a sample needs to go through these steps

  1. Starting with a dropper/installer that on executing will locate a target process, obtain a handle to it, with OpenProcess().

  2. With the Malicious DLL's Size, the malicious executable will either download the Malicious DLL/ load it from the resources section if it exists as a resource in the binary.

  3. Allocate enough memory space in the remote process in an arbitrary location for the DLL using VirtualAllocEx(), and write the Malicious DLL in it.

  4. Figuring out the Offset to the Custom Loader ReflectiveLoader() exported function from the Injected DLL.

  5. Execution is then passed to ReflectiveLoader() in the target process, that will start calculating it's own image's location in memory to parse it's image's headers.

  6. ReflectiveLoader() will use PEB Traversal and parses the export table for Mapped Modules like Kernel32/ ntdll looking for offsets to important functions like GetProcAddress() | LoadLibraryA() / VirtualAlloc().

  7. With these APIs' offsets in hand, ReflectiveLoader() will allocate continuous regions of memory to map it's images' the malicious DLL headers/ sections, resolve it's imports, fix relocations and load any needed additional libraries.

  8. Finally ReflectiveLoader() will call DllMain() with DLL_PROCESS_ATTACH flag passed, then it will return execution to the host process by terminating itself.

To Summarize, Reflective DLL-Injection can be described as an emulation of the Windows image Loader, instead of directly calling LoadLibraryA() passing the Malicious DLL, an exported position independent function namely ReflectiveLoader() from the DLL is executed doing all the Manual Loading/ Mapping of the Malicious DLL.

For a full implementation of the Reflective DLL-Injection Technique, check Steven Fewer's github repository, the full code is very well commented and explained.


APC-Injection

APC-Injection is yet another stealthy injection method malware uses to inject their payload into Legitimate Remote Processes, though this also is not stealthy anymore because AV/EDR Products are now actively scanning for the use of APCs to execute functions, but variants of this method could in fact sneak behind these detection and succeed.

Any injection method could be summarized into two parts

  1. Injecting/Writing the malicious payload into a target process.

  2. Executing the malicious payload. What's interesting in APC-Injection is that it forces the target process to execute the malicious injected code on it's behalf, this is done by leveraging Asynchronous Procedure Calls (APC).

For any application, it's threads can execute functions asynchronously by issuing a request to execute a specific function in it's context, which in this case is called The APC Function, tons of requests of this sort are scheduled in an APC-queue that is specific for each thread in a process. In order for a thread to execute any of it's queued APCs it must enter an Alterable State, For Example:

a thread sleeps for 10 seconds by calling SleepEx(50000, bAlertable==TRUE); then the thread is said to perform an alterable wait operation for a time window of 10 seconds, the thread can then handle any pending APCs in it's APC-queue in an FIFO-Manner, if there are any, the thread will invoke the queued APC and stops sleeping.

In Real-Scenarios what a malware sample will do is inject the malicious payload into a target process that is most likely to enter an alterable state explorer.exe is the most targeted then it would open multiple threads in the process and queues the malicious payload in each thread using QueueUserAPC(); that takes a pointer to the APC-Function which is the malicious payload obviously, and just waits for any thread to enter an alterable state, only then the malicious payload will be executed.

This method is much better than calling CreateRemoteThread()/ ResumeThread(), yet security products are fully aware of this technique nowadays.

=> Implementation of APC-Injections:

Steps taken to perform an APC-Injection:

Starting with a malicious executable that's responsible for this stage's injection, the malicious executable will:

  1. Locate a target process that's most likely to enter an alterable state e.g. explorer.exe .

  2. With the target process's PID, the executable will allocate a region of memory in the target process with size of malicious code, write the malicious code into the remote process.

  3. Open a couple of threads in the target process.

  4. The sample will then queue an APC pointing to the malicious payload in all opened threads.

  5. The sample waits for any thread to enter an alterable state.

/******************** APC-Injection Pseudocode *****************/

/* sample starts by querying running processes to locate a process thats
 most likely to have threads enter alertable states, e.g. explorer.exe */

hSnapshot = CreateToolhelp32Snapshot();
Process32First(hSnapshot);
// _strcmp(&pe32.szExeFile, 'explorer.exe');
Process32Next();
// _strcmp(&pe32.szExeFile, 'explorer.exe');
.
.
.
         // get the target Pid
         PID = pe32.th32ProcessID;


// obtain a handle to target process
hProcess = OpenProcess(PROCESS_ALL_ACCESS, ..., dwPid);


// allocate memory in target process with size of malicious payload
&allocated_memory = VirtualAllocEx(hProcess, &Address,
                          <SIZE_OF_SHELLCODE | _strlen(MALICIOUS_DLL_PATH)>, 
                          ...);

// write data into allocated memory
result = WriteProcessMemory(hProcess, &allocated_memory, &buffer,
                          <SIZE_OF_SHELLCODE |_strlen(MALICIOUS_DLL_PATH)>,
                          ...);
// Case1: payload is shellcode:     result -> &allocated_memory (base address of shellcode)
// Case2: payload is malicious DLL: result -> &PATH (pointer to path str) 



// find a couple of threads in target process
Thread32First();
Thread32Next(); 
[] =+ te.th32ThreadID;

// for each thread, open the thread and queue an APC pointing to payload
hThread = OpenThread(THREAD_FULL_ACCESS, ..., dwThreadId);

// Case1: payload is a malicious shellcode
QueueUserAPC(&allocated_memory, hThread, NULL);
CloseHandle(hThread);
       

// Case2: payload is a malicious DLL
QueueUserAPC((PAPCFUNC)LoadLibraryA, hThread, &PATH);
CloseHandle(hThread);


/*
DWORD QueueUserAPC(
   PAPCFUNC  pfnAPC,
   HANDLE    hThread,
   ULONG_PTR dwData
);

*/

// wait for any thread to enter an alertable state

Malware could decide to execute payload in a local process and sets it in an alert-able state by calling any of the Alertable Wait Functions .

/******************** APC-Injection in a Local Process ********************/

// calculating size of payload
payload_size = sizeof(BUFFER);


//allocating enough memory in local process
&allocated_memory = VirtualAlloc(GetCurrentProcess(), &Address, payload_size);


// write shellcode into local process
WriteProcessMemory(GetCurrentProcess(), &allocated_memory, &BUFFER, payload_size, ...);



// queue shellcode as APCFUNC inside own's thread
QueueUserAPC((PAPCFUNC)allocated_memory, GetCurrentThread(), ...);


// set the thread in an alertable state
SleepEx(10000, TRUE);

/*

DWORD SleepEx(
  DWORD dwMilliseconds,
  BOOL  bAlertable
);


*/

Helpful APIs:


EarlyBird-APC-Injection

Based on the scheme we laid out Injection techniques so far, this technique is obviously better that the previous. In fact there are lots of variants of utilizing APCs to execute injected malicious code without triggering AVs**/EDR**s. The EarlyBird APC-Injection is one sophisticated variant of the original technique, that shows how Threat Actors actually reverse engineer legitimate windows components and APIs to circumvent/ sneak behind common detection.

Instead of firing alarms with QueueUserAPC(), this technique depends on the normal behavior of Windows OS and how it handles newly created threads/ processes. For newly created/ suspended process, on resuming the main thread with the initialization of the process starting, a call to NtTestAlert() is executed to check for any queued APCs before the starting of the main thread, if any APCs are found, the thread is set up to call KiUserApcDispatcher() that leads to the execution of the APC.

NtResumeThread();
  |_ LdrInitializethunk() 
       |_ LdrInitialize -> _LdrpInitialize 
                              | _ NtTestAlert()
                                      |_ KiUserApcDispatcher()

=> Implementing EarlyBird APC-Injection

The Malware starts by targeting a process, creating it in a suspended state, writing payload inside it's memory and queuing the payload's address as an APC to the process then resuming the process's main thread. On resuming the main thread of a process suspended process the OS will test for any queued APCs over this process to be executed before the main thread, which guarantees the execution of the malicious injected payload in whatever legitimate binary it's injected into. So it's all the Operating System's work. This gets the malware to sneak behind AV/EDR hooks as they are implemented after the process initialization, which is after the execution of the malicious thread in this case.

/******************** EarlyBird APC-Injection PseudoCode *********************/


// open the target process in a suspended state
hProcess = CreateProcessA(lpName, ...,CREATE_SUSPENDED, ...);

// allocate memory in the target process, write payload into target process
&allocated_memory = VirtualAllocEx(hProcess, &address, PAYLOAD_SIZE, ...);
WriteProcessMemory(hProcess, &allocated_memory, &PAYLOAD, PAYLOAD_SIZE, ...);

// create a thread off of the payload
hPayloadThread = RtlCreateUserThread(hProcess, ... &allocated_memory, ...);

// APCRountine -> &allocated_memory : start of injected shellcode
// queue the malicious thread as an APC to the remote suspended process
NtQueueApcThread(hProcess, &allocated_memory -> ApcRoutine, ...);

// resume suspended process for indirect execution of the malicious thread as an APC.
NtResumeThread(hProcess, ...);

=> Spotting EarlyBird APC-Injection

This method is not the easiest to spot, the only API call you can catch is the NtQueueApcThread() but usually malware authors won't let that an easy catch and would trip analysts away with anti-analysis trick. So the best guess is to watch for a created process as suspended, and maybe attach a debugger on this process looking for an executable region of memory that is possibly the start of a malicious thread.

Practical Example: EarlyBird APC-Injection #Gamaru

Last updated