关联漏洞
描述
Triggering and Analyzing Android Kernel Vulnerability CVE-2019-2215
介绍
# Android Kernel Vulnerability
# Overview
In November 2017 a [use-after-free](#uaf-vulnerability) bug to linux [kernel](#kernel) was detected by [syzkaller ](#syzkaller)system. In February 2018 this was patched in some linux kernels and android versions.
This fix was never included in [Android monthly security bulletins](#android-security-bulletins), so it was not patched in many newly released devices such as Pixel and Pixel2.
In September 2019 android was informed of the security implications of this bug by [Project Zero](#project-zero). Then android assigned [CVE-2019-2215](#cve-2019-2215) to this vulnerability to make it more formal and known.
CVE-2019-2215 is a use-after-free in binder.c that allows evaluation of [privilege](#privileged-and-unprivileged) (getting root access) from an android application. There is no need to user’s interaction, to [exploit](#exploitation) this vulnerability. It only requires the installation of a malicious local application.
Here we are going to introduce this android kernel vulnerability in more details and use this vulnerability to get root access (privilege escalation) of the whole android device.
We will use this Proof of Concept (PoC):
[https://github.com/cloudfuzz/android-kernel-exploitation](https://github.com/cloudfuzz/android-kernel-exploitation)
# Triggering vulnerability
We will first show you a way to trigger this vulnerability on an android emulator and make a kernel crash. Then to see how it would be dangerous, we continue using PoC to get root access in the simulated android device. Then we will analyze the kernel code to see what is the reason ([static](#static-analysis) and [dynamic analysis](#dynamic-analysis)).
After analysis, we will see how we got the root access. At last we see how this vulnerability is mitigated using patches.
To make a kernel crash by triggering this vulnerability we use this steps:
1. First of all, you need a linux OS with ['gdb'](#gdb) and 'python' installed.
2. Clone the PoC repository. ([https://github.com/cloudfuzz/android-kernel-exploitation](https://github.com/cloudfuzz/android-kernel-exploitation))
3. Install android emulator and [android NDK](#android-ndk) (By Installing Android Studio)
4. Clone the android Kernel source code. (The '[q-goldfish-android-goldfish-4.14-dev](#android-goldfish)' branch will be used)
5. This kernel is already patched, we change it to reintroduce the vulnerability in this kernel code.
6. Now We should build the kernel from source code. We build our kernel with [KASan](#the-kernel-address-sanitizer).
7. We boot the built kernel and run our emulator with it.
8. Then by using 'trigger.cpp' in the PoC repository, we trigger a crash (using ['adb'](#android-debug-bridge) cmd).
9. We will use 'root-me.py' in PoC and '[gdb](#gdb)' cmd to get root access in the emulated device.
Watch the following video:
[[video](https://drive.google.com/file/d/1I2hN8rQEAVNTU-qHhkbiJKZokuaZvnmh/view?usp=sharing)]
# Static analysis
In This (and next) section we are going to understand why the crash happens by using static and dynamic analysis.
Here we will analyze kernel code (static analysis) to understand the problem. In **crash_report.txt** there is the report from KASan which says this is a use-after-free bug. It means an object is allocated in heap (and we have a reference to it), then we freed the object from the heap and then we erroneously called it by a reference. In this report the stack trace of these three stages is printed.
If you remember from above, we used 'trigger.cpp' in the PoC.
Here is main code of trigger.cpp:
```
int main() {
//1
int fd, epfd;
//2
struct epoll_event event = {.events = EPOLLIN};
//3
fd = open("/dev/binder", O_RDONLY);
//4
epfd = epoll_create(1000);
//5
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
//6
ioctl(fd, BINDER_THREAD_EXIT, NULL);
}
```
Let's see what 'trigger.cpp' is doing.
In Android (like other Unix-based operating systems) we have some processes. Every program that we run creates one (or more) processes and these processes are handled by OS. OS may switch between them (multi-tasking) or terminate a process, etc. For security reasons, processes are isolated from each other by default.
In some cases, one process may need to exchange data with another process. It is called Interprocess Communication (IPC). There are several ways for processes to communicate in linux. Android introduced a specific IPC mechanism called **'Binder'**. Binder is a kernel driver to facilitate inter-process communication.
In android, IPC can be done by directly calling some kernel methods (most of them are in drivers/binder.c) or using high-level implementations (for example in java).

For using **binder**, we should open the kernel binder module. It is done using line 3 of trigger.cpp. Then we have a file descriptor pointer, By using this fd kernel initiator and recipients of the IPC can be identified.
All interactions with the driver will happen through a small set of **'ioctl**' commands (BINDER_THREAD_EXIT, BINDER_WRITE_READ, ...).
More about binder: [link1](https://www.nds.ruhr-uni-bochum.de/media/attachments/files/2012/03/binder.pdf), [link2](http://rts.lab.asu.edu/web_438/project_final/CSE_598_Android_Architecture_Binder.pdf)
In linux, we have a concept called '**event polling**'. '**epoll**' API is used when we want to monitor multiple file descriptors (file descriptors are what we have when opening a driver or working with IO, etc).
**epoll** is a kernel struct which has two important field.
* interest list = list of file descriptors we want to monitor.
* ready list = list of file descriptors that are ready for I/O.
Inorder to use event polling, we first create an epoll (line 4), then add or delete (EPOLL_CTL_ADD) an event (&event) associated with a file descriptor (**fd**) to our created epoll (**epfd**) by calling **epoll_ctl** method of kernel.
=> `epoll_event event` is an event, triggered when the associated file (fd) is available for read operation.
Now we understand what **trigger.cpp** is doing (no need to go deep!). It opens the **binder** module, creates an **epoll** to listen to it when it is ready. Then at line 6, we exit from the binder that we started in line 3.
#### Allocate:
By calling open(), we actually call **open_binder()** ( open() implementation in binder.c ), in open_binder(), a new **'binder_proc'** struct will be created and:
` fd->pricate_data = binder_proc`
By calling **epoll_create(),** a new epoll struct will be created and added to a queue structure.

By calling **epoll_ctrl(epdf, ADD, fd, event)**, a new **ep_item** is created, associate **fd **(file descriptor to listen) to this **ep_item**, and it is inserted to event_poll's** red black tree** ( a data structure in ep to save ep_items ). It also calls **ep_item_poll(),** this method handles the call back function association to ep_item.
It creates new **binder_thread** struct (**allocation happens here**), links it to the **binder_proc **(created above), then an **epoll_entry** struct is created, it has two lists,** epoll_entry->wait** and **epoll_entry->whead**, both of these lists have a pointer to **binder_thread** created before.
Then **epoll_entry** is linked to ep_item (**ep_item->pwqlist** is a list that contains this epoll_entry).

#### Free:
By calling **ioctl(fd, ...)**, **binder_proc** is accessed through fd->private_data, then the **binder_thread** struct will be **freed** from the memory.
#### Use:
When our current process exits, **epoll_ctl(epfd, DEL, fd, event)** will be called.
It calls **ep_remove(event_poll, ep_item)**. This method gets the **epoll_entry** from **ep_item->pwqlist**, then gets the wait list of **ep_item (ep_tem->wait)**, it is a linked list, it wants to remove one of this wait list's items.
It uses following code (pseudo code is used):
```
entry = wait->entry;
entry.next.prev = entry.prev;
entry.prev.next = entry.next;
```
Here **wait->entry** is a pointer to **binder_thread which was removed from memory!** So it is a use after free and causes a bug!!
* In above, we used codes **similar** to actual kernel codes, they may be different in some details. (**some cases binder_thread is used instead of binder_thread->wait**)
#### Summary:
We created an **event_poll** that has a **red_black_tree**, each node is an **ep_item** that has a field which is a list of **epoll_entry**, each epoll_entry has two pointers to **binder_thread** struct **(wait, whead).**
By calling **ioctl**() we **freed** the **binder_thread** from memory, Then while exiting this struct is accessed through a pointer which was still available!
# Dynamic analysis
Dynamic analysis is the testing and evaluation of a program by executing data in real-time ; to find errors in a program while it is running.
steps:
1. Build Android Kernel without [KASan](#the-kernel-address-sanitizer)
We build it without KASAN to monitor write and unlink operations and what is really happening after unlink operation.
2. Boot emulator with the newly built kernel
3. Launch emulator
4. Use [GDB](#gdb) to attach to the [QEMU](#qemu) instance
5. Build the vulnerability trigger and push it to the virtual device
6. Break in GDB
Load the custom python script([dynamic-analysis.py](https://github.com/cloudfuzz/android-kernel-exploitation/blob/master/gdb/dynamic-analysis.py) in repo) :
To trace function calls and dump the binder_thread structure chunk before and after it's freed. Also dump the same binder_thread structure before and after the unlink operation has been done.
In this file we first [delete all breakpoints](https://github.com/cloudfuzz/android-kernel-exploitation/blob/d408cc833df175c2410c2a0ef35cc27c23b80f3e/gdb/dynamic-analysis.py#L157) and the put 2 breakpoints(BP); The first symbol is “[binder_free_thread](https://github.com/cloudfuzz/android-kernel-exploitation/blob/d408cc833df175c2410c2a0ef35cc27c23b80f3e/gdb/dynamic-analysis.py#L164)” (will trace binder_free_thread function)before binder_thread is freed, stop function will be called; so parameters and symbol will be shown with ([gb.write(....)](https://github.com/cloudfuzz/android-kernel-exploitation/blob/d408cc833df175c2410c2a0ef35cc27c23b80f3e/gdb/dynamic-analysis.py#L96) ) and then the callback method(we set it [set_dump_binder_thread ](https://github.com/cloudfuzz/android-kernel-exploitation/blob/d408cc833df175c2410c2a0ef35cc27c23b80f3e/gdb/dynamic-analysis.py#L34)) will be called; In this function binder_thread_address will be set in our global variable and gdb.execute send any output produced by command to GDB’s standard output .
The second symbol is “[remove_wait_queue](https://github.com/cloudfuzz/android-kernel-exploitation/blob/d408cc833df175c2410c2a0ef35cc27c23b80f3e/gdb/dynamic-analysis.py#L175)”(will trace remove_wait_queue function) the parameters which we want to observe are "wq_head", "wq_entry" and for exit wait.c:52 breakpoint will be set. Their callback function is [dump_binder_thread ](https://github.com/cloudfuzz/android-kernel-exploitation/blob/d408cc833df175c2410c2a0ef35cc27c23b80f3e/gdb/dynamic-analysis.py#L42).These breakpoints will show what happens before and after the unlink operation.
7. launch adb shell and run the trigger PoC
**result**:
* first part of result:
<pre>
<b>binder_free_thread(thread=0xffff88800c18f200)(enter)</b>
0xffff88800c18f200: 0xffff88806793c000 0x0000000000000001
0xffff88800c18f210: 0x0000000000000000 0x0000000000000000
0xffff88800c18f220: 0xffff88800c18f220 0xffff88800c18f220
0xffff88800c18f230: 0x0000002000001b35 0x0000000000000001
0xffff88800c18f240: 0x0000000000000000 0xffff88800c18f248
0xffff88800c18f250: 0xffff88800c18f248 0x0000000000000000
0xffff88800c18f260: 0x0000000000000000 0x0000000000000000
0xffff88800c18f270: 0x0000000000000003 0x0000000000007201
0xffff88800c18f280: 0x0000000000000000 0x0000000000000000
0xffff88800c18f290: 0x0000000000000003 0x0000000000007201
<b>0xffff88800c18f2a0:</b> 0x0000000000000000 <b>0xffff88805c05cae0</b>
0xffff88800c18f2b0: 0xffff88805c05cae0 0x0000000000000000
0xffff88800c18f2c0: 0x0000000000000000 0x0000000000000000
0xffff88800c18f2d0: 0x0000000000000000 0x0000000000000000
0xffff88800c18f2e0: 0x0000000000000000 0x0000000000000000
0xffff88800c18f2f0: 0x0000000000000000 0x0000000000000000
0xffff88800c18f300: 0x0000000000000000 0x0000000000000000
0xffff88800c18f310: 0x0000000000000000 0x0000000000000000
0xffff88800c18f320: 0x0000000000000000 0x0000000000000000
0xffff88800c18f330: 0x0000000000000000 0x0000000000000000
0xffff88800c18f340: 0x0000000000000000 0x0000000000000000
0xffff88800c18f350: 0x0000000000000000 0x0000000000000000
0xffff88800c18f360: 0x0000000000000000 0x0000000000000000
0xffff88800c18f370: 0x0000000000000000 0x0000000000000000
0xffff88800c18f380: 0x0000000000000000 0x0000000000000001
0xffff88800c18f390: 0xffff88806d4bb200
</pre>
in our python code we had below codes:
```
gdb.write (
"{function}({param})(enter)\n".format(
function=self.function_name, param=params
)
)
```
and it is binder_free_thread function so the parameter for this function is a pointer to binder_thread:
```
static void binder_free_thread(struct binder_thread *thread)
{
[...]
kfree(thread);
}
```
in the result we had(`binder_free_thread(thread=0xffff88800c18f200)(enter)`).
lines after shows the result of execute , with below command we get offset of binder_thread.wait:
```
p offsetof(struct binder_thread, wait)
```
the result is 0xa0 and if we put wait.head instead of wait in command , the result will be 0xa8 and it contains `0xffff88805c05cae0`
```
0xffff88805c05cae0 is pointer to eppoll_entry->wait.entry which is of type struct list_head
```
* second part of result
<pre>
<b>remove_wait_queue(wq_head=0xffff88800c18f2a0, wq_entry=0xffff88805c05cac8)(enter)</b>
0xffff88800c18f200: 0xffff88800c18f600 0x0000000000000001
0xffff88800c18f210: 0x0000000000000000 0x0000000000000000
0xffff88800c18f220: 0xffff88800c18f220 0xffff88800c18f220
0xffff88800c18f230: 0x0000002000001b35 0x0000000000000001
0xffff88800c18f240: 0x0000000000000000 0xffff88800c18f248
0xffff88800c18f250: 0xffff88800c18f248 0x0000000000000000
0xffff88800c18f260: 0x0000000000000000 0x0000000000000000
0xffff88800c18f270: 0x0000000000000003 0x0000000000007201
0xffff88800c18f280: 0x0000000000000000 0x0000000000000000
0xffff88800c18f290: 0x0000000000000003 0x0000000000007201
0xffff88800c18f2a0: 0x0000000000000000 0xffff88805c05cae0
0xffff88800c18f2b0: 0xffff88805c05cae0 0x0000000000000000
0xffff88800c18f2c0: 0x0000000000000000 0x0000000000000000
0xffff88800c18f2d0: 0x0000000000000000 0x0000000000000000
0xffff88800c18f2e0: 0x0000000000000000 0x0000000000000000
0xffff88800c18f2f0: 0x0000000000000000 0x0000000000000000
0xffff88800c18f300: 0x0000000000000000 0x0000000000000000
0xffff88800c18f310: 0x0000000000000000 0x0000000000000000
0xffff88800c18f320: 0x0000000000000000 0x0000000000000000
0xffff88800c18f330: 0x0000000000000000 0x0000000000000000
0xffff88800c18f340: 0x0000000000000000 0x0000000000000000
0xffff88800c18f350: 0x0000000000000000 0x0000000000000000
0xffff88800c18f360: 0x0000000000000000 0x0000000000000000
0xffff88800c18f370: 0x0000000000000000 0x0000000000000000
0xffff88800c18f380: 0x0000000000000000 0x0000000000000001
0xffff88800c18f390: 0xffff88806d4bb200
</pre>
In `remove_wait_queue(wq_head=0xffff88800c18f2a0, wq_entry=0xffff88805c05cac8)(enter) ` , wq_head is binder_thread.wait address and wq_entry is data of wait.head .
after this unlink operation will occur.
* Third part of result:
<pre>
Breakpoint 3 at 0xffffffff802aa5be: file /home/ashfaq/workshop/android-4.14-dev/goldfish/kernel/sched/<b>wait.c</b>, line 53.
<b>remove_wait_queue_wait.c:52(exit)</b>
0xffff88800c18f200: 0xffff88800c18f600 0x0000000000000001
0xffff88800c18f210: 0x0000000000000000 0x0000000000000000
0xffff88800c18f220: 0xffff88800c18f220 0xffff88800c18f220
0xffff88800c18f230: 0x0000002000001b35 0x0000000000000001
0xffff88800c18f240: 0x0000000000000000 0xffff88800c18f248
0xffff88800c18f250: 0xffff88800c18f248 0x0000000000000000
0xffff88800c18f260: 0x0000000000000000 0x0000000000000000
0xffff88800c18f270: 0x0000000000000003 0x0000000000007201
0xffff88800c18f280: 0x0000000000000000 0x0000000000000000
0xffff88800c18f290: 0x0000000000000003 0x0000000000007201
<b>0xffff88800c18f2a0: 0x0000000000000000 0xffff88800c18f2a8
0xffff88800c18f2b0: 0xffff88800c18f2a8 0x0000000000000000</b>
0xffff88800c18f2c0: 0x0000000000000000 0x0000000000000000
0xffff88800c18f2d0: 0x0000000000000000 0x0000000000000000
0xffff88800c18f2e0: 0x0000000000000000 0x0000000000000000
0xffff88800c18f2f0: 0x0000000000000000 0x0000000000000000
0xffff88800c18f300: 0x0000000000000000 0x0000000000000000
0xffff88800c18f310: 0x0000000000000000 0x0000000000000000
0xffff88800c18f320: 0x0000000000000000 0x0000000000000000
0xffff88800c18f330: 0x0000000000000000 0x0000000000000000
0xffff88800c18f340: 0x0000000000000000 0x0000000000000000
0xffff88800c18f350: 0x0000000000000000 0x0000000000000000
0xffff88800c18f360: 0x0000000000000000 0x0000000000000000
0xffff88800c18f370: 0x0000000000000000 0x0000000000000000
0xffff88800c18f380: 0x0000000000000000 0x0000000000000001
0xffff88800c18f390: 0xffff88806d4bb200
</pre>
The highlighted part is because of [this](https://github.com/cloudfuzz/android-kernel-exploitation/blob/d408cc833df175c2410c2a0ef35cc27c23b80f3e/gdb/dynamic-analysis.py#L143) .The result is after unlink operation and we can see that for unlinking write the address (`0xffff88800c18f2a0 + 0x8`) to next and previous.
it means:
(pointer to `binder_thread->wait.head)` = `binder_thread->wait.head.next` = `binder_thread->wait.head.prev
# Exploiting the vulnerability
In this part we will show how we can use this bug to get root access.
Remember from above, we had a **binder_thread** struct, it was **freed** and **used** again by using a pointer. This is the code of **binder_thread** struct:
```
struct binder_thread {
struct binder_proc *proc;
struct rb_node rb_node;
struct list_head waiting_thread_node;
int pid;
int looper; /* only modified by this thread*/
bool looper_need_return; /* can be written by other thread*/
struct binder_transaction *transaction_stack;
struct list_head todo;
bool process_todo;
struct binder_error return_error;
struct binder_error reply_error;
wait_queue_head_t wait;
struct binder_stats stats;
atomic_t tmp_ref;
bool is_dead;
struct task_struct *task;
};
```
One of the fields is pointer **task_struct**, this struct has a field called **addr_limit**.
When we want to access an address in a process, it will be checked that the address is in **user-space** or not, if it was in **kernel-space** this access should be blocked. This checking is performed by comparing our address with **addr_limit**, if our address is less than **addr_limit** we have valid access.
**addr_limit** is actually separating user-space from kernel-space, so by changing this field in **task_struct** we have full access to kernel-space and we can do anything!
Here we have two steps to exploit:
1. finding the address of task_struct in kernel-space
2. changing the addr_limit in task_struct
#### Finding address of task_struct
In kernel, we can perform vectored I/O, it means that we can write or read more than one chunk of data to or from a file descriptor (file, socket, etc).
Vectored I/O is done by using **writev**, **readv**, **recvmsg** methods and **iovec** struct.
```
struct iovec
{
void __user *iov_base; /* BSD uses caddr_t (1003.1g requires void *) */ \
__kernel_size_t iov_len; /* Must be size_t (1003.1g) */ \
};
```
By using vectored I/O, we are doing I/O operations on an array of buffers (**iovec**). Each iovec has a pointer to a buffer (**iov_base**) and the size of buffer (**iov_len**).
[Vectored I/O](https://www.oreilly.com/library/view/linux-system-programming/0596009585/ch04.html)
For example, if we want to write an array of buffers into a file (fd), we call
**writev(fd, iovecStack, count)**
This method (like **readv** and **recvmsg**) first of all copies the iovec array (**iovecStack**) into kernel-space, then reads from these buffers and writes to fd.
* first part (copying iovec array into kernel-space) is similar in all three methods.
We can write and read buffers by using **pipe** , pipe is a struct that gives us two file descriptors, one for reading and one for writing. Pipe has a length in bytes, when one process writes to a pipe more than its length, pipe blocks in that process and waits for another process to read from that pipe (using the read file descriptor of that pipe).
[pipe in linux](https://man7.org/linux/man-pages/man2/pipe2.2.html)
Kernel tries to allocate memory (for a struct) according to its size. For example when we freed the **binder_thread** struct from memory (see static analysis), and after that if we have a struct with a size similar to binder_thread, it has a good chance to be allocated at the same place as the freed binder_thread.
First we create an iovecStack (array of iovec structs) with a size similar to binder_thread struct.
Then we free binder_thread from memory (see static analysis ‘free’ part)
Then we call writev() on this iovecStack. First part of this method, copies iovecStack in kernel space,
It is more likely that our iovecStack is allocated in the same place as freed binder_thread.
If we have enough iovec in iovecStack, memory at binder_thread’s location looks like this:

You see that **iovecStack[10].iov_base** and **iovecStack[10].iov_len** and **iovecStack[11].iov_base** will be at the same place as binder_thread’s **wait.lock**, **wait.head.next** and **wait.head.prev fields.**
In the **‘use’** part of the static analysis we saw that crash happened because of an access to wait part of **binder_thread** during the **unlinking** process (removing one item from a linked list).
During the unlinking process,**wait.head** is unlinked and **next** and **prev** fields of it would point to the wait field of binder_thread (unlinking).

Before the second part of writev happens (writing from iovecs to file), if we run the unlink process, **iovecStack[10].iov_len** and **iovecStack[11].iov_base** will be overwritten by a kernel address. Then after running rest of **writev**, when it wants to process **iovecStack[11]**, it reads from **iovecStack[11].iov_base** (=address of wait in binder_thread) length of **iovecStack[11].iov_len** of data.
if **iovecStack[11].iov_len** is enough, we read from the **wait** field to the **task_struct** field of the **binder_thread** so we would have the task_struct pointer.
* during the free part in static analysis, all of binder_thread wouldn’t free and parts like task_struct remain (we didn’t mention this for simplicity).
So to get the task_struct pointer we do the following:
1. we create a pipe and a stack of iovecs
2. create binder_thread and event_poll and link them (like what we did for static analysis)
3. fork a child process (now we have parent and child process)
4. In parent process call writev, it will import iovecStack in kernel-space (before continuing, block writev by writing junk data in pipe for example.)
5. in the child process, perform unlink. then read junk data so that the parent process is notified.
6. in the parent process, continue the rest of writev() and read from iovecs in kernel-space and write to file.
7. Now the file contains reading from wait to below, of the binder_thread in kernel-space (task_struct is 0xe8 bytes after the wait field).
see **exploit.cpp** in repo.
#### Changing addr_limit in task_struct
Now we have the address of task_struct in kernel-space (task_ptr).
Here we use **socket_pair** instead of **pipe**. And we use recvmsg() for reading from socket and writing to iovecs.
Steps:
1. first we do initializations like above
2. fork a child process
3. write some junks in **socket_pair**
4. in parent process call **recvmsg**():
1. it imports iovecs in kernel-space
2. it then reads the junks and waits (blocks) for receiving other data from the socket.
5. in child process perform **unlink** operation
6. in child process write this data in the socket:
```
static uint64_t finalSocketData[] = {
0x1, // iovecStack[10].iov_len
0x41414141, // iovecStack[11].iov_base
0x8 + 0x8 + 0x8 + 0x8, // iovecStack[11].iov_len
(uint64_t) ((uint8_t *) task_ptr + OFFSET_OF_ADDR_LIMIT_IN_TASK_STRUCT), // iovecStack[12].iov_base
0xFFFFFFFFFFFFFFFE // addr_limit value
};
```
After writing, **recvmsg**() starts reading from socket and writing into **iovecStack**. Because of junk, it has written up to **iovecStack**[10].
So it start writing **finalSocketData** in **iovecStack**[12], gets the address from **iovecStack[12].iov_base**, which is address of** wait in binder_thread **because of unlink operation, **iovecStack[12].iov_len** is set 4 bytes, so writes:
* 0x1 into iovecStack[10].iov_len
* 0x41414141 into iovecStack[11].iov_base
* 0x8 + 0x8 + 0x8 + 0x8 into iovecStack[11].iov_len
* **pointer_to_addr_limit** into iovecStack[12].iov_base
now 4 bytes have been written in **iovecStack[11]** so **recvmsg**() goes to **iovecStack[12]** to write rest of **finalSocketData**:
writes **0xFFFFFFFFFFFFFFFE **into the address in** iovecStack[12].iov_base** which is set to **pointer_to_addr_limit**, it means that recvmsg() changes addr_limit to 0xFFFFFFFFFFFFFFFE (not 0xFFFFFFFFFFFFFFFF because of some problems with arm64).
Now the user-space expands to approximately all kernel-space! (and can do anything!!)
# Patch
binder_poll() passes the thread->wait waitqueue that can be slept on for work. When a thread that uses epoll explicitly exits using BINDER_THREAD_EXIT, the waitqueue is freed, but it is <span style="text-decoration:underline;">never removed from the corresponding epoll data structure</span>. When the process subsequently exits, the epoll cleanup code tries to access the waitlist, which results in a use-after-free.
Prevent this by using POLLFREE when the thread exits.
we had this code:
```
static int binder_thread_release(struct binder_proc *proc, struct binder_thread *thread)
{
.
.
.
int active_transactions = 0;
.
.
.
binder_thread_dec_tmpref(thread);
return active_transactions;
}
```
These lines have been added to source code:
<pre>
static int binder_thread_release(struct binder_proc *proc,
binder_thread *thread)
{
.
.
.
/*
* If this thread used poll, make sure we remove the waitqueue
* from any epoll data structures holding it with POLLFREE.
* waitqueue_active() is safe to use here because we're holding
* the inner lock.
*/
if ((thread->looper & BINDER_LOOPER_STATE_POLL) && waitqueue_active(&thread->wait)) {
<b>wake_up_poll(&thread->wait, EPOLLHUP | POLLFREE);</b>
}
binder_inner_proc_unlock(thread->proc);
/*
* This is needed to avoid races between wake_up_poll() above and
* and ep_remove_waitqueue() called for other reasons (eg the epoll file
* descriptor being closed); ep_remove_waitqueue() holds an RCU read
* lock, so we can be sure it's done after calling synchronize_rcu().
*/
if (thread->looper & BINDER_LOOPER_STATE_POLL)
synchronize_rcu();
.
.
.
}
</pre>
see full code [here](https://code.woboq.org/linux/linux/drivers/android/binder.c.html#binder_thread_release)
# Definitions
### Kernel
operating system is a layer of software which is responsible for making all of the hardware work more efficiently and building an infrastructure on top of which application you use can work, its core is kernel.
### Exploitation
The idea behind exploitation is simple: software has bugs, and bugs make software misbehave or incorrectly perform a task it was designed to perform properly ; exploiting a bug means turning this misbehavior into an advantage for attackers.
The bugs which are exploitable are referred to as vulnerabilities.
### Privileged and Unprivileged
A privileged user or process is one who has full access of the device.
Most instruction set architectures provide at least two modes to execution:
privileged : All of the machine level instructions are accessible.
unprivileged : only a subset of the instruction are accessible.
### UAF vulnerability
Use-After-Free vulnerabilities are a type of [memory](https://www.webopedia.com/TERM/M/memory.html) [corruption](https://www.webopedia.com/TERM/C/corrupted.html) flaw that can be leveraged by [hackers](https://www.webopedia.com/TERM/H/hacker.html) to execute arbitrary code.
Use-After-Free specifically refers to the attempt to access memory after it has been freed, which can cause a program to <span style="text-decoration:underline;">crash</span> or, in the case of a Use-After-Free flaw, can potentially result in the <span style="text-decoration:underline;">execution of arbitrary code</span> or even<span style="text-decoration:underline;"> enable full [remote](https://www.webopedia.com/TERM/R/remote_access.html) code execution</span> capabilities.
### CVE-2019-2215
Is a use-after-free in Binder in the Android kernel.The bug is a local privilege escalation vulnerability that allows for a full compromise of a vulnerable device. If chained with a browser renderer exploit, this bug could fully compromise a device through a malicious website.It is reachable from inside the Chrome sandbox.
Note : It works on Pixel 1 and 2, but not Pixel 3 and 3a.see this [attack](https://blog.trendmicro.com/trendlabs-security-intelligence/first-active-attack-exploiting-cve-2019-2215-found-on-google-play-linked-to-sidewinder-apt-group/).
### Android Security Bulletins
Android security bulletins is a list published by google (monthly). This list contains fixed security vulnerabilities which affect android framework, linux kernel, etc.
### Syzkaller
Syzkaller is a kernel fuzzer. Fuzzing is a testing technique in which an automated program produces semi-random inputs for a target program to check if any bug is triggered. Fuzzing is especially useful in finding memory corruption bugs in C or C++ programs.
### Project Zero
Project Zero is a team of security analysts employed by Google tasked with finding zero-day vulnerabilities. A zero-day vulnerability is a vulnerability that is unknown by those who should fix it.
### GDB
GDB stands for GNU Project Debugger which is the most popular debugger for UNIX systems to debug C and C++ programs.GDB allows you to run the program up to a certain point, then stop and print out the values of certain variables at that point, or step through the program one line at a time and print out the values of each variable after executing each line.
You can connect your android emulator with a gdbserver for debugging.
### The Kernel Address Sanitizer
Kernel Address SANitizer (KASAN) is a dynamic memory error detector designed to find out-of-bound and use-after-free bugs. KASAN uses compile-time instrumentation to insert validity checks before every memory access, and therefore requires a compiler version that supports that. Kernel memory accesses can be checked against the shadow map to see if they are valid.
### QEMU
QEMU (Quick Emulator) is a free and open-source emulator that performs hardware virtualization.The Android Emulator is downstream from the QEMU emulator ; It adds support for booting Android devices, emulates typical Android hardware (OpenGL, GPS, GSM, Sensors) and a GUI interface. The android emulator extends qemu in various ways.
### Static Analysis
In static analysis, We use the source code of a program to find a bug or any problem. We don’t run the program.
### Dynamic Analysis
In Dynamic analysis, We analyze a program's behaviour while it is running. For example by providing special inputs.
### Android NDK
Android NDK is a tool-set that lets you run native code like C and C++ in an android device.
### Android Goldfish
Android goldfish kernel is used to run kernel code in an android emulator. It can be cloned and changed and then built to use in an emulator.
### Android Debug Bridge
ADB is a command-line tool. It helps to communicate with a running android device and get a shell from it. It can be used for debugging purposes.
# References
[https://googleprojectzero.blogspot.com/2019/11/bad-binder-android-in-wild-exploit.html](https://googleprojectzero.blogspot.com/2019/11/bad-binder-android-in-wild-exploit.html)
[https://nvd.nist.gov/vuln/detail/CVE-2019-2215](https://nvd.nist.gov/vuln/detail/CVE-2019-2215)
[https://www.tutorialspoint.com/gnu_debugger](https://www.tutorialspoint.com/gnu_debugger/what_is_gdb.htm)
[https://www.kernel.org/doc/html/latest/dev-tools/kasan.html](https://www.kernel.org/doc/html/latest/dev-tools/kasan.html)
[https://android.googlesource.com/kernel/goldfish/](https://android.googlesource.com/kernel/goldfish/)
[https://man7.org/linux/man-pages/man7/epoll.7.html](https://man7.org/linux/man-pages/man7/epoll.7.html)
https://www.scaler.com/topics/c/debugging-c-program/
...
文件快照
[4.0K] /data/pocs/4f4736faa956d3fc2a2176c8cdd7afa0d8b865e2
├── [5.2K] crash_report.txt
├── [ 25K] exploit.cpp
├── [4.0K] images
│ ├── [ 20K] binder.jpg
│ ├── [ 51K] even_poll.jpg
│ ├── [ 18K] event_poll.jpg
│ ├── [ 62K] iovecStack.jpg
│ └── [ 36K] unlink.jpg
├── [ 33K] README.md
└── [1.3K] trigger.cpp
1 directory, 9 files
备注
1. 建议优先通过来源进行访问。
2. 如果因为来源失效或无法访问,请发送邮箱到 f.jinxu#gmail.com 索取本地快照(把 # 换成 @)。
3. 神龙已为您对POC代码进行快照,为了长期维护,请考虑为本地POC付费,感谢您的支持。