This time, I am going to show you an android kernel exploitation coming from not checking the array index. You can find the source code of the vulnerable driver and the way to set up the environment here.
http://xdshao.com/2018/04/10/Android-exploitation-Kernel-stack-buffer-overflow/
Also, you can check my previous post about how to build the kernel module.

I’ve record a video here for those who prefer to watch videos.

Ok, first let’s read the source code of the misc driver and find out how can we use it to gain the root shell.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
CommandHandler handlers[] = {
{
.runHandler = &doNothingIntializer
},
{
.runHandler = &doNothingIntializer
}
};

long ai_ch_ioctl(struct file *filp,
unsigned int cmd,
unsigned long arg)
{
unsigned int handler_index = arg;

switch (cmd) {
case RUN_COMMAND_HANDLER:
handlers[handler_index].runHandler();
break;
default :
printk("Unknown ioctl cmd: %d", cmd);
return -1;
}
return 0;
}

I don’t want to mention the misc driver here but you can find a ton of tutorials about it online. What we need to know here is that when we call

1
handlers[handler_index].runHandler();

The code doesn’t check the handler_index here. So if we give a big handler_index, it will point to somewhere else. And if we can control that place, we can let it execute the code we want. Take a look at this picture.

The basic idea is this. If we input 0, it will call handles[0] which is the function at the 0xC00C1000. But if we call handler[4], it will call the function at 0xDEADBEEF.
Then, let’s take a look at the solution code.
In that code, we see the function get_symbol which will get the address of the function commit_creds and prepare_kernel_cred. The idea is that if we call commit_creds(prepare_kernel_cred) it will get the root id. And the main function is really simple. At first, we mmap a piece of memory and put the shellcode at the end of that part. And the data before the shellcode are zero which is nop. It means do nothing. So when we jump to that nop part, the shellcode will get executed at the end. This sounds really simple, right? But when we actually take an action, it’s much more difficult.

So, let’s make our hands dirty.

Before we start, I made a few changes to the driver source code to make our life easier. Take a look at this.

This will print out the handlers[index] content. So we know where it points to and we can figure out the mmap base address. If we don’t do this, we have to try index starting from 0 until we find a reasonable address where we can mmap and execute the code. I tried to write a shell script to automatically get that index but failed. Because you have to mess up with those threading and subprocess stuff. It’s really annoying and it takes a long time. If you want to have a try, just go ahead.

So let’s start the emulator and insert the driver. We can see it prints out these address.

You have to be careful here. In my machine, I can’t mmap around 0 or c0000000. The latter one is the starting address of the kernel. And you can’t choose the address like 72657a69. That’s because the last bit of the address is 1 and it will go to execute the thumb instruction. But our shell code is arm instruction. So you have to choose the even address. Here I choose the address 165. So my solution code looks like this.

Before you compile you code using ndk. Please do this in adb shell.

1
2
“chmod 777 /dev/array_index”
“echo 0 > /proc/sys/kernel/kptr_restrict”

So you can write to the device as a normal user. I have to mention that it’s much more difficult to exploit in real word. Here as I just want to explain this vulnerability so we can do this to make our life easier.
At the end, run the binary and get the root shell.