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 0×00 on the ARM architecture is equivalent to INT 0×80 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:
|
1 2 3 |
/* socket syscall is defined in unistd.h for ARM as 0x119 */ #include "sys/socket.h" sockfd = socket(int socket_family, int socket_type, int protocol); |
|
1 2 3 |
/* socket syscall is defined in unistd.h for ARM as 0x119 */ #include "sys/socket.h" sockfd = socket(int socket_family, int socket_type, int protocol); |
Searching through the ARM ELF in IDA for system call invocations of socket yield the following…
|
1 2 3 4 |
.text:00008190 sub_8190 ; CODE XREF: sub_8030+2Ep .text:00008190 STMFD SP!, {R4,R7} .text:00008194 LDR R7, =0x119 .text:00008198 SVC 0 |
The above is definitely our socket system call. IDA denotes cross-references of the call which is reflected below:
|
1 2 3 4 |
.text:00008058 MOVS R0, #0x1F .text:0000805A MOVS R1, #2 .text:0000805C MOVS R2, #0 .text:0000805E BLX sub_8190 |
This means that the socket invocation looked something like this:
|
1 |
sockfd = socket(0x1F,0x2,0x0); |
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.
|
1 2 3 4 5 |
/* PF_BLUETOOTH = AF_BLUETOOTH = 31 == 0x1F SOCK_DGRAM = 2 = 0x2 */ sockfd = socket(PF_BLUETOOTH, SOCK_DGRAM, 0); |
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).
|
1 2 3 4 5 6 7 8 9 |
.text:0000805E BLX socket_syscall .text:00008062 MOVS R4, R0 .text:00008064 CMP R0, #0 .text:00008066 BLT loc_8084 .text:00008068 MOVS R3, #0x80 ; '' .text:0000806A MOVS R1, R5 .text:0000806C MOVS R2, #0 .text:0000806E LSLS R3, R3, #5 .text:00008070 BLX sub_8150 |
Jumping into sub_8150, we see that this is a system call. System call with the number 0xBB.
|
1 2 3 |
.text:00008150 STMFD SP!, {R4,R7} .text:00008154 MOV R7, #0xBB ; '' .text:00008158 SVC 0 |
Checking out unistd.h for ARM defines 0xBB as the sendfile syscall. The prototype looks as follows:
|
1 2 3 4 5 6 |
/* sendfile syscall is defined in unistd.h for ARM as 0xBB */ ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count); /* our particular invocation looked like this, where fdin is some arbitrary file*/ sendfile(sockfd, fdin, NULL, PAGE_SIZE); |
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.

November 16, 2012 at 3:39 PM
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.
November 16, 2012 at 3:52 PM
It’s also important to note that this vulnerability is not unique to Android. This vulnerability affects all Linux kernel based distributions.