Skip to content
HN On Hacker News ↗

OEMpocalypse Now: A Generic Exploitation Strategy from Android untrusted app to root

▲ 104 points • 63 comments • by negura • 4w ago • HN discussion ↗

Pangram verdict · v3.3

We believe that this text is a mix of AI and human-written content.

80 %

AI likelihood · overall

AI
16% human-written 84% AI-generated
SEGMENTS · HUMAN 2 of 5
SEGMENTS · AI 2 of 5
WORD COUNT 1,484
PEAK AI % 86% · §4
Analyzed
Sep 14
backend: pangram/v3.3
Segments scanned
5 windows
avg 297 words each
Distribution
16 / 84%
human / AI fraction
Verdict
AI
Pangram v3.3

Article text · 1,484 words · 5 segments analyzed

Human AI-generated
§1 Human · 18%

Part 1 of a series that takes an unprivileged Android app to root on Samsung, Xiaomi, and Oppo/OnePlus/Realme devices, with a single strategy. On Android, every third-party app runs in a sandboxed context called untrusted_app. If you ask five offensive security researchers how to go from this context to root, you will most likely get five different strategies, and each has its own trade-offs. Here, I describe the one I took and why, measured against three properties I use as a yardstick throughout: Reliability, i.e., succeed at near-100 percent regardless of which defenses or customizations are enabled. Portability, i.e., run with minimal per-kernel-version, per-OEM, per-chipset, or per-device adjustment. Universality, i.e., cover as many devices as possible. The core idea is to only target code written by Original Equipment Manufacturers (OEMs) such as Samsung and Xiaomi. Specifically, I target a page Use-After-Free (UAF) in an OEM-specific kernel driver, using an OEM-specific sandbox escape to reach that driver where the OEM's SELinux policy requires one.

§2 AI · 79%

In brief, a page UAF is a bug that leaves a live reference to a physical memory page after the kernel has freed it, and a sandbox escape is any bug that lets code cross from a lower-privilege process into a higher-privilege one.

§3 Human · 24%

I then instantiated the strategy three times, once per major Android OEM, finding multiple vulnerabilities along the way and yielding chains that cover all Samsung flagship devices (at least the Galaxy S23 through S26 series and the recent Z series), a large share of Xiaomi mid-range to flagship devices, and recent Oppo, OnePlus, and Realme flagship devices. Figure 1: The Samsung chain end to end on a bootloader-locked Galaxy S26 Ultra. Recordings for the other four tested devices are in The Three Chains at a Glance. This first post explains the reasoning behind the strategy and compares it with two alternatives. The subsequent posts will provide technical details of these chains. TL;DR Strategy: Exploit a page UAF in an OEM-specific kernel driver.

§4 AI · 86%

