Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2026-43499-S26 — Exploit do kernel Android GKI 6.12 para CVE-2026-43499, encadeando um bug de rollback do rt_mutex com sobrescrita de pilha do pselect para obter root em dispositivos Samsung e Pixel. | Kitploit
Ferramentas/GitHubGitHub/sammyenigma/cve-2026-43499-s26
Segurança AndroidEscalada de PrivilégiosForensia de MemóriaMecanismos de PersistênciaExploraçãoEngenharia ReversaShellcodePós-ExploraçãoSegurança Móvel

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar
Desenvolvimento de Payloads
Exploração de Binários
GitHubsammyenigma/cve-2026-43499-s26

CVE-2026-43499-S26

Exploit do kernel Android GKI 6.12 para CVE-2026-43499, encadeando um bug de rollback do rt_mutex com sobrescrita de pilha do pselect para obter root em dispositivos Samsung e Pixel.

Ver Repositório
1há 22 diasAinda não revisado

CVE-2026-43499 — exploit de kernel Android GKI 6.12 pselect / configfs

Um exploit de kernel Linux para a linha GKI 6.12 do Android (dispositivos Samsung e Pixel) visando CVE-2026-43499: um rollback defeituoso de remove_waiter() em rt_mutex_start_proxy_lock() que usa current em vez de waiter::task, deixando o pi_blocked_on de um waiter apontando para seu rt_mutex_waiter na pilha do kernel (posteriormente removido). Combinado com uma sobrescrita de pilha de fd_set via pselect() e uma travessia de rb_tree falsa controlada por sched_setattr do consumidor, isso produz uma primitiva de escrita determinística no kernel e root completo.

O exploit roda como uma biblioteca compartilhada LD_PRELOAD (preload.so) e instala um daemon su root mais o wallpaper como artefatos pós-root.

