
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: desligar o SELinux via CVE-2026-43499 (futex PI UAF).
ghost-hoock rodando em um Samsung A17
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 em um 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)
Depois:
$ getenforce
Permissive
getenforce retorna Permissive
O exploit tem como alvo o CVE-2026-43499 — um use-after-free na cadeia rt_mutex do PI (Priority Inheritance) do futex do kernel Linux. A cadeia no 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 precisamos depois.
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 | Propósito |
|---|---|---|
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 através de um socket AF_UNIX como SKB_SEND_SIZE = 2 * ORDER3_SIZE de sendmsg, para que os dados do skb caiam 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 seu pai — e o rb_set_parent(child, parent) resultante se torna:
*(uint64_t *)target = value | color
Para mode = 1 (Write 1), target = selinux_enforcing e value = base + 0x100, que codifica 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 ocorreu: o consumer atingiu o alvo durante select(), acordando-o mais cedo.
Este é um fork de mobilehackinglab/ghostlock-a17 (MIT). O seguinte foi 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 (somente subconjunto W1) |
| Offsets de struct em tempo de execução | 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 foi 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.