
CVE-2026-43499 GhostLock root-эксплойт для Chromecast с Google TV (sabrina)
Root-эксплойт для Chromecast with Google TV (sabrina) через CVE-2026-43499 — use-after-free в подсистеме futex PI (priority inheritance) ядра Linux.
Получает root на устройстве с заблокированным загрузчиком под управлением Android 14 с ядром 5.15.170 (PGO+BOLT+LTO, clang 17).
GhostLock эксплуатирует ошибку в remove_waiter(), вызываемой из пути отката -EDEADLK в rt_mutex_start_proxy_lock(). Функция очищает current->pi_blocked_on (у requeuer'а, уже NULL) вместо поля задачи-ожидающего. Поле pi_blocked_on ожидающего никогда не очищается, оставляя висячий указатель на освобождённый rt_mutex_waiter в стеке ядра.
Последующий вызов sched_setattr запускает rt_mutex_adjust_pi -> rt_mutex_adjust_prio_chain, который следует по висячему указателю и проходит по PI-цепочке над контролируемыми атакующим данными на переиспользованной странице кучи — давая примитив произвольной записи через rb_erase.
| Этап | Техника |
|---|---|
| Утечка KASLR | perf_event_open с PERF_SAMPLE_IP (с ограничением по TID) — минимальный IP ядра находится в .entry.text (+0x10000), _text выровнен по 2MB, а сдвиг KASLR кратен 2MB, поэтому min_ip & ~0x1fffff точно восстанавливает runtime-адрес _text |
| Утечка task struct | perf_event_open с PERF_SAMPLE_REGS_INTR (с ограничением по TID, мода адресов linear-map) |
| Валидация таблицы символов | perf-сэмплы &init_user_ns из регистровых аргументов security_capable() во время шторма setpriority(-20) и сопоставление с kaslr_base + off_init_user_ns — доказывает, что раскладка .data/.bss работающего ядра совпадает с таблицей смещений до любой слепой записи |
| Утечка mm_struct | KernelSnitch — тайминговая побочная атака на коллизии хеша futex |
| Heap spray | Хореография отбрасывания SLUB (memfd-close, CPU-partial overflow) + возврат страниц порядка 2 через io_uring_setup(256) |
| Наложение стека | AF_UNIX SOCK_SEQPACKET sendmsg — move_addr_to_kernel копирует 128-байтный sockaddr в стек ядра, накладываясь на поля task/lock/prio висячего waiter'а |
| Триггер обхода | sched_setattr с монотонной лестницей nice (7 -> 14 -> 19) запускает обход PI-цепочки, по одному на раунд наложения |
| Примитив записи | rb_erase Case 1: `{pc=(TARGET-8) |
| Отключение SELinux (обход 0) | 8-байтный ноль в selinux_state очищает enforcing, checkreqprot, initialized и policycap[0..4] — avc_denied() никогда не запрещает (enforcing=0), а security_compute_av() замыкается на разрешение всего (!initialized), поэтому kernel-SID после подмены (u:r:kernel) перестаёт иметь значение |
| Подмена cred (обходы 1+2) | task->cred = fake_cred и task->real_cred = fake_cred (uid=0, caps FULL, самодостаточный фейковый user_namespace на странице спрея; обе записи обязательны — commit_creds() при exec вызывает BUG_ON, если cred != real_cred) |
| Батарея проверок root | пробы только через raw-сисколлы (uid/SELinux//proc/1//dev/kmsg/wifi/пакеты//data/data/kallsyms) в /data/local/tmp/.ghostlock_out |
| Root-шелл | execve("/system/bin/sh") — commit_creds в пути exec копирует fake_cred в чистый slab-credential; выполняется при условии, что все запланированные стирания выполнены |
rb_right = 0 выполняет одну чистую 8-байтовую запись нуля по произвольному адресу ядра (без побочной записи, без диких разыменований); обнуление первого qword selinux_state даёт двойной permissive (enforcing=0 + !initialized → allow-all), что разблокирует весь файловый ввод-вывод после получения root, несмотря на SID ядра&init_user_ns из живого пути security_capable() и прерывается до потока только с cred, если значение не совпадает с kaslr_base + off_init_user_nsucounts = NULL -- cap_capable совпадает на первой итерации, а inc_rlimit_ucounts завершается после одного цикла, устраняя необходимость утечки &init_user_nsPERF_SAMPLE_TID гарантируют, что учитываются только снимки регистров вызывающего потока, не позволяя горячим системным службам доминировать в голосовании режима5.15.170-android14-11-gf4a1f03072af (aarch64, PGO+BOLT+LTO, clang 17.0.2)CONFIG_FUTEX_PI=y, CONFIG_IO_URING=y, perf_event_paranoid=-1, SELinux в режиме enforcing, panic_on_oops=1, без пользовательских пространств имёнexport ANDROID_NDK_HOME=/path/to/android-ndk
make -j$(nproc)
Требуется Android NDK (протестировано с r27). Создаёт статически слинкованный бинарный файл aarch64.
adb push ghostlock /data/local/tmp/
adb shell "cd /data/local/tmp && ./ghostlock --cred"
Эксплойт занимает ~25 секунд (утечки KASLR/task/layout + heap spray + брутфорс KernelSnitch + три 3-секундных раунда наложения). При успехе он записывает root battery в /data/local/tmp/.ghostlock_out, маркер в /data/local/tmp/.ghostlock_root и выполняет exec /system/bin/sh с uid=0 в разрешительном ядре.
Параметры окружения:
GHOST_SELINUX=0 — пропустить запись SELinux, выполнить проверенный маршрут только для cred (режим 6)GHOST_SELINUX_FORCE=1 — включить запись SELinux, даже если проверка раскладки символов устройства не удаласьGHOST_EXEC=0 — записать battery и выполнить exit(99) вместо exec оболочкиCRED_ATTEMPTS=n — количество повторных попыток полного spray, когда запуск не достигает reclaim (по умолчанию 3)Этот эксплойт был портирован и разработан Claude Opus 4.6 (Anthropic) при участии GLM-5.3 (Z.ai) в качестве консультанта по эксплуатации ядра.
kernel/futex/, kernel/locking/rtmutex.c, lib/rbtree.cЭтот эксплойт опубликован в целях исследования безопасности и обучения. Он нацелен на устройство, принадлежащее исследователю. Не используйте его на устройствах, которые вам не принадлежат, или без разрешения.
MIT