Mobile Defense Blog

Protect and Manage Your Mobile Life

Analysis of Null Pointer Dereference in the Android Kernel

The basic idea of a NULL pointer dereference is when an object is unexpectantly NULL and subsequently executed. We can strategically map code at the address NULL and this will subsequently be executed. Two very good articles were written by Nelson Elhage of Ksplice/Oracle explaining a NULL pointer and a NULL pointer dereference.

CVE-2009-2692 found that sock ops is not properly initialized under some circumstances. As you can see, there are no NULL checks before dereferencing sendpage in the ops structure. This trivially allows code to be mapped to NULL that will be executed in kernel context when the NULL pointer is dereferenced.

Below is a implementation of CVE-2009 that was included in the ASROOT malware that was release for Android.

A few things need to happen to make this possible:

1. Create a socket with uninitialized op structure
2. Trigger the NULL pointer dereference in sock_sendpage
3. Code mapped to NULL to be executed in Ring0

The asroot binary is statically linked. We will not be able to see any of the included libraries. We will start by investigating the system calls. As we are walking through the exploit, the ARM assembly will be inline with the C code. A little background of ARM interrupts and system calls will be necessary.

ARM SystemCalls

The system call number is placed in register R7. The parameters to the system call are placed in registers R0-R6. The system call is invoked by creating a software exception via the SuperVisor call which switches contexts into kernel mode. SVC is a SuperVisor call instruction that causes a software interrupt (as a note, SVC used to be named SWI). SVC 0x00 on the ARM architecture is equivalent to INT 0x80 on the x86 architecture. The ARM system call table for the Linux Kernel can be found here for reference.

Create a socket with uninitialized op structure. Looking up the system call number and prototype of socket yields the following:


Searching through the ARM ELF in IDA for system call invocations of socket yield the following…

The above is definitely our socket system call. IDA denotes cross-references of the call which is reflected below:

This means that the socket invocation looked something like this:

From here on out, we can rename sub_8190 to socket_syscall, but we need to know what parameters it was called with. Looking into the socket.h file we can determine what parameters were actually passed to the system call.

Files in Linux can define their set of operations by using the file_operations struct as a template. This is a layer of abstraction to allow subsystems to operate on files without worrying about the file types. A socket, being a file, has its defined set of file operations. When this is done, it defines its .sendpage operation as the sock_sendpage function that we would like to exploit. This means that we just have to chose any arbitrary file system operation which eventually calls the .sendpage op on our file descriptor (which in this case is our exploitable socket).

Jumping into sub_8150, we see that this is a system call. System call with the number 0xBB.

Checking out unistd.h for ARM defines 0xBB as the sendfile syscall. The prototype looks as follows:

We now know that this particular exploit chose to use sendfile to trigger the sock_sendpage NULL pointer dereference. Now, the payload needs to be triggered. This is done by mapping code to address space at NULL (0). This particular exploit accomplishes that by creating a section in the ELF and telling the loader to put that at the address 0. When the above triggers the NULL pointer dereference in the Kernel, the code mapped to address 0 (NULL) gets executed.

According to Duo Security, over 50% of Android device are vulnerable to some type of local privilege escalation. This is especially frustrating when you consider that whole classes of exploits like the above can be mitigated by including sets of security patches to the Linux kernel, namely GRSEC.


  • 1

    For the record, the Linux kernel already has defenses against null pointer dereference attacks. In most modern Linux distributions, /proc/sys/vm/mmap_min_addr is set to 4096. Userspace programs cannot allocate memory below that point.

    Android takes this one step further, and sets /proc/sys/vm/mmap_min_addr to 32768. Userspace programs cannot, under any circumstances, allocate memory below 32K.

    While GRSEC may have some good patches, the example that you provided is already mitigated in the mainline Linux kernel.

  • 2

    It’s also important to note that this vulnerability is not unique to Android. This vulnerability affects all Linux kernel based distributions.