An assumption in my previous blog posts was the ability to execute arbitrary assembly code in kernel context. While it is possible to obtain this from a write-what-where vulnerability condition and often from a pool overflow, it does require both a kernel read/write primitive and a KASLR bypass to some kernel driver. If we limit ourselves to using the read/write primitive to perform a data only attack, we can omit the KASLR bypass. This blog post describes how each of the three methods can be converted to a data only attack instead of an actual shellcode.
Before we start, a kernel read/write primitive is needed, luckily I showed in a previous blog post how the tagWnd structure can be abused, even in Windows 10 Anniversary Edition or the upcoming Creators Update. This technique will be based upon that exploit primitive, however any other kernel read/write primitive could take its place. It should also be noted that the tagWnd exploit primitive is not blocked by Win32k syscall filtering, so it works in both Internet Explorer and Microsoft Edge.
The token stealing shellcode was explained in the first blog post and begins by fetching the address of the KTHREAD from the GS register. Here we run into a problem, since we are not able to read this using a read primitive. Luckily we may find it from the tagWnd object, Windows 10 is not released with the symbols for tagWnd, but ReactOS has the structures for 32 bit Windows XP, so we can hopefully translate ourselves. The tagWND object has the follow beginning structure:
That is some header structure, looking that up we find:
Which again contains another header structure we can look up:
The second element a pointer to a THREADINFO structure, if we look that up on ReactOS we find:
Where the W32THREAD element covers over the following structure:
That means a pointer to the ETHREAD is present here. To sum it up, we leak the address of a tagWND object using the user mode mapped desktop heap. From the address of the tagWND object, we use our read primitive to read the QWORD at offset 0x10, to get the pointer to the THREADINFO structure. Then we read offset 0x0 to get the pointer to the ETHREAD. This is shown in WinDBG below:
In this case the tagWND object is at 0xfffff1b0407afe90, after two reads we have the KTHREAD, but to prove it we read offset 0x220 from that since that is the address of the EPROCESS, which we then verify.
This now gives us a way to read the address of the EPROCESS. Implementing it can be seen below:
The token stealing shellcode looks like this:
The steps we need to convert from assembly to read and writes are:
- Getting the PID of the parent process.
- Locate the EPROCESS of the parent process.
- Locate the EPROCESS of the system process.
- Overwrite the token of the parent process.
The first part is easy through a single read of offset 0x3E0 of the current EPROCESS:
Next we iterate through the EPROCESS’s till we find the PPID at offset 0x2E8:
And then the EPROCESS of the system process:
Finally we fetch the token address and overwrite it in the EPROCESS of the parent process:
Running it and manually modifying the cbwndExtra field of the tagWND to simulate a write-what-where vulnerability we get the following:
So the same effect may be resolved without any kernel mode shellcode execution.
The next method I went through is editing the SID of the DACL in the SecurityDescriptor of the winlogon.exe process along with the MandatoryPolicy of the current process. This allows the program to inject a thread into the winlogon.exe process and run a cmd.exe with SYSTEM privileges. The shellcode looked like this:
The steps we need to translate are:
- Find the EPROCESS of the current process.
- Find the EPROCESS of the winlogon.exe process.
- Modify the DACL of the winlogon.exe.
- Modify the Token of the current process.
We start in the same way by finding the EPROCESS of the current process as before:
Then we find the EPROCESS of the winlogon.exe process by searching for its name:
Then we modify the DACL of the winlogon.exe process at offset 0x48 in the SecurityDescriptor:
Since the exploit primitive only reads and writes QWORDS we read out a full QWORD at offset 0x48 and modify it, then write it back. Then finally we modify the token of the current process in the same way:
We then run the PoC and again manually enlarge the cbwnExtra field to simulate a write-what-where and get the following result:
The final technique was enabling all privileges in the parent process, which in assembly looked like this:
The first part of the code is just a repeat of the token stealing shellcode, where we first find the EPROCESS of the current process, and use that to find the EPROCESS of the parent process, which we did like shown below:
Next we find the token, ignore the fast reference bits and set the values at offset 0x48 to 0xFFFFFFFFFFFFFFFF:
Running this method and simulating a white-what-where again we get the following result:
Running the PoC again we are now able to inject into winlogon.exe due to the higher privileges:
This concludes the conversion of kernel shellcode to data only attacks and makes it clear that for standard privilege escalation to SYSTEM arbitrary kernel mode execution is not needed and neither is KASLR bypass for kernel drivers.
The proof of concept code can be found on GitHub at here