
Адаптер эксплойта CVE-2026-43499 для MT6985 MediaTek Dimensity 9300 (vivo PD2241)
Вывод: потрачено 68M токенов, root не получен.
Причина: тайминговая атака KernelSnitch ненадёжна на MTK Dimensity 9200,CONFIG_PANIC_ON_OOPS=yне оставляет пространства для проб и ошибок.
В этой статье задокументирован весь процесс наступания на грабли, чтобы будущие исследователи могли их избежать.
| Проект | Значение |
|---|---|
| Устройство | vivo PD2241 (Dimensity 9200 / MT6985), Android 15 |
| Прошивка | PD2241_A_15.2.10.2.W10.V000L1 |
| Ядро | 5.15.178-android13-8-gfb31f5bdd612-dirty |
| Bootloader | Заблокирован (ro.boot.flash.locked=1) |
| SELinux | Enforcing |
| panic_on_oops | Включён → любой OOPS ядра = мгновенная перезагрузка |
| Эксплойт | CyberMeowfia — CVE-2026-43499 (IonStack) |
| Дерево исходников | android_15.0_kernel_MT6985 (5.15.178) — не совпадает с версией устройства (исходники — android15 GKI, устройство работает на android13 GKI) |
Извлечено из arch/arm64/include/asm/memory.h + include/linux/fs.h + android/abi_gki_aarch64.xml:
KIMAGE_TEXT_BASE = 0xffffffc008000000, VA_BITS=39, DIRECT_MAP=256GBlayout-offset-in-bits)iopoll (android13 GKI), IOCTL=0x48, open=0x68atomic_t usage = 4 байта, uid=0x04slab_cache=0x18Ключевое открытие: исходники — android15 GKI, устройство — android13 GKI — смещения task_struct отличаются на 0x40~0x88 байт, нельзя слепо копировать из исходников.
OTA zip (8.3GB)
→ payload.bin (8.2GB)
→ payload_dumper → boot.img (96MB, v4 header)
→ LZ4 распаковка → Image (50MB ARM64)
→ kallsyms-finder → 187810 символов
Символы извлечены из двух версий прошивки (15.2.7.6 / 15.2.10.2) — один и тот же символ отличается между версиями на 10KB~200KB, обязательно использовать правильную версию.
# В rt_mutex_adjust_pi:
LDR x21, [x19, #0x8b0] → pi_blocked_on = 0x8b0 (значение android15, не frankel)
Это подтвердило, что раскладка task_struct относится к ветке android15, а не к android13 от frankel.
NDK r29, make PROJECT=android_15.0_kernel_MT6985 → preload.so (150KB).
[+] preload starting pid=25414
[+] p0 profile ... все символы загружены корректно
[-] KernelSnitch mm_struct leak failed ← иногда этой строки нет (изредка успех)
[+] slide child context route=pselect ← запуск дочернего процесса для утечки KASLR slide
[panic ядра] ← rt_mutex_adjust_prio_chain+0x1b0
Инструкция краша (capstone):
ldar w8, [x27] ; x27 = waiter->lock (загружено из [x28, #0x38])
; значение x27 — мусор → нет отображения в таблице страниц → translation fault
; → die() → panic → перезагрузка
KernelSnitch — точка входа всего эксплойта — он утекает адрес mm_struct через тайминговые различия в futex-хеш-таблицах:
MT6985 имеет CONFIG_KASAN_HW_TAGS=y → ядро помечает slab-аллокации тегами MTE
указатель mm_struct несёт KASAN-тег → futex_hash вычисляется на основе tagged pointer
но bruteforce сканирует direct map (untagged адреса) → вычисленный hash не совпадает
даже с перебором MTE-тегов (0-14, всего 15 вариантов), на системе с VA_BITS=39
биты тега (bit56-59) перекрываются с битами знакового расширения → некоторые комбинации
тегов дают невалидные адреса → пропуск
На устройствах Pixel нет KASAN_HW_TAGS, этот механизм работает. На MTK — нет.
CONFIG_PANIC_ON_OOPS — убийцаPixel: OOPS ядра → dump_stack → продолжение работы → эксплойт может повторять попытки
MT6985: OOPS ядра → die() → panic() → мгновенная перезагрузка → нет пространства для проб
малейшее отклонение в π-цепочке = полный крах; на Pixel отклонение — просто "не вышло, попробуем другой набор адресов".
Плюс bootloader заблокирован (flash.locked=1) → нельзя прошить кастомное ядро, чтобы убрать эту опцию.
Дерево исходников: 5.15.178 android15 GKI
Устройство: 5.15.178-android13 (vivo vendor)
Хотя мажорные номера версий совпадают (5.15.178), ветки GKI разные (android13 vs android15), раскладка ключевых структур (task_struct/cred и др.) не совпадает. Многократное переключение между наборами смещений frankel/android15, в итоге зафиксировано только через дизассемблирование.
| Изменение | Цель | Результат |
|---|---|---|
THRESHOLD_MULT 10→5→3 | Снизить порог обнаружения коллизий | <5 слишком много ложных срабатываний |
APPENDED_FUTEXES 4096→8192 | Увеличить различия в hash-цепочках | Без эффекта |
REPEAT_MEASUREMENT/AVERAGE | Повысить точность выборки | Без эффекта |
MTE=1 | Перебор тегов в bruteforce | Медленнее, крашей меньше |
MM_STRUCT_SZ 0x500→0x400 | Исправить шаг mm_struct | Необходимо, в ABI фактически 992 байта |
IDENTITY_END 64GB→256GB | Расширить область сканирования | Слишком медленно (перебор MTE), всё равно не совпадает |
| TASK offsets: android15↔frankel | Зафиксировать правильные смещения | Дизассемблирование подтвердило android15 |
| FOPS offsets: android15↔frankel | android13 не имеет iopoll | Использован frankel |
В exploit/targets/android_15.0_kernel_MT6985/target.h:
| Категория | Достоверность | Способ проверки |
|---|---|---|
| Раскладка памяти | Верно | Вычисления из memory.h + проверка kallsyms _text |
| Смещения символов (22 шт.) | Верно | Извлечены из boot.img 15.2.10.2 |
| Смещения task_struct | Верно | ABI XML + дизассемблирование capstone (pi_blocked_on=0x8b0) |
| Смещения FOPS | Ошибочно (исправлено 2026-07-31) | Исходные значения скопированы из frankel (android13 без iopoll); в ABI дерева исходников фактически есть iopoll@0x30, ioctl=0x50, open=0x70 — см. VERIFICATION.md |
| Смещения CRED | Верно (проверено) | ABI XML: uid=0x04, securebits=0x24, caps=0x28, security=0x78 |
Ассемблер собирается, код запускается, проигрыш на последнем километре.
CONFIG_PANIC_ON_OOPS — либо прошить кастомное ядро (нужна разблокировка bootloader), либо найти устройство MT6985, где эта опция отключена по умолчанию/proc/self/pagemap — на этом устройстве ограничен (возвращает нули)/proc/mtk_*) — существуют, но требуют дальнейшего анализа