
Полноценная цепочка эксплуатации для Android Chromium, объединяющая утечку информации CVE-2026-11057 и use-after-free CVE-2026-5281 для достижения перехвата vtable и произвольного выполнения кода.
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
Этот репозиторий объединяет CVE-2026-11057 (неинициализированная память образа глифа в Skia) и CVE-2026-5281 (use-after-free DeviceInfo в wire-сервере Dawn) в полную цепочку. Все модификации выполняются на стороне рендерера; код GPU-процесса не изменяется:
Утечка информации (CVE-2026-11057) — запуск неинициализированного буфера образа глифа через рендеринг текста на canvas, чтение указателей из кучи GPU-процесса, их проверка с помощью арифметических прогонов, вычисление базы пула PartitionAlloc и расчёт целевого адреса поддельного Server M = POOL + 0x202A08000 (калиброванное смещение).
CC-спрей — createBuffer(label="CCM:<m>:<n>") вызывает инъекцию 3000 незавершённых ChunkedCommands (бакет 10KB, remainingSize=1, удерживаются в mChunkedCommands wire-сервера). Полезная нагрузка заполняет область вокруг M поддельным Server, скомпонованным для Android arm64: vptr MutexProtected по адресу +0xB20, ChunkedCommandSerializer по адресам +0xB50/+0xB58, поддельные CommandSerializer/vtable по адресам +0xC00/+0xD00.
Триггер 5281 + захват — queue.writeBuffer(buffer, 0x414141, ...) сначала вызывает Unregister(Device) для освобождения 16-байтового DeviceInfo, затем инъектирует 16-байтовый захватывающий ChunkedCommand для повторного занятия этого слота и перезаписи info->server значением M, и наконец вызывает ошибку валидации с выходом за границы смещения.
Висячий обратный вызов → перехват vtable — обратный вызов необработанной ошибки разыменовывает info->server = M (поддельный Server): обнулённые мьютексы проходят проверку, выполнение достигает OnUncapturedError → SerializeCommand, и косвенный вызов через относительную vtable попадает в нашу поддельную vtable, давая pc = fake_vtable + контролируемое смещение int32, что демонстрируется как pc = 0x7641414141.
Примечание: Chromium для Android arm64 использует относительные vtable (
-fexperimental-relative-c++-abi-vtables); записи vtable представляют собой 32-битные относительные смещения, а не абсолютные указатели на функции, поэтому PoC использует контролируемое смещение int32 в качестве маркера в точке перехвата vtable.
На тестовом устройстве полная цепочка достигает краха с перехватом vtable почти со 100% успехом; этап утечки иногда может потребовать нескольких автоматических перезагрузок до успеха.
SkStrike.cpp.patch — триггер утечки CVE-2026-11057. FlattenGlyphsByType() переписывает полезную нагрузку strike так, что GPU-процесс создаёт SkGlyph с fImage == nullptr, в итоге читая неинициализированный буфер образа для утечки указателей кучи.Device.cpp.patch — CC-спрей wire-клиента Dawn. APICreateBuffer() разбирает метку CCM:, строит полезную нагрузку поддельного Server для Android arm64 и инъектирует N незавершённых ChunkedCommands, которые остаются живыми в GPU-процессе.ApiProcs.cpp.patch — триггер CVE-2026-5281 + захват. При магическом смещении 0x414141 в QueueWriteBuffer он вызывает Unregister(Device) для освобождения DeviceInfo, инъектирует 16-байтовый ChunkedCommand для повторного занятия слота и установки info->server = M, затем продолжает с выходом за границы смещения, чтобы вызвать висячий обратный вызов.exploit.html — оркестрация страницы: примитив утечки (groom/draw/readCell/verifyRuns/derivePoolBase), CC-спрей и автономный триггер exploit5281(); при неудаче он очищает состояние спрея и перезагружается, чтобы этапы не мешали друг другу.Этот ExP раскрывается только до шага перехвата vtable (контролируемая цель косвенного вызова, pc = base+0x41414141). Шаги от контролируемого перехода и далее, а также реализация RCE, сохраняются конфиденциальными и не включены в этот репозиторий.
Мой эксплойт отличается от подхода оригинального исследователя в одном ключевом моменте. Оригинальный эксплойт был достигнут путём модификации исходного кода PartitionAlloc в GPU-процессе для отключения рандомизации адресов. Этот подход подходит только для целей proof-of-concept. В отличие от него, мой эксплойт объединяет уязвимость раскрытия информации для обхода рандомизации адресов, обеспечивая полностью функциональный эксплойт, работающий в реальных условиях.
# 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 на ПК