
GhostLock reduzido a uma única primitiva: SELinux desativado no Galaxy A17 (BZA5) via futex PI UAF (CVE-2026-43499). Sem root, sem patch de credenciais, sem rwforge.
Um fork minimalista do GhostLock que mantém apenas uma primitiva: desativar o SELinux via CVE-2026-43499 (futex PI UAF).
ghost-hoock é um fork reduzido do exploit GhostLock de Mobile Hacking Lab, reduzido a uma única primitiva:
Uma escrita restrita via futex PI UAF ->
selinux_enforcing = 0.
Sem root, sem sobrescrita de cred, sem canal rwforge, sem UMH, sem configfs. Apenas o mínimo necessário para colocar o SELinux em modo permissivo no kernel vulnerável.
Exemplo de saída num 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)
Em seguida:
$ getenforce
Permissive
getenforce retorna Permissive
O exploit tem como alvo o CVE-2026-43499 — um use-after-free na cadeia rt_mutex de PI (Priority Inheritance) do futex do kernel Linux. A cadeia em ghost-hoock tem quatro etapas:
Canal lateral de temporização contra a tabela hash de futex do kernel. Martelamos FUTEX_WAKE_PRIVATE em um conjunto de futexes do espaço do usuário, medimos os deltas de rdtsc e correlacionamos colisões de buckets de hash. Isso recupera o endereço do nosso próprio mm_struct — a base da página de spray que precisaremos mais tarde.
Esta é a técnica KernelSnitch, retirada literalmente do exploit original.
Alocamos uma grande página slab order-3 e, em seguida, a organizamos com o layout de objeto falso usado pela rota PI:
| Offset | Objeto | Finalidade |
|---|---|---|
0x0E80 | fake_lock | rt_mutex falso |
0x0F80 | fake_fops | Tabela file_operations falsa |
0x1180 | fake_w0 | rt_mutex_waiter falso usado como árvore alvo |
0x1240 | fake_right | Nó direito falso da rb-tree — é daqui que vem o valor da escrita |
0x1260 | fake_left | Nó esquerdo falso da rb-tree |
0x1280 | fake_task | task_struct falso |
A página inteira é enviada por um socket AF_UNIX como SKB_SEND_SIZE = 2 * ORDER3_SIZE de sendmsg, de modo que os dados do skb caem na nossa página vazada. Em seguida, liberamos na ordem controlada para que nossa página acabe em um slab parcial por CPU que possamos recuperar.
Três threads:
FUTEX_WAIT_REQUEUE_PI em f_wait, com alvo em f_pi_target.FUTEX_LOCK_PI em f_pi_target e depois em f_pi_chain.sched_setattr(tid, SCHED_BATCH, nice=19) no TID do waiter, o que dispara rt_mutex_setprio() e força o kernel a percorrer a árvore PI falsa.Uma quarta chamada da thread principal — FUTEX_CMP_REQUEUE_PI(1, f_pi_target) — inicia o requeue. Dentro do kernel, rb_erase() é executado contra nossa árvore falsa.
pselect() / select() copia o fd_set do usuário para a pilha do kernel e depois o percorre. Organizamos os bitmaps do fd_set de modo que as palavras que o kernel trata como ponteiros de rb-tree caiam em fake_right e em seu pai — e o resultante rb_set_parent(child, parent) se torna:
*(uint64_t *)target = value | color
Para mode = 1 (Write 1), target = selinux_enforcing e value = base + 0x100, que é codificado como byte0 = 0, byte1 = 1. O kernel escreve 0 em selinux_enforcing[0] — o SELinux agora está permissivo.
ret = 6 (em vez do padrão 9) confirma que a escrita foi efetivada: o consumidor atingiu o alvo durante select(), acordando-o antecipadamente.
Este é um fork de mobilehackinglab/ghostlock-a17 (MIT). O seguinte é retirado 1:1 do exploit upstream:
| Componente | Arquivo | Notas |
|---|---|---|
| KernelSnitch | src/kernelsnitch/* | vazamento de mm_struct via temporização do hash de futex |
| Heap spray | src/spray.c | layout de objeto falso, prepare_skb_payload, prepare_kernel_page |
| Rota PI + pselect | src/route.c | prepare_pselect_fdsets, do_pselect_fake_lock_route, consumer_thread, waiter_thread, owner_thread |
| Offsets BZA5 | include/offsets_bza5.h | Tabela de símbolos extraída de 6.12.23-android16-5-abA175FXXS5BZD2-4k |
| Cabeçalho de alvo BZA5 | include/target.h | Layout de endereços, offsets de payload (apenas subconjunto W1) |
| Offsets de struct em runtime | include/runtime_struct_offsets.h | Macros _RSO() para campos de task_struct |
O código auxiliar (macros pr_*, SYSCHK, pin_to_core, set_limit, set_unbuffer) também é mantido como está do original.
O GhostLock original obtém root completo no A17: instala um canal físico de R/W rwforge, faz patch de cred / real_cred, executa um helper UMH com credenciais de init, captura logs e mais. No ghost-hoock, tudo após a primeira escrita restrita foi removido.