Windows Kernel Shellcode on Windows 10 – Part 4 - There is No Code

This blog post is an addendum to the three blog posts about Windows kernel shellcode I posted based on the techniques by Cesar Cerrudo. You can find the previous blog posts here, here and here.

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.

Token Stealing

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 following beginning structure:

1fc97-img.png

That is some header structure, looking that up we find:

0db49-img.png

Which again contains another header structure we can look up:

1486733389823_3.png

The second element a pointer to a THREADINFO structure, if we look that up on ReactOS we find:

09ad3-img.png

Where the W32THREAD element covers over the following structure:

1f04b-img.png

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:

1d6f2-img.png

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:

ccd14-img.png

The token stealing shellcode looks like this:

1486733500285_8.png

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:

4db5c-img.png

Next we iterate through the EPROCESS’s till we find the PPID at offset 0x2E8:

34418-img.png

And then the EPROCESS of the system process:

97d1c-img.png

Finally we fetch the token address and overwrite it in the EPROCESS of the parent process:

af22a-img.png

Running it and manually modifying the cbwndExtra field of the tagWND to simulate a write-what-where vulnerability we get the following:

c3ab4-img.png

So the same effect may be resolved without any kernel mode shellcode execution.

ACL Edit

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:

f7d04-img.png

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:

7c19c-img.png

Then we find the EPROCESS of the winlogon.exe process by searching for its name:

fda25-img.png

Then we modify the DACL of the winlogon.exe process at offset 0x48 in the SecurityDescriptor:

ccf40-img.png

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:

53bc9-img.png

We then run the PoC and again manually enlarge the cbwnExtra field to simulate a write-what-where and get the following result:

2b8fb-img.png

Enable Privileges

The final technique was enabling all privileges in the parent process, which in assembly looked like this:

8faa1-img.png

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:

d019b-img.png

Next we find the token, ignore the fast reference bits and set the values at offset 0x48 to 0xFFFFFFFFFFFFFFFF:

a61fe-img.png

Running this method and simulating a white-what-where again we get the following result:

a0a8a-img.png

Running the PoC again we are now able to inject into winlogon.exe due to the higher privileges:

4f44a-img.png

The End

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