Starting with Windows Kernel Exploitation – part 2 – getting familiar with HackSys Extreme Vulnerable Driver

Recently I started learning Windows Kernel Exploitation, so I decided to share some of my notes in form of a blog.

The previous part was about setting up the lab. Now, we will play a bit with HackSysExtremeVulnerableDriver by Ashfaq Ansari in order to get comfortable with it. In the next parts I am planning to walk through the demonstrated vulnerabilities and exploitation techniques.

What I use for this part:


Installing and testing HEVD

First, I will show how to install HEVD. We will and configure Debugee and the Debugger in order to see the Debug Strings and HEVD’s symbols. We will also play a bit with dedicated exploits. You can see the video and read the explanations below:

Watching the DebugStrings

HEVD and the dedicated exploits prints a lot of information as DebugStrings. We can watch them from the Debugger machine (using WinDbg) as well as from Debugee machine (using DebugView).

Before installing HEVD, we will set up everything in order to see the strings that are being printed during driver’s initialization.

On the Debugger:

We need to break the execution of the Debugee in order to get the kd prompt (in WinDbg: Debug -> Break). Then, we enable printing Debug Strings via command:

ed nt!Kd_Default_Mask 8

After that, we can let the Debugee run further by executing the command:

g

Warning: Enabling this slows down the Debugee. So, whenever possible, try to watch DebugStrings locally (on the Debugee only).

On the Debugee:

We need to run DebugView as Administrator. Then we choose from the menu:

Capture -> Capture Kernel

capture_kernel

Installing the driver

First, we will download the pre-build package (driver+exploit) on the Debugee (the victim machine), install them and test. We can find it on the github of HackSysTeam, in section releases (https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/releases). The package contains two version of driver – vulnerable and not. We will pick the vulnerable one, built for 32 bit (i386).

osr_load

We choose Service Start as Automatic. Then we click: [Register Service] and when it succeeded: [Start Service].

For driver installation I used OSR Driver Loader, that is a very convenient manager. But alternatively, you can do the installation from commandline, using:

sc create [service name] type=kernel binpath=[driver path]
sc start [service name]

If the installation succeeded, we should see the HEVD banner printed on WinDbg (on the Debugger machine) as well as on DbgView on Debugee Machine.

Adding symbols

The precompiled package of HEVD comes with symbols (sdb file) that we can also add to our Debugger. First, let’s stop the Debugee by sending it a break signal, and have a look at all the loaded modules.

lm

To find the HEVD module, we can set a filter:

lm m H*

We will see, that it does not have any symbols attached. Well, it can be easily fixed. First, turn on:

!sym_noisy

– in order to print all the information about the paths to which WinDbg referred in search for the symbol. Then, try to reload the symbols:

.reload

…and try to refer to them again. You will see the path, where we can copy the pdb file. After moving the pdb file to the appropriate location on the Debugger machine, reload the symbols again. You can test them by trying to print all the functions from HEVD:

x HEVD!*

