One of my vulnerability research goals is to find and exploit a privilege escalation bug in a real world driver. In preparation to meet this goal I started working with the HackSys Extreme Vulnerable Driver, specifically the 2.0 version on an x64 Windows 7 sp1 system. Being a new comer to kernel exploitation I chose to start working on the simple stack buffer overflow vulnerability provided by the driver. There are many write ups on how to exploit this vulnerability and the official github repo even comes pre-packaged with an exploit for it.
For some reason none of these exploits were working for me! This was very frustrating because I wanted to follow a working example to help learn how to do it. Eventually, I solved the problem on my own, and I’m very curious to know if anyone else encountered it. Because there are so many write ups for this vuln out there I’m not going to do a full analysis, but instead just focus on the area that was giving me trouble. My wish is that this post will help someone in the future.
The vulnerability is in the memcpy
highlighted below, and the issue I was having is with the pop rdi
also highlighted:
With the memcpy
vulnerability we can overwrite the return address on the stack. You can see though, that before the ret
instruction there is a pop rdi
. What gets popped in to rdi
is a pointer to an IRP
which will be required as an argument to IoCompleteRequest
in order to cleanly exit this call to the driver. But, in order to overwrite the return address we also have to overwrite the pointer to this IRP
. None of the exploits/writeups that I’ve seen take this in to account, and thus they were all crashing my VM rather than giving me a system shell.
To see what I’m talking about here’s what the stack looks like if we break on this pop rdi
instruction during a run that does not attempt to exploit the memcpy
:
As you can see there is a pointer on the top of the stack. Now let’s look at the stack exploiting the buffer overflow vulnerability:
You can see that on the top of the stack we have AAAAAAAA
which is from the buffer overflow. The pointer following that is the address of the userland buffer containing shellcode. The shellcode will run, but because rdi
ends up not having the IRP
that it needs the program will die shortly after the shellcode executes.
Once I realized what the problem was the solution was quite simple. It turns out that this IRP
pointer is also in other locations on the stack at a fixed offset from rsp
at the time that the shellcode is triggered. I added a mov
instruction to the beginning of the standard token stealing shellcode and all of sudden it worked: