Security News

Cybersecurity news aggregator

⚔️
MEDIUM Attacks Reddit r/netsec

A stealth approach to Process Injection - EntryPoint Hijacking

  • What: A new code injection technique called EntryPoint Hijacking was described
  • Impact: Enables threat actors to evade EDR detection and extend dwell time
Read Full Article →

The technique of EntryPoint Hijacking introduces a stealthier approach to code injection as it doesn’t use API calls that create a new thread within the context of a process, and it independent of the attack chain. Arbitrary code is written in memory, but it is executed only when a thread is created by the process legitimately. The technique enables threat actors to evade EDR defences and extend the dwell time in the affected environment. Playbook Windows processes dynamically load multiple modules (DLLs) into memory at runtime. Each module contains a DllMain() function that the operating system automatically invokes in response to process and thread creation or termination events. The Windows loader function ( ntdll!Lrdp* ) maintains a record of each DLL loaded, with its properties to manage these invocations, including the EntryPoint address. Sophisticated threat actors can overwrite the EntryPoint function of the targeted DLL to redirect the execution flow to attacker-controlled code whenever the loader function calls the DllMain() . However, hijacking the EntryPoint introduces challenges for threat actors, such as process stability issues, race conditions and crashes. Kurosh Dabbagh Escalante released the EPI (EntryPoint Injection) proof of concept in 2023 and introduced a documented method to abuse the EntryPoint property of a DLL. EPI patches the EntryPoint of a loaded DLL ( kernelbase.dll ) and uses the QueueUserWorkItem from inside the redirected EntryPoint . The malicious code is executed on a thread-pool thread. Hugo Valette approached the same technique during x33fcon 2025, and released two proof-of-concepts examples called LdrShuffle , demonstrating EntryPoint Hijacking within the same and remote processes. It should be noted that LdrShuffle handles the execution differently, even though both proof of concepts hijack the same property. Windows identifies the DllMain() of DLLs via the EntryPoint property. Purple team operators can identify the memory address of the EntryPoint by executing the following commands. In the example below, the kernelbase.dll was used. lm m kernelbase dt nt!_LDR_DATA_TABLE_ENTRY 0x7ffdc640bcb0 DLL EntryPoint The code of the LdrShuffle has been analysed to understand the technique internals. The DontCallForThreads == 0 is a boolean configuration check that allows thread-related calls in the process. If the setting is set to 1, threading is blocked. It should be highlighted that more complex APIs such as the InternetOpenW will cause the process to crash due to thread-syncing issues (deadlocks). Therefore, APIs receiving C2 callbacks must run in a separate thread. The i>5 is used to skip from hijacking the first five DLLs of the process for stability reasons. (pDte->EntryPoint != NULL && pDte->DontCallForThreads == 0 && i > 5) The EntryPoint is restored promptly to prevent process crashes. BOOL RestoreLdr(IN ULONG_PTR dllBase) { //PEB #ifdef _WIN64 PPEB pPeb = (PPEB)(__readgsqword(0x60)); #elif _WIN32 PPEB pPeb = (PPEB)(__readgsqword(0x30)); #endif PDATA_T pDataT = NULL; PEB_LDR_DATA* pPebLdr = (PEB_LDR_DATA*)pPeb->pLdr; //First element of list: PLDR_DATA_TABLE_ENTRY2 pDte = (PLDR_DATA_TABLE_ENTRY2)((ULONG_PTR)pPebLdr->InMemoryOrderModuleList.Flink - 0x10); while (pDte) { if (pDte->BaseDllName.Length != NULL) { if (pDte->DllBase == (PVOID)dllBase) { pDataT = (PDATA_T)pDte->OriginalBase; // restore the two modified fields to their original values kept in PDATA_T struct pDte->EntryPoint = (PLDR_INIT_ROUTINE)pDataT->bakEntryPoint; pDte->OriginalBase = pDataT->bakOriginalBase; return TRUE; } } else { break; } //travelling to next pDte: pDte = *(PLDR_DATA_TABLE_ENTRY2*)(pDte); } return FALSE; } Execution and arguments are passed via a Runner(), a helper application that is invoked to conduct API calls. The data structure resides in the heap, a dynamically managed region where a process allocates and frees memory at runtime. The Runner() accesses this memory region to determine what to execute. typedef struct _DATA_T { // LDR structures manipulation ULONG_PTR runner; // malicious entry point to execute ULONG_PTR bakOriginalBase; // backup of overwritten OriginalBase ULONG_PTR bakEntryPoint; // backup of overwritten EntryPoint HANDLE event; // event signalling that the Runner has executed // function call ULONG_PTR ret; // return value DWORD createThread; // run this API call in a new thread (required for wininet/winhttp) ULONG_PTR function; // Windows API to call DWORD dwArgs; // number of args ULONG_PTR args[MAX_ARGS]; // array of args } DATA_T, * PDATA_T; The LdrShuffle proof of concept can be executed from a console with no arguments to conduct the process injecting within the same process. LdrShuffle.exe LdrShuffle – EntryPoint Hijacking The second proof-of-concept released (part of the LdrShuffle repository) enables operators to target a remote process within the system and inject shellcode. LdrInject.exe 3612 demon.x64.bin LdrInject – Shellcode LdrInject The diagram below ...

Share this article