This presentation details the defensive architecture of Scudo, the hardened heap allocator used in Android userland since version 11, and analyzes its susceptibility to advanced exploitation techniques. Scudo employs state-of-the-art mitigations, including strict segregation of allocation classes, introduction of allocation randomness via shuffling and guard pages, protection of chunk headers via CRC32 checksums using a global secret, and external storage of metadata. Despite these defenses, the talk emphasizes that Scudo's security properties are fundamentally compromised on Android due to the characteristics of the Zygote process. Zygote forks share initialization state, effectively neutralizing Scudo’s randomization mechanisms (seeds, security cookies, and memory maps) and allowing an attacking application to predict the heap layout of a victim application. Two recent techniques are explored: "Forged Commit Base" for arbitrary secondary allocation and a patched "Safe Unlink" attack targeting the local cache, along with a post-patch primitive that manipulates allocator metadata to restore arbitrary allocation capabilities. The conclusion notes that while current exploits exist, future widespread adoption of ARM Memory Tagging Extension (MTE) is expected to negate most memory corruption vulnerabilities.
Scudo Heap Implementation Exploits, Leaks, and Defenses
0:28 Scudo Context: Scudo is the hardened memory allocator used by Android since version 11, developed by the LLVM project and maintained by Google. It prioritizes performance and security by mitigating common heap-based vulnerabilities, contrasting sharply with the exploitation-prone ptmalloc2 (GLIBC).
1:51 ptmalloc2 Vulnerability Review: The legacy GLIBC allocator is deterministic; its primary security weakness is storing linked list pointers (metadata) directly within the user data section of freed chunks, making the list structure easily corruptible via buffer overflows to achieve arbitrary chunk allocation.
6:16 Scudo Security Foundations: Scudo's design incorporates four main security principles:
6:28 Allocation Separation: Chunks of different sizes are stored in dedicated memory regions to prevent heap massaging and limiting interaction between allocation classes.
6:45 Randomness: Allocation order is randomized, preventing adjacency prediction between consecutive allocations.
7:13 Checksummed Headers: Chunk headers are protected by a CRC32 checksum, computed using the header contents, the header address, and a global secret key, defending against small buffer overflows.
7:33 Off-Heap Metadata: Critical allocator metadata, notably free lists, are stored externally in libc rather than on the heap itself.
7:54 Scudo Allocator Structure: The allocator is split into a Primary Allocator (for small allocations) and a Secondary Allocator (for large, mmap-based allocations). Both use guard pages (memory with zero permissions) surrounding allocations to prevent large overflows from spilling into adjacent regions.
11:36 Primary Allocator Caching: The primary allocator uses an L1 local cache (in libc) and an L2 free list (in the region). Crucially, when refilling the cache, pointers are copied into transfer batches and then shuffled (13:28) before being inserted, injecting randomness into the allocation order.
13:53 ARM MTE Support: Scudo is designed to utilize the Memory Tagging Extension (MTE) on ARMv8.5+ architecture. MTE assigns tags to both pointers and memory blocks; Scudo re-tags memory upon free (14:53). A tag mismatch during memory access aborts the program, completely mitigating use-after-free (UAF) vulnerabilities and making the quarantine cache unnecessary (15:01).
16:19 Zygote Security Impact: The Android Zygote process severely undermines Scudo's randomization defenses. When Zygote forks to launch applications, the child processes share the same memory mappings, randomization seeds, and security cookies initialized by Zygote (18:26). This allows one app to function as an oracle, accurately predicting the heap layout and forging necessary checksums for exploitation in another app (19:14).
19:21 Exploitation Technique 1: Forged Commit Base: This technique targets the Secondary Allocator. By exploiting a buffer overflow to forge a fake secondary header and setting the commit base field to an arbitrary address, the attacker forces Scudo to cache the arbitrary address. The next secondary allocation returns a chunk at the chosen address (20:41).
21:17 Exploitation Technique 2: Safe Unlink (Patched): This complex attack utilized buffer overflow and a controlled free primitive to forge a secondary header, set up an unlinking attack by manipulating the internal linked list pointers (prev/next), and subsequently force the local cache to point to itself, achieving control over all subsequent allocations (23:35).
23:39 Post-Patch Exploitation Primitive: The Safe Unlink vulnerability was patched by implementing compact pointers (offsets relative to the region base) in the local cache, breaking the unlinking mechanism (24:09). However, a viable primitive remains: by assuming control over the local cache contents, an attacker can write a compact pointer value corresponding to the distance to the allocator's metadata structure in libc. The subsequent allocation overlaps this metadata, allowing the attacker to change the Region Base variable, thus re-establishing the ability to perform arbitrary chunk allocations (25:56).
26:16 Future Outlook: Outside of the Zygote context, Scudo remains robust. Within Android, the combination of exploitation techniques offers powerful primitives. However, the widespread adoption of ARM MTE is expected to effectively eliminate most memory corruption vulnerabilities (26:49).