(See the details on the Video#1)

Testing the exploits

The same package contains also a set of the dedicated exploits. We can run each of them by executing an appropriate command. Let’s try to deploy some of them and set cmd.exe as a program to be executed.

deploying_pool_overfl

Pool Overflow Exploit deployed:

pool_overfl

If the exploitation went successful, the requested application (cmd.exe) will be deployed with elevated privileges.

By the command

whoami

we can confirm, that it is really run elevated:

system

At the same time, we can see on our Debugger machine the Debug Strings printed by the exploit:

dgb_str

All of the exploits, except the double fetch should run well on one core.  If we want this exploit to work, we need to enable two cores on the Debugee machine.

WARNING: Some of the exploits are not 100% reliable and we can encounter a system crash after deploying them. Don’t worry, this is normal.


Hi driver, let’s talk!

Just like in case of the user land, in the kernel land exploitation begins from finding the points, where we can supply an input to the program. Then, we need to find the input that can corrupt the execution (in contrary to the user land – in kernel land a crash will directly result in having a blue screen!). Finally, we will be trying to craft the input in a way that let us control the execution of the vulnerable program.

In order to communicate with a driver from user mode we will be sending it IOCTLs – Input-Output controls. The IOCTL allows us to send from the user land some input buffer to the driver. This is the point from which we can attempt the exploitation.

HEVD contains demos of various classes of vulnerabilities. Each of them can be triggered using a different IOCTL and exploited by the supplied buffer. Some (but not all) will cause our system to crash when triggered.

Finding Device name & IOCTLs

Before we try to communicate with a driver, we need to know two things:

  1. the device that the driver creates (if it doesn’t create any, we will not be able to communicate)
  2. list of IOCTLs (Input-Output Controls) that the driver accepts

HEVD is open-source, so we can read all the necessary data directly from the source code. In real life, most of the time we will have to reverse the driver in order to get it.

Let’s have a look at the fragment of code where HEVD creates a device. https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/blob/master/Driver/HackSysExtremeVulnerableDriver.c#L79

The name of the device is mentioned above.

Now, let’s see find the list of IOCTLs. We will start from looking at the array of IRPs:

https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/blob/master/Driver/HackSysExtremeVulnerableDriver.c#L109

The function linked to IRP_MJ_DEVICE_CONTOL will be dispatching IOCTLs sent to the driver. So, we need to take a look inside this function.

https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/blob/master/Driver/HackSysExtremeVulnerableDriver.c#L193

It contains a switch, that calls a handler function appropriate to handle a particular IOCTL. We can grab our list of IOCTLs by coping the switch cases. The values of the constants are defied in a header:

https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/blob/master/Driver/HackSysExtremeVulnerableDriver.h#L57

Writing a client application

Ok, we got all the necessary data that we can use to communicate with the driver by our own program. We can put it all together in a header file, i.e.: hevd_constants.h


#pragma once
#include <windows.h>
const char kDevName[] = "\\\\.\\HackSysExtremeVulnerableDriver";
#define HACKSYS_EVD_IOCTL_STACK_OVERFLOW CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_NEITHER, FILE_ANY_ACCESS)
#define HACKSYS_EVD_IOCTL_STACK_OVERFLOW_GS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_NEITHER, FILE_ANY_ACCESS)
#define HACKSYS_EVD_IOCTL_ARBITRARY_OVERWRITE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS)
#define HACKSYS_EVD_IOCTL_POOL_OVERFLOW CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_NEITHER, FILE_ANY_ACCESS)
#define HACKSYS_EVD_IOCTL_ALLOCATE_UAF_OBJECT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_NEITHER, FILE_ANY_ACCESS)
#define HACKSYS_EVD_IOCTL_USE_UAF_OBJECT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x805, METHOD_NEITHER, FILE_ANY_ACCESS)
#define HACKSYS_EVD_IOCTL_FREE_UAF_OBJECT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x806, METHOD_NEITHER, FILE_ANY_ACCESS)
#define HACKSYS_EVD_IOCTL_ALLOCATE_FAKE_OBJECT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x807, METHOD_NEITHER, FILE_ANY_ACCESS)
#define HACKSYS_EVD_IOCTL_TYPE_CONFUSION CTL_CODE(FILE_DEVICE_UNKNOWN, 0x808, METHOD_NEITHER, FILE_ANY_ACCESS)
#define HACKSYS_EVD_IOCTL_INTEGER_OVERFLOW CTL_CODE(FILE_DEVICE_UNKNOWN, 0x809, METHOD_NEITHER, FILE_ANY_ACCESS)
#define HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80A, METHOD_NEITHER, FILE_ANY_ACCESS)
#define HACKSYS_EVD_IOCTL_UNINITIALIZED_STACK_VARIABLE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80B, METHOD_NEITHER, FILE_ANY_ACCESS)
#define HACKSYS_EVD_IOCTL_UNINITIALIZED_HEAP_VARIABLE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80C, METHOD_NEITHER, FILE_ANY_ACCESS)
#define HACKSYS_EVD_IOCTL_DOUBLE_FETCH CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80D, METHOD_NEITHER, FILE_ANY_ACCESS)

Number of each IOCTL is created by a macro defined in a standard windows header winioctl.h:

If you include windows.h header, the above macro will be added automatically. For now, we not need to bother about meaning of the particular constants – we will just use the defined elements as they are.

So, we are ready to write a simple user land application that will talk to the driver. First, we open the device using function CreateFile. Then, we can send the IOCTL using DeviceIoControl.

Below you can see a tiny example. This application sends the STACK_OVERFLOW IOCTL to the driver: send_ioctl.cpp


#include <stdio.h>
#include <windows.h>
#define HACKSYS_EVD_IOCTL_STACK_OVERFLOW CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_NEITHER, FILE_ANY_ACCESS)
const char kDevName[] = "\\\\.\\HackSysExtremeVulnerableDriver";
HANDLE open_device(const char* device_name)
{
HANDLE device = CreateFileA(device_name,
GENERIC_READ | GENERIC_WRITE,
NULL,
NULL,
OPEN_EXISTING,
NULL,
NULL
);
return device;
}
void close_device(HANDLE device)
{
CloseHandle(device);
}
BOOL send_ioctl(HANDLE device, DWORD ioctl_code)
{
//prepare input buffer:
DWORD bufSize = 0x4;
BYTE* inBuffer = (BYTE*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufSize);
//fill the buffer with some content:
RtlFillMemory(inBuffer, bufSize, 'A');
DWORD size_returned = 0;
BOOL is_ok = DeviceIoControl(device,
ioctl_code,
inBuffer,
bufSize,
NULL, //outBuffer -> None
0, //outBuffer size -> 0
&size_returned,
NULL
);
//release the input bufffer:
HeapFree(GetProcessHeap(), 0, (LPVOID)inBuffer);
return is_ok;
}
int main()
{
HANDLE dev = open_device(kDevName);
if (dev == INVALID_HANDLE_VALUE) {
printf("Failed!\n");
system("pause");
return -1;
}
send_ioctl(dev, HACKSYS_EVD_IOCTL_STACK_OVERFLOW);
close_device(dev);
system("pause");
return 0;
}

view raw

send_ioctl.cpp

hosted with ❤ by GitHub

Try to compile this program and deploy it on the Debugee machine. Start the DebugView and observe DebugStrings printed by the driver.

If you enabled printing DebugStrings on the Debugger machine, you should see similar output:

As we can see, the driver got our input and reported about it.

Exercise: let’s have a crash!

As an exercise, I created a small client for HEVD, that allows to send it various IOCTLs with the input buffer of the requested length. You can find the source code here:

https://github.com/hasherezade/wke_exercises/tree/master/task1

..and the compiled 32 bit binary here.

Try to play with various IOCTLs, till you get the crash. Because the Debugee runs under the control of the Debugger, you should not get a blue screen – instead, WinDbg will get triggered. Try to make a brief crash analysis for every case. Start from printing the information by:

!analyze -v

Some other helpful commands:

k - stack trace
kb - stack trace with parameters
r - registers
dd [address]- display data as DWORD starting from the address

For more, check the WinDbg help file:

.hh

In our sample application, the user buffer is filled with “A” -> ASCII 0x41 (https://github.com/hasherezade/wke_exercises/blob/master/task1/src/main.cpp#L34):

RtlFillMemory(inBuffer, bufSize, 'A');

So, wherever we see it in the crash analysis, it means the particular data can be filled by the user.

Example #1

Example #2

Mind the fact, that triggering the same vulnerability can give you a different output, depending on the immediate source of the crash, that is related to i.e. size of the overflow, current layout of the memory, etc.

Part 3:
https://hshrzd.wordpress.com/2017/06/22/starting-with-windows-kernel-exploitation-part-3-stealing-the-access-token/

Appendix

About hasherezade

Programmer and researcher, interested in InfoSec.
This entry was posted in KernelMode, Tutorial, WKE and tagged , . Bookmark the permalink.

8 Responses to Starting with Windows Kernel Exploitation – part 2 – getting familiar with HackSys Extreme Vulnerable Driver

  1. DEY says:

    Keep up the great work.

  2. albert says:

    Can I ask you please to share you literature (books, articles, etc.) where are you learning these from?

  3. Pingback: 【知识】6月6日 – 每日安全知识热点 – 安百科技

  4. Pingback: IT Security Weekend Catch Up – June 11, 2017 – BadCyber

  5. Pingback: 【技术分享】Windows内核利用之旅:熟悉HEVD(附视频演示) – 安百科技

  6. Pingback: Software-Security-Learning 学习资料 – 雨苁

  7. Pingback: Weekendowa Lektura 2017-06-11 – bierzcie i czytajcie | Zaufana Trzecia Strona

Leave a comment