Target: iPhone 12+ (A14 Bionic and newer) Component: WebKit (ANGLE) & XNU Kernel Vector: Mobile Safari / WebGL (Remote)
The target environment is the “Liquid Glass” iteration of iOS (v26). This update introduced a visual overhaul requiring substantial rewrites in Core Animation and WebKit to support advanced GPU compositing. These changes significantly expanded the attack surface.
Defensively, the A14 Bionic is a fortress. It enforces:
The Context: This is a CVSS 10.0 chain. It requires No Authentication, Zero User Interaction (beyond viewing a texture), and results in Permanent Root Access. It falls into the “Targeted Surveillance” category, hoarded by Private Sector Offensive Actors (PSOAs).
Root Cause: A Triad of Failures—Logic Error (ANGLE), Use-After-Free (WebKit), and Integer Overflow (Kernel).
Component: ANGLE (OpenGL to Metal Translator)
The first flaw lies in how ANGLE handles texture uploads. To upload a texture, the engine calculates memory allocation based on the user’s Unpack Settings.
GL_UNPACK_IMAGE_HEIGHT to 1. The allocator calculates a tiny buffer size (Row_Pitch * 1).Component: WebKit LayerPool The second flaw is a Use-After-Free in the rendering lifecycle.
LayerPool (a recycling bin for graphical objects) on the temporary Stack instead of the persistent Heap.Render() returns, destroying the stack frame and freeing the LayerPool. However, a background thread still holds a reference to it. When the thread wakes up to use the pool, it accesses invalid memory.Component: XNU Kernel The final flaw allows the attacker to escape the sandbox via an Integer Overflow.
Deadline = Current Time + User Duration.0xFFFFFFF0), the addition wraps around the 32-bit limit, resulting in a tiny number. The Kernel uses this tiny value to allocate a buffer, but writes full-sized data into it, corrupting the Process Credentials (struct proc).The victim visits a malicious website containing crafted WebGL content (potentially via a 1-click link). WebKit passes drawing commands to ANGLE. The exploit sets GL_UNPACK_IMAGE_HEIGHT to 1, tricking the allocator.
The texture upload writes past the end of the allocated buffer, corrupting internal WebKit structures. This provides the “Write Primitive” needed to manipulate the Heap layout.
The attacker triggers the LayerPool Use-After-Free and sprays the heap to fill the freed slot with fake data.
The compromised WebContent process issues a syscall to the Kernel (via IOKit), passing a crafted “User Duration” integer that is near the 32-bit maximum.
The integer overflows inside the Kernel. The resulting Heap Overflow overwrites the struct proc of the calling process, manually changing the User ID (UID) to 0 (Root). The device is now fully compromised.
The remediation requires decoupling buffer sizing from user input, enforcing heap allocation for shared objects, and modernizing integer types.
Part 1: ANGLE (Logic Fix)
// Commit 95a32cb37: Decoupled buffer sizing from user input.
// OLD: Used pixelsDepthPitch (derived from user input)
// NEW: Calculates size based on ACTUAL texture dimensions
size_t requiredSize = actualWidth * actualHeight * bytesPerPixel;
if (allocatedSize < requiredSize) {
// SECURITY: Block the upload or reallocate to match reality.
ReallocateBuffer(requiredSize);
}
Part 2: WebKit (Lifetime Fix)
// Bug 302502: Enforced Heap Allocation.
// Mandates LayerPool creation on the heap via smart pointers.
// Vulnerable:
// LayerPool pool; // Stack allocated, dies when function returns.
// Fixed:
RefPtr<LayerPool> pool = LayerPool::create(); // Heap allocated.
// The memory remains valid as long as 'pool' is referenced by any thread.
Part 3: Kernel (Integer Fix)
// XNU Update: 64-bit Adoption.
// Transition from 32-bit to 64-bit for time values to prevent wrapping.
// Vulnerable:
// uint32_t deadline = current_time + user_duration; // Overflows if > 2^32
// Fixed:
uint64_t deadline = current_time + user_duration; // Safe capacity.
Handle the Edge-Cases.
RefPtr, shared_ptr).