
Full-chain exploit for Android Chromium combining CVE-2026-11057 info leak and CVE-2026-5281 use-after-free to achieve vtable hijack and arbitrary code execution.
Pixel 7 Android 16 ARM64
commit 994c846bbd7a35241ba7c08158c13d66412a6993 (HEAD -> 146.0.7680.111, tag: 146.0.7680.111)
Author: Roger McFarlane <[email protected]>
Date: Mon Mar 9 12:48:07 2026 -0700
This repository combines CVE-2026-11057 (Skia uninitialized glyph image memory) and CVE-2026-5281 (Dawn wire server DeviceInfo use-after-free) into a full chain. All modifications are renderer-side; the GPU-process code is unchanged:
Info leak (CVE-2026-11057) — Trigger an uninitialized glyph image buffer via canvas text rendering, read back pointers from the GPU-process heap, validate them with arithmetic runs, derive the PartitionAlloc pool base, and compute the fake Server target M = POOL + 0x202A08000 (calibrated offset).
CC spray — createBuffer(label="CCM:<m>:<n>") triggers injection of 3000 incomplete ChunkedCommands (10KB bucket, remainingSize=1, kept alive in the wire server's mChunkedCommands). The payload fills the region around M with a fake Server laid out for Android arm64: MutexProtected vptr at +0xB20, ChunkedCommandSerializer at +0xB50/+0xB58, fake CommandSerializer/vtable at +0xC00/+0xD00.
5281 trigger + occupy — queue.writeBuffer(buffer, 0x414141, ...) first calls Unregister(Device) to free the 16-byte DeviceInfo, then injects a 16-byte occupy ChunkedCommand to reclaim that slot and overwrite info->server with M, and finally triggers a validation error with an out-of-bounds offset.
Dangling callback → vtable hijack — The uncaptured-error callback dereferences info->server = M (fake Server): the zeroed mutexes pass, execution reaches OnUncapturedError → SerializeCommand, and the relative-vtable indirect call hits our fake vtable, giving pc = fake_vtable + controlled int32 offset, demonstrated by pc = 0x7641414141.
Note: Android arm64 Chromium uses relative vtables (
-fexperimental-relative-c++-abi-vtables); vtable entries are 32-bit relative offsets rather than absolute function pointers, so the PoC uses a controlled int32 offset as the marker at the vtable-hijack point.
On the test device, the full chain reaches the vtable-hijack crash with an almost 100% success rate; the leak stage may occasionally require several automatic reloads before it succeeds.
SkStrike.cpp.patch — CVE-2026-11057 leak trigger. FlattenGlyphsByType() rewrites the strike payload so the GPU process creates a SkGlyph with fImage == nullptr, eventually reading an uninitialized image buffer to leak heap pointers.Device.cpp.patch — Dawn wire client CC spray. APICreateBuffer() parses the CCM: label, builds the Android arm64 fake Server payload, and injects N incomplete ChunkedCommands that stay alive in the GPU process.ApiProcs.cpp.patch — CVE-2026-5281 trigger + occupy. On the QueueWriteBuffer magic offset 0x414141, it calls Unregister(Device) to free DeviceInfo, injects a 16-byte ChunkedCommand to reclaim the slot and set info->server = M, then proceeds with an out-of-bounds offset to fire the dangling callback.exploit.html — Page orchestration: leak primitive (groom/draw/readCell/verifyRuns/derivePoolBase), CC spray, and a standalone exploit5281() trigger; on failure it cleans up the spray state and reloads so stages do not interfere.This ExP is only disclosed up to the vtable hijack step (controlled indirect call target, pc = base+0x41414141). The steps from the controlled jump onward, as well as the RCE implementation, are kept confidential and are not included in this repository.
My exploit differs from the original reporter’s approach in a key way. The original exploit was achieved by modifying the PartitionAlloc source code in the GPU process to disable address randomization. This approach is only suitable for proof-of-concept purposes. In contrast, my exploit combines an information disclosure vulnerability to bypass address randomization, enabling a fully functional exploit that works in real-world environments.
# Set build arguments here. See `gn help buildargs`.
is_official_build = true
is_debug = false
symbol_level = 0
v8_symbol_level = 0
blink_symbol_level = 0
is_component_build = false
proprietary_codecs = true
ffmpeg_branding = "Chrome"
dcheck_always_on =false
optimize_webui = true
android_static_analysis = "off"
target_os = "android"
target_cpu = "arm64"
# Disable PartitionAllocEventuallyZeroFreedMemory
disable_fieldtrial_testing_config = true
treat_warnings_as_errors = false
adb logcat | grep DEBUG on PC