
adb-driven KernelSU loader para Google Pixel de fábrica: R/W temporário do kernel via CVE-2026-43499 (GhostLock), seguido do carregamento tardio de um kernelsu.ko com assinatura correspondente para o KMI em execução. Independente de gerenciador.
Uma ferramenta orientada por adb que transforma um Google Pixel padrão, com bootloader bloqueado, em um dispositivo com root via KernelSU sem desbloquear o bootloader ou modificar a imagem de boot. A partir do host, ela executa um exploit de kernel no espaço de usuário sem privilégios no dispositivo para obter leitura/escrita temporária do kernel, usa essa primitiva para aplicar um credential de root e, em seguida, carrega tardiamente um módulo de kernel carregável do KernelSU (kernelsu.ko) no kernel GKI em execução e entrega o controle ao gerenciador KernelSU já instalado (KernelSU, KernelSU-Next, SukiSU ou outra variante). Ela tem como alvo Pixels Android 17 em kernels GKI 6.1 e 6.6 e conduz todo o fluxo via adb shell para sequenciamento determinístico e logs com timestamp.
O payload no lado do dispositivo é a cadeia LPE completa fornecida para CVE-2026-43499 ("GhostLock"), um use-after-free de pilha na herança de prioridade futex/rtmutex em kernel/locking/rtmutex.c. No caminho de rollback do requeue-PI, remove_waiter() limpa pi_blocked_on no requeuer (current) em vez do waiter real, deixando um ponteiro pendente para um slot de pilha do kernel liberado que continha um rt_mutex_waiter. O bug é acessível a partir de um processo comum sem privilégios:
owner, waiter, consumer) constroem uma cadeia PI; o waiter estaciona em FUTEX_WAIT_REQUEUE_PI, a thread principal dispara FUTEX_CMP_REQUEUE_PI, e um sched_setattr do consumer conduz o rollback.pselect()/select() controlado (ou uma rota TCP_ZEROCOPY_RECEIVE em alguns alvos 6.1) cujas palavras fd_set caem sobre a struct do waiter, escrevendo um rt_mutex_waiter plano forjado para que o ponteiro pendente percorra campos de rb-tree e lock controlados pelo atacante — uma primitiva de escrita de ponteiro único controlada.mm_struct// pulverizados.O estado de root e SELinux é então corrigido através da primitiva do pipe: o cred da tarefa filha root é zerado para uid/gid 0 com conjuntos completos de capabilities, seu osid/sid SELinux definido como SECINITSID_KERNEL, seccomp limpo, e selinux_state.enforcing definido como 0.
A cadeia de exploit, o oráculo KASLR e o canal lateral KernelSnitch originam-se da pesquisa IonStack Part II — GhostLock da NebuSec (o PoC NebuSec/CyberMeowfia, Apache-2.0), adaptada aqui para Pixel/aarch64. Consulte Atribuição e Licença.
Exatamente um estágio da cadeia pode causar pânico no kernel: a derivação do slide KASLR, que disputa uma página que espera ter reivindicado. Todos os outros estágios são seguros para repetição, e a base do texto do kernel é fixa durante a vida útil de uma única inicialização. O fluxo do host divide-se nessa propriedade:
KASLR_BASE em seu ambiente. A escrita do waiter forjado redireciona o ctl_table.data do sysctl random_table para um ponteiro conhecido do texto do kernel; ler /proc/sys/kernel/random/boot_id o vaza através de proc_do_uuid(), e subtrair o deslocamento da imagem produz _stext/a base KASLR. restore_slide_boot_id() repara o ctl_table.data corrompido. Como uma corrida perdida reinicia o dispositivo, uma espera-por-boot precede cada tentativa e uma verificação de vivacidade classifica um desaparecimento como pânico. Em caso de sucesso, o log do dispositivo emite slide-kaslr-ok pid=<pid> base=<hex>, e a base é fixada ao boot atual.KASLR_BASE=0x<base> exportado. Esse caminho nunca causa pânico e é repetido até id reportar através do su temporário.O dispositivo conectado é resolvido contra data/targets.json em tempo de execução; nada é codificado por dispositivo. Duas resoluções independentes ocorrem:
uname -r), seja da entrada de alvo correspondente ou derivado da string de release (ex.: android14-6.1).A reutilização de um payload entre muitos dispositivos decorre da estrutura GKI/KMI. Todo dispositivo no mesmo build GKI executa o vmlinux byte-por-byte idêntico, e os offsets de campos de struct (task_struct->cred, cred->uid, …) são congelados durante a vida de um ramo KMI pelo contrato de tipo KMI e pela aplicação de CRC MODVERSIONS. Endereços absolutos de símbolos do kernel, por outro lado, são decididos pelo linker por build ab<NNN>, então os offsets fixos do exploit pertencem a um vmlinux específico; imagens de kernel distintas exigem portanto payloads distintos mesmo quando seus KMIs coincidem. data/targets.json codifica exatamente isso: muitos dispositivos são deduplicados em um payload chaveado pela imagem do kernel, enquanto uma imagem de kernel diferente recebe o seu próprio.
O carregamento tardio do LKM requer um kernel GKI (5.10+) com suporte a módulos carregáveis e um .ko compatível com KMI. O módulo de kernel do KernelSU autentica seu gerenciador verificando o bloco de assinatura v2 do APK do gerenciador no kernel e comparando o SHA-256 do certificado de assinatura com um par KSU_EXPECTED_SIZE/KSU_EXPECTED_HASH compilado no .ko. O kernelsu.ko distribuído dentro de um release do gerenciador e o APK desse gerenciador compartilham portanto uma identidade de assinatura; um ksud incompatível carrega o driver mas nunca define o bit de autorização do gerenciador, deixando o dispositivo sem root utilizável.
O fluxo honra esse vínculo: ele resolve o caminho do APK do gerenciador instalado (pm path <pacote do gerenciador>), puxa o APK, extrai lib/arm64-v8a/libksud.so como o binário ksud, e se nenhum gerenciador estiver instalado ele aborta. Com o root temporário mantido, esse ksud é preparado como um executável de propriedade do root e invocado como ksud late-load --kmi <kmi> --package-name <pacote do gerenciador>. O late-load detecta o KMI atual, puxa "{kmi}_kernelsu.ko" de seus assets incorporados, realiza realocação manual de símbolos (resolvendo cada símbolo SHN_UNDEF contra /proc/kallsyms, reescrevendo entradas para SHN_ABS), e chama init_module(2) no buffer corrigido. Em seguida, executa o pipeline restante de inicialização que o init faria (instalar ksud, restorecon, carregar sepolicy.rule e perfis de root, executar scripts post-fs-data/stage, montar o overlay de módulos).
O late-load daemoniza e reaplica SELinux em seu filho bifurcado, que derruba o daemon de su temporário do exploit; a verificação portanto não deve passar pelo su. Em vez disso, o driver carregado é consultado diretamente através de sua superfície de syscalls, acessível a partir de um shell comum sem root: ksud debug version é consultado em loop e a versão do kernel reportada é analisada. Uma versão não vazia e não zero confirma que o driver está residente e respondendo. O caminho de instalação do driver é o mecanismo mágico reboot(2) → install-fd → KSU_IOCTL_GET_INFO (reboot(0xDEADBEEF, 0xCAFEBABE, 0, &fd) instala um fd anônimo [ksu_driver]; GET_INFO retorna {version, flags, features, uapi_version}), com o canal legado prctl(0xDEADBEEF, …) sondado como fallback. A mesma sondagem executada na inicialização contorna todo o fluxo quando o módulo já está residente para o boot atual.
O exploit prepara seu su temporário em /apex/com.android.virt/bin/su, em um tmpfs montado sobre esse diretório bin do apex dentro do namespace de montagem do adbd, porque esse diretório precede /system/bin no PATH do shell — então um su simples em adb shell alcança o temporário enquanto o fluxo está em execução. O late-load então derruba o daemon de su temporário (veja (e)) sem remover a sombra, o que deixa um adb shell su simples executando um cliente órfão que falha com su: connect daemon: Permission denied mesmo com o root funcionando, e deixa os binários reais do apex (crosvm, virtmgr, vm, …) ocultos. Uma vez que a verificação reporta um driver ativo, o fluxo desmonta o tmpfs de staging — através do próprio /system/bin/su do KernelSU, já que o daemon do exploit já se foi — remove o cliente su temporário, o socket e o log, e reporta qual su um simples agora resolve. É best-effort: em caso de falha, avisa com o comando manual em vez de falhar a execução, e um reboot limpa a montagem independentemente.
adb no host, com o dispositivo autorizado (depuração USB habilitada).kernelsu.ko incorporado.artifacts/exploits/ (veja Compilando payloads).# Um dispositivo no adb; gerenciador instalado; payloads compilados.
bin/pixel-ksu-root
O driver resolve o dispositivo contra data/targets.json, executa o fluxo KASLR em duas fases, carrega tardiamente o módulo através do ksud derivado do gerenciador e verifica via syscall do driver. Ele sai com código não zero se nenhum payload for resolvido para o dispositivo, se nenhum gerenciador estiver instalado, ou se a verificação nunca reportar um driver ativo.
KASLR_BASE=0x<hex> — passada ao payload do dispositivo durante a Fase B para repetir contra uma base fixa já derivada por boot. Não definida durante a Fase A para que o payload derive a base por si mesmo.ANDROID_NDK_HOME — caminho para o Android NDK, necessário apenas ao compilar payloads.API — nível de API Android para o toolchain NDK ao compilar payloads (padrão 35).pixel-ksu-root/
├── bin/ Ponto de entrada do driver do host (fluxo orientado por adb)
├── data/
│ └── targets.json Tabela de resolução dispositivo→payload e dispositivo→KMI
├── exploit/ Código-fonte do payload CVE-2026-43499 fornecido
│ ├── Makefile Build aarch64 NDK por alvo
│ ├── src/ Conjunto de fontes base android15-6.6
│ │ ├── main.c slide.c fops.c pipe.c root.c preload.c util.c
│ │ ├── su_daemon.c Helper de su temporário gerado pela cadeia
│ │ ├── kernelsnitch/ Headers do canal lateral de ocupação de hash futex
│ │ └── targets/ target.h por dispositivo+build (offsets do kernel)
│ └── src/61/ Conjunto de fontes android14-6.1 (slide61.c, rota TCP)
├── lib/ Funções compartilhadas/helper de shell do lado do host
├── scripts/
│ └── build-payloads.sh Compila e deduplica o conjunto de payloads
├── artifacts/
│ └── exploits/ Arquivos .so de payload compilados e deduplicados
└── docs/ Notas de design e análise
scripts/build-payloads.sh envolve o exploit/Makefile por alvo e emite o conjunto de payloads deduplicado nomeado em data/targets.json em artifacts/exploits/. Ele compila um .so por grupo de offsets único (a partir do alvo build_from desse grupo) em vez de um por dispositivo.
export ANDROID_NDK_HOME=/caminho/para/android-ndk # deve conter o toolchain NDK aarch64
scripts/build-payloads.sh # compila todos os payloads em data/targets.json
O Makefile seleciona o toolchain Clang NDK aarch64 de ANDROID_NDK_HOME e compila um único alvo por vez; API (padrão 35) escolhe o driver aarch64-linux-android<API>-clang. O conjunto de fontes é escolhido pela família de kernel — alvos android15-6.6 compilam a base src/, alvos android14-6.1 compilam src/61/ — e os offsets absolutos do kernel de cada alvo vêm de src/targets/<codename>-<build>/target.h. Para compilar um único alvo diretamente:
make -C exploit TARGET=husky-CP2A.260705.006
data/targets.json lista 19 entradas de dispositivo/build cobrindo 18 modelos Pixel (bluejay aparece em dois builds de firmware), agrupados em 5 payloads de offsets de kernel. A seleção é por imagem de kernel, então dispositivos que compartilham um vmlinux são deduplicados em um payload; uma imagem de kernel diferente recebe o seu próprio.
Dispositivos na imagem de kernel compartilhada android14-6.1-a (6.1.157-android14-11-gbd23337e42e7-ab14791245) abrangem as famílias Pixel 6/6 Pro/6a, 7/7 Pro/7a, 8/8 Pro e 9/9 Pro/9 Pro XL/9 Pro Fold; android14-6.1-b e android14-6.1-akita isolam modelos no mesmo KMI cujo build do kernel ou offsets diferem; android15-6.6 cobre a família Pixel 10.
NebuSec/CyberMeowfia sob Apache-2.0; descoberta creditada à ferramenta VEGA da NebuSec, divulgada em 2026-07-07.exploit/ fornecida adiciona offsets de alvo android14-6.1 e android15-6.6 e um daemon de carregamento tardio do KernelSU sobre o exploit da NebuSec; ela não carrega licença separada e herda os termos Apache-2.0 upstream.ksud e o modelo de autorização do gerenciador que esta ferramenta carrega tardiamente.O código-fonte fornecido sob exploit/ mantém sua licença upstream (Apache-2.0 para o exploit derivado da NebuSec). Este projeto é agnóstico quanto ao gerenciador: ele carrega tardiamente o gerenciador da variante KernelSU que estiver instalada e não tem como alvo nem empacota nenhum fork específico.
ksudinit_module e detecção de driverapk_sign.rssk_buffpipe_bufferashmem_miscs[0].fops com um file_operations forjado cujos slots apontam para funções reais do kernel compatíveis com o protótipo (configfs_bin_write_iter, configfs_read_iter, copy_splice_read, ashmem_ioctl, noop_llseek, …), de modo que o CFI de borda direta é satisfeito enquanto read/write/splice em um fd ashmem produzem um R/W restrito do kernel.pipe_buffer na página de slab vazada (page apontando para qualquer alvo via conversão vmemmap↔direct-map, ops = anon_pipe_buf_ops, PIPE_BUF_FLAG_CAN_MERGE), de modo que read()/write() simples no pipe movem bytes de e para endereços arbitrários do kernel — um R/W arbitrário estável do kernel.uid=0/proc/sys/kernel/random/boot_id ativo com o boot registrado no momento da captura; qualquer alteração descarta a base e retorna à Fase A. Um loop externo repete derivar→repetir entre reboots.adb shellumount| Payload | KMI | Compilado de | Dispositivos |
|---|
android14-6.1-a | android14-6.1 | bluejay-CP2A.260705.006 | oriole, raven, bluejay (CP2A), panther, cheetah, comet, shiba, husky, lynx, caiman |
android14-6.1-b | android14-6.1 | komodo-CP2A.260705.006 | komodo, tegu, tokay |
android14-6.1-akita | android14-6.1 | akita-CP2A.260805.005 | akita |
android14-6.1-cp1a | android14-6.1 | bluejay-CP1A.260405.005 | bluejay (CP1A) |
android15-6.6 | android15-6.6 | blazer-CP2A.260705.006 | frankel, blazer, mustang, rango |