Hey gang. Today’s post will be about hijacking thread execution. I hope you knew that already. It was in the title. Quick post this weekend. I’ve been quite busy.
Before You Read
It is recommended to read these related posts before reading this one, so that you don’t get lost during the discussion of the concepts:
What
First things first, let’s run through a quick crash course on threads. You’ve probably heard of them, but here goes nothing.
A process is a container. This container holds threads, modules, and code (and a few other things that aren’t that important right now). In order to execute the code you have written and then built into a process, it must be executed via a thread.
Threads are what run your code. And there are usually a bunch of them. Think of threads like workers in a factory. Each worker executes his or her job within the context of the factory (the process!). Threads work together to keep the factory producing what it needs to produce, and they also help spread out the work that needs to be done. Considering threads can be created, suspended, prioritized, interrogated, executed, killed, put to sleep, and even hijacked, this analogy feels very appropriate.
Each thread has its own allocated set of registers, stack, and program counter. There are 2 types of threads as well, one for user-mode and another for kernel-mode. I won’t go too in-depth on the differences, but the basics is that user-mode threads are managed by a user-mode library, and kernel-mode threads are managed by the kernel. Since, in kernel-mode, there is more control over scheduling, creation, and permissions, kernel-mode threads are arguably more powerful.
We’re going to focus on user-mode for now. As mentioned, threads are managed by a user-mode library which has exposed API. Therefore, you can use that API for not-so-benevolent purposes.
Remember in DLL Injection where the main concept is that we are creating a remote thread within another process to run some code? This is just an example of the type of attack vectors the Windows API happily gives us.
Now we can finally explore the concept of thread hijacking.
Thread hijacking entails telling Windows the following instructions:
Hey Windows, open this thread and suspend it for me
Ok!
Hey Windows, RIP is actually supposed to be here
Ok!
Hey Windows, resume the thread
Ok!
Tada!
This is called thread execution hijacking. Sounds pretty easy. Namely because it is. This is a very simple concept because Windows provides all of the nicely documented API for you to do it. The only hard part is writing the shellcode.
Why
The main benefits of thread execution hijacking is that it’s a neat way to have your victim process run some shellcode for you. It’s also sneakier than just running CreateRemoteThread because you aren’t creating an entire thread to run your code. You’re using a thread that already exists. If you do it well enough, you won’t have any trace of execution after everything runs.
You can use thread execution hijacking as a form of injection too!
How
Now, unfortunately, I currently do not have the time to spin up a full program that utilizes a thread execution hijack. I could use the code from my own injector, but that could dox me :). However, there is a reference that I used when I wrote it, which is here. The full implementation is in x86, and there are a few pointers in the thread for writing it in x64. But, I can outline the implementation here.
Find the program you want to attack
Should be pretty straightforward.
Iterate the threads of the process
This is done by calling CreateToolhelp32Snapshot with TH32CS_SNAPTHREAD as the first parameter and the process ID as the second parameter.
Open the thread
Call OpenThread with THREAD_ALL_ACCESS.Suspend the thread
Call SuspendThread.Get the thread’s registers
Call GetThreadContext with the CONTEXT structure’s ContextFlags as CONTEXT_FULLSet up the shellcode
The end of the shellcode should JMP back to the current RIP/EIPInsert shellcode
Allocate a buffer in the target program, and stick the shellcode into the bufferSet IP
Set the context’s IP to the shellcode, then call SetThreadContextResume the thread
ResumeThreadSneakily deallocate the buffer
If you want to be sneaky, you can zero out and deallocate the buffer. You might run into a race condition, so you may want to sleep for a few ms before doing that.
Sorry about that. I usually provide a POC with my paid posts. Oh well, consider it some homework.
That’s all for this post. Have a great weekend!
Go!
-BowTiedCrawfish