/REM-Essentials/Windows: Malware Evasion through Injection Part-2
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.
This Episode's Flow:
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>, ...);
// 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 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 resouces 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 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 funtions, but variants of this method could in fact sneak behind these detections and succeed.
Any injection method could be summerized into two parts:
1) Injecting/Writting 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 Alertable State, For Example:
a thread sleeps for 10 seconds by calling SleepEx(50000, bAlertable==TRUE); then the thread is said to perform an alertable 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 alertable 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 alertable 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 inejction, the malicious executable will:
1-> Locate a target process that's most likely to enter an alertable 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 alertable 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 alertable 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 ); */
Based on the scheme we layed 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/EDRs. 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 detections.
Instead of firing alarms with QueueUserAPC(), this technique depends on the normal behaviour 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 gurantees 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 get's 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 ususally 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 susended, 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