Skip to content

Instantly share code, notes, and snippets.

@s-ff
Last active November 26, 2023 04:35
Show Gist options
  • Save s-ff/a999fd3ef9d309fbfce31112de233725 to your computer and use it in GitHub Desktop.
Save s-ff/a999fd3ef9d309fbfce31112de233725 to your computer and use it in GitHub Desktop.
Title Date Author email
APC Queue Code Injection
05 May 2021
Soufiane Fariss
soufiane.fariss@um5s.net.ma

APC Queue Code Injection

Simple APC Queue Code Injection

Injection techniques that rely on creating a remote thread in the target process to execute the shellcode might cause a huge increase in the malware confidence score which will raise a lot of suspicion among security products. Nevertheless, these techniques create a new thread, which causes a lot of overhead because of allocating new resources to get the thread up and running.

So, is there a way we can run shellcode without resorting to creating a new thread? The answer is yes!

Threads are able to run asynchronous tasks (Asynchronous Procedure Calls), though they can only do it one at time. If and only if, a thread enters an alterable state, it checks its APC queue for queued tasks to run. We can take advantage of this and queue a malicious task (APC) for it to get executed by the thread.

Though, there's no guarantee that the thread will run the shellcode. Because, as we previously stated, a thread can't switch between APCs unless it enters an alterable state. This happens when one of the following APIs is called SleepEx, WaitForSignalObject, WaitForMultipleObjects.. etc.

Control Flow for an Application Using APCs

Picture courtesy of Dwight Hohnstein

Injection Flow

To carry out this code injection techniques, we first need to find the target (remote) process PID, allocate and copy our shellcode to the process memory, enumerate all the remote threads, finally queue our code to their respective APC queues and wait for one of them to enter an alterable state.

This flow is presented as sequence diagram: We suppose that only one thread is running in svchost.exe and that is the the main thread.

sequenceDiagram
participant malware.exe
participant svchost.exe
activate svchost.exe
malware.exe ->> malware.exe: Enumerate running processes
Note left of malware.exe: Malware.exe retrieves<br/> the PID of svchost.exe

activate svchost.exe

malware.exe ->> svchost.exe: OpenProcess()
malware.exe ->> svchost.exe: VirtualAlloc()
malware.exe ->> svchost.exe: WriteProcessMemory()

Note over malware.exe,svchost.exe: Malware.exe opens a handle<br/> to the remote process,<br/> allocates virtual memory <br/>and, copies shellcode to memory  

malware.exe ->>svchost.exe: Enumerate remote threads<br>and OpenThread()
malware.exe ->>svchost.exe: QueueUserAPC()

deactivate svchost.exe
Note over malware.exe,svchost.exe: After queueing, wait<br> for a thread to enter an alterable state 

rect rgb(191, 223, 255)



svchost.exe-->> svchost.exe: Thread enters alterable state

Note right of svchost.exe: Thread fetches a queued APC<br> from its APC Queue
end

activate svchost.exe
svchost.exe->>svchost.exe: Resume execution
Note right of svchost.exe: Thread resumes execution <br>and runs shellcode

deactivate svchost.exe
Loading

Blue rectangle means, main thread has entered an alterable state.

Note: In most case, allocating a RWX using VirtualAllocEx() with PAGE_EXECUTE_READWRITE could trigger an EDR/AV. A quick workaround is to first allocate a RW region with PAGE_READWRITE (Read, Write) permissions then using VirtualProtectEx to make the allocation executable. However, the payload has to be non-self-changing, meaning it doesn't write back to memory since it doesn't have the required permission Demo: IMAGE ALT TEXT HERE

Early Bird APC Queue Code Injection

The simple (or classic) APC queue injection techniques that we introduced the previous section, involves queuing an already-running remote process' threads. This is however, is not optimal when it comes to avoiding EDR hooks. When a process is already running and hooks are present, they can simply intercept the malicious code.

So we might ask ourselves, is there a way we can avoid EDR/AV hooks when deploying our shellcode?

The idea revolves around creating a legitimate process in a SUSPENDED state, queuing an APC to the main thread, then resuming the thread. In order to trigger execution the malware uses an asynchronous procedure call and enforces execution of the APC call using the NtTestAlert function in the thread initialization phase, avoiding hooks placed by EDR.

Note: NtTestAlert is an undocumented API. Though, it is called after the main thread initialization and before the the thread starts to check if APC jobs are queued. If yes, it notifies the kernel (By calling KiUserApcDispatcher), otherwise it has no effect. So, if we queued an APC, then start main thread, NtTestAlert is called, which will immediately process the malicious APC before the process main routine!

This variant of APC Queue injections is dubed "Early Bird" referring to the early stage of queuing the APC, and is famously used by "TURNEDUP" malware attributed to APT33.

Here is a simplified flowchart:

graph TB
id[Create SUSPENDED process] --> id2[Allocate virtual memory]
id2[Allocate virtual memory] --> id3[Copy shellcode to memory]
id3[Copy shellcode to memory] --> id4[Queue APC] --> id5[Resume Thread]
Loading

Demo:

Early Bird Demo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment