---
description: >-
  This blog details the methodology and the approach used against developing
  exploit code for CVE-2019-16098
---

# RTCore64.sys - CVE-2019-16098

This blog documents the approach towards reversing the driver and writing a working exploit for 64-bit Windows 10 OS.

Link to download the vulnerable RTCore64.sys driver: [https://drive.google.com/file/d/1zpIg87Ts5drcV7oG3Jvz7Ugf-d-iOVR5/view?usp=sharing](https://drive.google.com/file/d/1zpIg87Ts5drcV7oG3Jvz7Ugf-d-iOVR5/view?usp=sharing)

## IOCTL Codes

The initial approach to developing the exploit is to understand the driver. We hence reverse the driver and find the vulnerable code snippet which could later be used for developing the exploit. Since any driver code runs in ring0 of the OS, the approach to developing the exploit will be different from the one used during user mode exploit development.

We fire up IDA, load the RTCore64.sys driver into it and locate the DriverEntry method.

<figure><img src="https://1804885456-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIo3S6x9y21ea77Yw303B%2Fuploads%2Fb9t98G1Znyl4wgN5EAtW%2FScreenshot%202023-09-02%20at%2014.41.07.png?alt=media&#x26;token=83edd706-6737-4ba3-a61c-382e11be75f9" alt=""><figcaption><p>DriverEntry</p></figcaption></figure>

The `DriverEntry` has a `IoCreateSymbolicLink` which creates a symbolic link to the driver that would be accessible from user-space. The SymbolicLinkName here is `\\DosDevice\\RTCore64` which implies that the driver is accessible using `\\\\.\\RTCore64` . There is also no access control on incoming IRP requests which implies any low privilege user should be able to interact with the driver.&#x20;

Moving on to `DriverIoControl` function, we notice 3 important opcodes. The first opcode i.e. `IOCTL_READ_MSR` (0x80002030) allows reading from an MSR register. This is directly taken from SystemBuffer which implies an arbitrary read of MSR inside the kernel.&#x20;

<figure><img src="https://1804885456-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIo3S6x9y21ea77Yw303B%2Fuploads%2Ft8afIX6pPw0igOlxu503%2FScreenshot%202023-09-02%20at%2015.00.40.png?alt=media&#x26;token=ac040d71-2a33-4849-a47c-3050f949bf2e" alt=""><figcaption><p>IOCTL_READ_MSR</p></figcaption></figure>

Moving on to the next IOCTL code, the opcode `IOCTL_READ_DATA` (0x80002048) allows arbitrary reading by using the SystemBuffer offset. This implies a carefully planned input would allow an arbitrary read of 4 bytes inside the kernel.

<figure><img src="https://1804885456-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIo3S6x9y21ea77Yw303B%2Fuploads%2FaiqK47EuJLD6TMu4zgc2%2FScreenshot%202023-09-02%20at%2015.03.48.png?alt=media&#x26;token=31fdae74-20ca-41e5-88a2-b2163b6be888" alt=""><figcaption><p>IOCTL_READ_DATA</p></figcaption></figure>

Finally the last IOCTL code `IOCTL_WRITE_DATA` (0x8000204C) allows arbitrary write using the SystemBuffer offsets. This implies a carefully planned input would allow an arbitrary write of 4 bytes inside the kernel.

<figure><img src="https://1804885456-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIo3S6x9y21ea77Yw303B%2Fuploads%2FQK4Oyt1wwxUhBVe8j7nx%2FScreenshot%202023-09-02%20at%2015.07.18.png?alt=media&#x26;token=6062f0c5-f072-4586-9f96-62522871459e" alt=""><figcaption><p>IOCTL_WRITE_DATA</p></figcaption></figure>

## Windows Internals

Given that we now have a write and read inside the kernel, we would now want to perform privilege escalation using this vulnerablity. Any process created in a user mode has a corresponding `TOKEN` structure in the kernel which contains information about the privileges of that process. This `TOKEN` structure is found at an offset from `_EPROCESS`  structure. This structure is used to represent a process which includes details about the process like PIDs, pointer to `_KPROCESS`, Image Name, etc.&#x20;

Let's talk a bit about the `_EPROCESS` structure.

```
kd> dt !_eprocess
```

<figure><img src="https://1804885456-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIo3S6x9y21ea77Yw303B%2Fuploads%2F9HwVczQD4vlzL9X0C1LL%2FScreenshot%202023-09-02%20at%2015.16.15.png?alt=media&#x26;token=285f49c7-18b4-4975-b507-7391769057a2" alt=""><figcaption><p>_EPROCESS Structure</p></figcaption></figure>

This \_EPROCESS structure has multiple members and the ones we are interested in are UniqueProcessId, ActiveProcessLinks, and Token (offset 0x4b8 which may differ for different Windows versions)&#x20;

* UniqueProcessId: This tells us the PID of the process whose \_EPROCESS structure we are viewing right now. This would help us narrow down the processes we are interested in as part of the exploit.
* ActiveProcessLinks: This is a double-linked list that contains a Flink and Blink entry to the ActiveProcessLinks of the next process and previous process. This in short will help us traverse all the processes running in the OS from the kernel directly. To get the next process, we simply would need to get the value of ActiveProcessLinks member and subtract the offset 0x448 (offset of ActiveProcessLinks from `_EPROCESS`) from it to make it point to the start of `_EPROCESS` structure of the next process.
* Token: This member contains the pointer to the `_EX_FAST_REF` . To get the exact token address from this, we would and the least significant byte of the address with 0. ([This](https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/how-kernel-exploits-abuse-tokens-for-privilege-escalation) blog explains in detail why we do so)

Now that we know a bit about the kernel, let's talk about `PsInitialSystemProcess` variables. Windows has multiple global variables which are at a specific offset from the kernel's base address. This means leaking the base address of the kernel will allow us to get the address of any global variables in the kernel. One such variable is `PsInitialSystemProcess` which is a pointer to the `_EPROCESS` structure of the kernel. This will help us to get a starting point for traversing all the processes.

A full list of global variables of the kernel can be found [here](https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/mm64bitphysicaladdress). &#x20;

## Approach

We would create a cmd.exe application with low privileges. The end goal would be to swap `_TOKEN` this process with the `_TOKEN` system permissions.

&#x20;The approach would be first to read the 64-bit address present at `PsInitialSystemProcess` and add offsets to get value of UniqueProcessId and ActiveProcessLinks. We would then compare the UniqueProcessId with the one of the cmd.exe. If its not the same, we will move to the next process using ActiveProcessLinks linked-list and keep on checking pid and in case it matches, we will return with the address of the \_eprocess structure of the cmd.exe process.From here we can find the address of `_TOKEN`  structure for cmd.exe.

We would similarly fetch the address of `_TOKEN` structure for kernel and simply copy the kernel's token address into cmd.exe's token address. This would now mean that cmd.exe has system permissions.&#x20;

Exact exploit code: [https://github.com/0xDivyanshu-new/CVE-2019-16098/](https://github.com/0xDivyanshu-new/CVE-2019-16098/blob/main/exploit.cpp)

<figure><img src="https://1804885456-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIo3S6x9y21ea77Yw303B%2Fuploads%2FwdPG8lNc8rzHGk1DEiH5%2FScreenshot%202023-09-02%20at%2015.50.57.png?alt=media&#x26;token=7a190501-5c35-44e6-94c0-da9e56e39d7f" alt=""><figcaption><p>nt authority\system shell</p></figcaption></figure>

<figure><img src="https://1804885456-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIo3S6x9y21ea77Yw303B%2Fuploads%2FwNz8xNnUJL5vSFNA6b0Q%2FScreenshot%202023-09-02%20at%2015.52.03.png?alt=media&#x26;token=02bb6df2-e957-4df2-b22d-b904c728db08" alt=""><figcaption><p>Token copy</p></figcaption></figure>

This driver was also being used by BlackByte during their ransomware operations where using this driver they would nullify/remove the EDR's callback functions from the global Callbacks like ProcessCallbacks, HandleCallbacks, etc. This allowed them to create process without EDR hooking them and inspecting the process.