Where the OEM's SELinux policy gates that driver behind a privileged domain, reach it via an OEM-specific sandbox escape first. Every bug involved lives in OEM code rather than in generic Linux or chipset drivers. Why: A page UAF reached from the right context can provide a stable physical page-level primitive that is largely independent of kernel version, slab hardening, KASLR, CFI, and the specific phone model, which is exactly what a weaponized chain needs. Coverage: Three instantiations of the same strategy cover all Samsung flagship devices (at least the Galaxy S23 through S26 series and the recent Z series), most Xiaomi mid-range to flagship devices, and recent Oppo, OnePlus, and Realme flagships. Overview The Android Kernel Attack Surface Generic Linux Kernel Vulnerabilities Chipset Drivers Reachable from untrusted_app The OEMpocalypse Strategy Stage 1: Escaping the Sandbox Stage 2: The Page UAF The Trade-off The Three Chains at a Glance Conclusion The Android Kernel Attack Surface Before getting to OEM-specific code, it is worth stepping back and looking at what an unprivileged app can actually reach in the kernel, and what each kind of kernel bug offers for a weaponized exploit. The list is short due to three layered mechanisms: standard Unix Discretionary Access Control (DAC) via Android's per-app UID model, SELinux as the Mandatory Access Control (MAC) layer on top, and system call filtering via seccomp. Every third-party app on Android runs under its own UID, in the untrusted_app SELinux domain (or one of its per-API-level variants such as untrusted_app_32), and behind a seccomp filter. DAC gates access via file ownership and mode bits, the SELinux policy restricts which device nodes, sockets, and filesystem paths the domain may access, and seccomp prevents the app from invoking a subset of system calls. The combined effect is that most of the kernel, and in particular most of the driver stack, is simply not reachable without first moving to a more privileged domain. In other words, DAC, SELinux, and seccomp together define the kernel attack surface you get to start from. To make this concrete, let's take a current flagship line, e.g., the Samsung Galaxy S26 family, and enumerate the kernel attack surface from an untrusted_app process. On these devices, the list is roughly the following: There is the core Linux syscall surface that survives seccomp, e.g., memory management, the VFS layer, futexes, timers, basic networking, and Binder via /dev/binder. There are the shared-memory and DMA buffer (DMA-BUF) heap nodes that the graphics and other media stacks depend on. There is the GPU driver, exposed as /dev/kgsl-3d0 on the Snapdragon models (e.g., the S26 Ultra) and via the DRM render node on the Exynos/Xclipse models (e.g., the base S26 in some regions), because apps need direct GPU access for rendering. And then there is a tail of nodes that are less universal, e.g., DSP or NPU interfaces on some chipsets, plus a handful of OEM-specific additions whose presence depends entirely on what the OEM's own SELinux policy chose to expose. I should state that this enumeration is not meant to be exhaustive or authoritative. It is what I found on the devices in front of me, and the exact set may shift between models and Android releases. Looking at that list, the entries loosely group by who wrote the code behind them: upstream Linux and Android Common Kernel code, chipset drivers, and OEM-specific code. Before walking through these groups, let me state what we are actually optimizing for. In my view, an ideal weaponized untrusted_app-to-root exploit has three properties. First, it is reliable, i.e., it succeeds at near-100 percent regardless of which defenses and customizations a given OEM or kernel version happens to enable. Second, it is portable, i.e., it runs with as few per-kernel-version, per-OEM, per-chipset, or per-device adjustments as possible. Third, it is universal, i.e., a single exploit covers as many devices as possible. Put together, this is the "one exploit to rule them all" ideal. Most likely no real exploit fully reaches it, but it is the bar I measure candidates against. With that bar in mind, generic Linux bugs are the natural place to start, since on paper they are the most universal thing on the list. Generic Linux Kernel Vulnerabilities The first group is the code that every Android device shares: the upstream Linux kernel and the Android Common Kernel (ACK) additions on top of it. Bugs here, e.g., in io_uring, the network stack, or the memory-management core, are attractive precisely because they are OEM- and chipset-agnostic. A single exploit should, in principle, work on a Samsung, a Pixel, and a Xiaomi running the same kernel branch. IonStack is a good example of this line of work, and I think it is genuinely impressive: a single memory-management bug in generic code, pushed all the way to root across devices from multiple OEMs. It shows how much reach a well-chosen generic bug can have. In practice, that reach is also where most of the engineering effort goes, for two reasons. The first reason is the typical nature of the bugs. The accessible core Linux code has seen years of external auditing, and the resulting primitive from what remains exploitable is usually constrained and lives at the slab level, e.g., a UAF of one specific object type, a limited Out-Of-Bounds (OOB) write, or a race window that has to be won, rather than direct control over a physical page. Turning that into root typically means heap grooming, cross-cache tricks, an information leak, and other exploit techniques. All of those can be sensitive to the target's system. The second reason is that a single exploit has to stay stable across three axes of variation at once. There is the kernel version, since OEMs ship anything from 5.15 to 6.12 on modern devices. There is the OEM layer, since OEMs may apply their own patches to the kernel source (e.g., Samsung's RKP and KDP) and may ship their own runtime configuration (e.g., whether mem-cg is enabled). And there is the individual device, since even two phones from the same OEM on the same kernel branch ship different hardware, e.g., RAM size and core count, which shifts allocator behavior such as per-CPU page list watermarks and the number of per-CPU slab caches to account for. Each axis can add another dimension of per-target work, where the fully weaponized exploit gradually turns into a matrix of special cases that needs ongoing attention as monthly bulletins land. Put together, the two reasons point in the same direction: the bug itself is present almost everywhere, but turning it into a reliable and portable exploit is where the work concentrates. With that picture in mind, let's look at the next group.

§5 Mixed · 31%

Chipset Drivers Reachable from untrusted_app The second group is drivers for chipset components that are, for performance reasons, exposed directly to unprivileged apps, such as the GPU (Mali, Adreno KGSL, PowerVR) and the Digital Signal Processor (DSP) or Neural Processing Unit (NPU).