Status: desenvolvimento ativo. O alvo m1q (ZF1) é o foco do bring-up. O bootstrap pipei tmp_page-uname é a rota ativa atual: a travessia está comprovadamente limpa no dispositivo (build #33: regiões rt_mutex semeadas por filho) e a varredura completa de 168 candidatos está em andamento. A rota configfs CFI é um beco sem saída no m1q (Rust ashmem — sem slot fops injetável) e permanece a rota para alvos C-ashmem. Os offsets por alvo variam por dispositivo — verifique contra o binário real do kernel antes de confiar neles.

Primitiva vulnerável (CVE-2026-43499)

Quando FUTEX_CMP_REQUEUE_PI reenfileira um waiter e a travessia da cadeia de detecção de deadlock do requeue retorna -EDEADLK, __rt_mutex_start_proxy_lock() faz rollback via remove_waiter() (rtmutex.c:1535). O rollback desenfileira corretamente o waiter da árvore de espera, mas limpa o pi_blocked_on do chamador do requeue em vez de waiter->task->pi_blocked_on. O pi_blocked_on do WAITER permanece pendente em seu rt_mutex_waiter na pilha, que é removido quando o futex expira.

Após o timeout, a região da pilha do kernel do waiter é reutilizada: core_sys_select() copia os três fd_sets para esse buffer de pilha (o caminho de pilha nfds < 344 no ZF1). Um array de palavras fd_set forjado re-materializa um rt_mutex_waiter falso / task falsa / rt_mutex falso (com uma raiz rb_tree cujos ponteiros são controlados). Uma thread consumidora então chama sched_setattr_tid(waiter) → rt_mutex_adjust_pi() → rb_erase_cached, o que produz uma escrita em endereço arbitrário de um valor controlado.

A primitiva inteira requer o ciclo da cadeia PI: o owner mantém f_pi_target e também bloqueia em f_pi_chain (mantido pelo waiter), então a travessia da cadeia atinge owner → chain → waiter → target → owner e falha com -EDEADLK. Remover o ciclo faz o requeue retornar sucesso com efeito zero no kernel.

Cadeia de exploração

  1. Slide (KASLR) — vazar a base do kernel. Duas rotas:
    • tracefs sched_blocked_reason (primária no m1q, comprovada no dispositivo): o PC de retorno salvo de um kworker bloqueado (stack_trace_save_tsk) é lido do ring buffer e comparado contra o offset worker_thread compilado. Roda antes de qualquer palavra da rota boot_id ser usada, para que os alvos de escrita de data-alias (data_addr() = alias p0 + slide_p0_offset) permaneçam corretos em um slide diferente de zero.
    • rota boot_id pselect (fallback, independente de slide): uma escrita pselect planta SLIDE_LOGGERS_0_1 nos dados do sysctl boot_id via um alias de mapa linear; o valor vazado reconstrói stext. Estacionada no m1q: seu alvo W1 (SLIDE_RANDOM_BOOT_ID_DATA_OFF, RAM física baixa) tem gravabilidade dependente de página — o slide move o alvo através de limites de página a cada boot, então ~6/7 execuções falham. Só é exercitada explicitamente via SLIDE_FORCE_BOOTID (teste de mecanismo) com tree_pc/tree_left redirecionados para a página sprayada.
  2. KernelSnitch — spray de objetos do tamanho de mm_struct e uso de uma colisão de hash de futex para localizar um no heap, vazando um endereço de página do heap do kernel usado como base do spray de objetos falsos. Os waiters são desanexados (não unidos) na limpeza para evitar o OOM da pilha do kernel que derrubou builds antes da #26.

Forma da travessia (m1q, comprovada no dispositivo)

As palavras fd_set do pselect re-materializam um rt_mutex_waiter falso na pilha do kernel do waiter; o sched_setattr_tid(waiter) de uma thread consumidora o percorre via rt_mutex_adjust_pi(). Mapa de palavras do ZF1 (verificado por disasm): PSELECT_WAITER_WORD_SHIFT = 0, palavra 12 = task (@+0x50), palavra 13 = lock (@+0x58), palavra 14 = wake_state (@+0x60 = 3). Com um rt_mutex falso semeado, a travessia completa limpa: as W1 *(tree_left)=tree_pc / W2 *(tree_pc&~3+8)=tree_left do rb_erase de [7] são as únicas escritas no kernel; [11] (setprio / dequeue_pi) e o wake de [9] são ambos ignorados (waiter falso local em prio 100 mantém o nó da pilha fora do top-waiter). Toda conclusão que entra na travessia desde a build #19 sobrevive no dispositivo; os crashes determinísticos anteriores eram um erro de posicionamento de payload de 8 bytes (SKB_DATA_DELTA), não uma falha do corpo da travessia.

Estágio 3 (ponte pgd-swap, apenas estático/build)

Fase opcional STAGE3=1 que faz fork de um filho ponte, troca seu mm->pgd por uma tabela de páginas falsa preparada (3 níveis: PGD→L1→L2, folha de loop RX + folha de pilha RW) e deixa o filho executar um blob de assembly somente de registradores que faz patch de seu próprio cred através de uma janela de varredura física de 2MB — phys R/W de toda a RAM sem as primitivas configfs/pipe. Atualmente conectado apenas ao alvo m1q e não foi executado no dispositivo.

Alvos suportados

41 alvos em src/targets/<codename>-<build>/, cada um exigindo no mínimo um target.h (offsets de símbolos do kernel, KIMAGE_TEXT_BASE, base do direct-map, offsets de structs). Alvos Pixel (comet, tokay, tegu, caiman, komodo, frankel, mustang, rango, stallion, blazer) sobrescrevem fontes compartilhadas; alvos Samsung (m1q-*) adicionam lógica específica do dispositivo.

root@kitploit:~
make list-projects   # full list

Os layouts de structs do kernel diferem por dispositivo — os offsets devem ser verificados contra o binário real do kernel / kallsyms para cada alvo.

Build

Saída: build/<PROJECT>/bin/preload.so (biblioteca compartilhada carregada via LD_PRELOAD).

root@kitploit:~
# Default project
CC=clang make

# Specific device target (default: blazer-CP2A.260605.012)
CC=clang make PROJECT=m1q-BP4A.251205.006

# Without CC=clang: uses NDK if found ($NDK_ROOT / $ANDROID_NDK_HOME /
# $ANDROID_NDK_ROOT), otherwise host clang + Android sysroot
make PROJECT=m1q-BP4A.251205.006

# Show build configuration
make info

# Clean
make clean

O build embute um binário PIE su_daemon (src/su_daemon.c, compilado para build/embed/su_daemon_aarch64_pie) e assets/wallpaper.webp em preload.so via src/su_blob.S / src/wallpaper_blob.S.

Executar no dispositivo

Envie as saídas do build para /data/local/tmp, então execute o exploit sob LD_PRELOAD. Execute SEM tee em um alvo de bring-up ativo — tee faz buffer de seu próprio stdio e perde o final do log em um panic do kernel (o stdout do exploit é sem buffer, então redirecionar direto para um arquivo preserva cada linha):

root@kitploit:~
adb push build/m1q-BP4A.251205.006/bin/preload.so /data/local/tmp/preload.so
adb push build/embed/su_daemon_aarch64_pie /data/local/tmp/su_daemon_aarch64_pie
adb shell "chmod 755 /data/local/tmp/preload.so /data/local/tmp/su_daemon_aarch64_pie"

adb shell "LD_PRELOAD=/data/local/tmp/preload.so \
  /data/local/tmp/su_daemon_aarch64_pie \
  > /data/local/tmp/output.log 2>&1"

Em caso de sucesso, o daemon escuta em /data/local/tmp/temp_su.sock e su é instalado em /apex/com.android.virt/bin.

Varredura pipei do m1q (bring-up atual)

tools/run_pipei.sh envia o binário, verifica seu sha256 no dispositivo (um preload.so obsoleto produz vereditos silenciosamente ilegíveis — a escada uma vez executou uma build < #22 cujo formato de log difere), então executa a varredura completa de 168 candidatos em blocos de 3x56 para que um panic perca no máximo um bloco (~10 min). O stdout do exploit transmite diretamente pelo adb (sem redirecionamento no dispositivo — um tee do lado do dispositivo perde o final em caso de panic); uma cópia de cada bloco no lado do host é mantida em /tmp/m1q_pipei_chunk{1..3}_stream.log:

root@kitploit:~
sh tools/run_pipei.sh

PASS = qualquer bloco mostra tmp_page uname changed + after uname='CatOS. PIPEI_CHILD_REGIONS deve ser definido pelo menos com o tamanho do bloco, ou as regiões rt_mutex por filho permanecem não semeadas e a varredura entra em panic no candidato 0. A cobertura completa também requer TMP_UNAME_PIPEI_SPLIT_ORDER_PAGES=1 — sem isso, apenas uma página do bloco mm liberado de 8 páginas é varrida. run_pipei_slim.sh é a variante de diagnóstico de candidato único; run_m1q_ladder.sh é a bateria de travessia/isolamento (oráculo B16-2, mecanismo de slide, etapas de varredura).

Configuração em tempo de execução (variáveis de ambiente)

Layout do projeto

root@kitploit:~
exploit/
  Makefile
  assets/                     # wallpaper.webp (embedded post-root artifact)
  src/
    main.c                    # run_exploit() entry point (shared)
    preload.c                 # LD_PRELOAD constructor, su/wallpaper install
    util.c                    # addr translation, page prep, configfs helpers
    pipe.c                    # pipe-based physical read/write
    root.c                    # cred patching / task walk
    slide.c                   # KASLR slide (tracefs + boot_id routes)
    fops.c                    # pselect corruption + fops manipulation
    faketables.c              # stage-3 fake page-table builders (3-level)
    stage3.c                  # pgd-swap bridge child (no-op unless configured)
    stage3_loop.S, stage3_poll.S  # bridge blob + enter trampoline
    su_daemon.c               # root daemon (compiled as separate PIE)
    su_blob.S, wallpaper_blob.S   # embedded binaries
    offset.h                  # pulls per-target target.h via -DTARGET_CONFIG_H
    kernelsnitch/             # mm_struct futex-hash leak library
    targets/<codename>-<build>/    # per-device target.h + optional overrides
poc/poc.c                     # standalone POC (separate from the exploit)

Os alvos sobrescrevem fontes compartilhadas através do mecanismo pick_src do Makefile: se src/targets/<PROJECT>/<file>.c existir, ele é usado em vez do src/<file>.c compartilhado; caso contrário, a versão compartilhada é usada.

Notas e armadilhas

  • Nunca remova o ciclo da cadeia PI. A primitiva de escrita depende de o requeue falhar com -EDEADLK (errno 35). Um ciclo ausente aparece como success=1 com efeito zero no kernel.
  • O waiter deve manter o lock da cadeia PI durante o pselect. Liberá-lo primeiro deixa o owner acordar e correr com a travessia falsa da rb_tree → panic.
  • m1q: PSELECT_WAITER_WORD_SHIFT deve ser 0 (profundidades de pilha do ZF1 verificadas a partir do ELF). Outros alvos ainda usam o padrão 1 — verifique por dispositivo antes de confiar neles.
  • p0_data_alias() usa DIRECT_MAP_BASE (o mapa linear), que difere de P0_PAGE_OFFSET no ZF1. Aliases de dados para objetos do kernel desreferenciados devem usar o mapa linear.
  • pr_error() é fatal (exit(-1)), ao contrário das macros de aviso/informação.
  • A reconstrução do slide usa SLIDE_LOGGERS_0_1, não SLIDE_NFULNL_LOGGER; a palavra é um alias de mapa linear não randomizado, então a rota boot_id é independente de slide (use-a apenas em slide=0 — execute primeiro o vazamento tracefs).

Aviso legal

Este é código de exploit para dispositivos Android e destina-se a pesquisa de segurança, testes de vulnerabilidade autorizados e trabalho de defesa em hardware que você possui ou tem permissão explícita para testar. O uso indevido em dispositivos que você não possui é ilegal. O autor não é responsável por dispositivos brickados, kernels brickados ou garantias anuladas.

Baixar ferramenta
mm_struct
  • Rota principal — bootstrap pipei tmp_page (m1q, foco atual) — a rota configfs CFI original é um beco sem saída no m1q: o ashmem Rust não tem slot estático write_iter injetável e o objeto de heap ASHMEM_SET_NAME é um KVec cujo layout não se alinha com o private_data de configfs_bin_write_iter. O m1q pivota para uma escrita kmalloc: recupera o bloco mm order-3 vazado como objetos pipe_inode_info e usa a escrita pselect W1 (*(tree_left) = tree_pc) para sobrescrever o slot tmp_page de um candidato (+0x90) com a página do namespace UTS (init_uts_ns). Uma escrita fanout planta um nome marcador no offset sysname e uname() reporta "CatOS" — uma escrita de kernel verificável sem dependência de objeto estático. Alvos C-ashmem mantêm o caminho configfs.
  • Pipe physrw — forja páginas de buffer de pipe na página do kernel sprayada para leitura/escrita física: patch de cred, desativação do SELinux e manipulação direta da memória do kernel.
  • Root — faz patch do cred do filho root (uid/gid/caps/SELinux SID), então preload.c instala o daemon su embutido (montado em tmpfs em /apex/com.android.virt/bin, mais variantes de namespace adbd e local) e troca o wallpaper.
  • VariableDefaultPurpose
    PSELECT_ROUTE_SHIFTcompile-timeA/B fd_set word shift for the main route (m1q)
    SLIDE_SHIFTcompile-timeA/B fd_set word shift for the slide route
    PSELECT_ROUTE_DELAY_USEC50000Consumer delay before sched_setattr (must be > 0)
    SLIDE_CONSUME_USEC, SLIDE_ENTER_DELAY_USEC, SLIDE_CONSUMER_CORE—Slide route timing/pinning
    SKIP_SLIDE0Use direct-map fallback (slide = 0)
    SLIDE_ONLY0Run only the KASLR slide and exit
    SLIDE_P0_OFFSET, SLIDE_BOOTID_OFF—Override slide p0-alias / boot_id offsets
    SLIDE_FORCE_BOOTID0Force the boot_id pselect route after the tracefs slide (mechanism test)
    KSNITCH_COLLISIONS4KernelSnitch collision count
    STAGE30Enable the pgd-swap bridge child phase (m1q)
    STAGE3_DRYRUN0Stage/verify tables then abort before the swap
    PAGE_RECLAIM_SENDS, PSELECT_SIMPLE_LAYOUT—Spray/fd-set layout tweaks
    TMP_PAGE_UNAME0Run the m1q pipei tmp_page-uname bootstrap
    TMP_UNAME_PIPEI_SWEEP, TMP_UNAME_PIPEI_ALIGN_ORDER, TMP_UNAME_PIPEI_SPLIT_ORDER_PAGES—Sweep mode; split_order sweeps all 8 pages of the freed mm block
    TMP_UNAME_PIPEI_SLOT, TMP_UNAME_PIPEI_SLOT_CANDIDATES—Sweep range (chunk start / count)
    PIPEI_CHILD_REGIONS0Per-child seeded rt_mutex regions; MUST be >= sweep chunk or unseeded regions panic at candidate 0
    PIPEI_RECLAIM_COUNT, PIPEI_DRAIN_COUNT—Reclaim/drain pipe pairs; raise on a reclaim miss
    PIPEI_SPONGE_MB3072Order-0 flood sponge held across the reclaim (build #29)
    PSELECT_W0_PRIO_OVERRIDE130Top-waiter prio; inert with seeded regions (legacy stale-tree guard, A/B only)
    PSELECT_SKB_DELTA_OVERRIDE0A/B payload intra-page offset (only if a device differs from -0xe78)
    TMP_UNAME_NAME, TMP_UNAME_HOLD_SEC"CatOS", 5Marker name / hold time after success
  • SKB_DATA_DELTA é -0xe78 (medido no dispositivo, build #19) — o verdadeiro skb frag head é 8 bytes menor que o -0xe80 derivado do disasm; esse erro de 8 bytes causou todo crash determinístico de pselect das builds #6-18 (entrou na travessia → deref de lixo). Valor compartilhado do GKI 6.12; cópias de util.c por alvo carregam um botão A/B de ambiente SKB_DATA_DELTA. NÃO empilhe PSELECT_SKB_DELTA_OVERRIDE sobre a constante embutida.
  • O corpo da travessia está limpo; o posicionamento era o bug. Com o payload semeado nos offsets modelados, a travessia da cadeia completa sem falhas no dispositivo. Um crash que entrou na travessia significa que a memória nos endereços das palavras fd NÃO é o payload (corrida de reclaim/posicionamento), não um erro de modelo.
  • m1q: a rota configfs CFI é um beco sem saída (Rust ashmem) — sem slot fops estático injetável e o envelope KVec de ASHMEM_SET_NAME não se alinha com configfs_bin_write_iter. Use o bootstrap pipei tmp_page. As guardas PSELECT_WRITE_RIGHT_SPRAYED / PSELECT_PI_TOP_TASK_SELF no target.h do m1q redirecionam as escritas [11] da travessia para fora dos fops estáticos RO.
  • Regiões rt_mutex por filho (build #33): cada filho da varredura redireciona a palavra fd w13 para sua própria região semeada (payload+0x6000 + idx*0x40) dentro da página recuperada. Varreduras com fake_lock compartilhado entraram em panic no candidato ~9-12 (nós obsoletos da pilha do kernel reciclados), e as regiões vazias da build #32 entraram em panic no candidato 0 (wake [9] owner==NULL na fake_task preenchida com 0x41). PIPEI_CHILD_REGIONS deve ser definido ou as regiões permanecem não semeadas.
  • O slide pselect boot_id do m1q está estacionado — seu alvo W1 é RAM baixa cuja gravabilidade de página varia a cada boot; tracefs é o primário e mede o slide a cada execução.