
CVE-2026-43499 adaptador de exploit para MT6985 MediaTek Dimensity 9300 (vivo PD2241)
Conclusão: Gastei 68M tokens, não obtive root.
Motivo: O ataque de temporização do KernelSnitch não é confiável no MTK Dimensity 9200,CONFIG_PANIC_ON_OOPS=ynão deixa espaço para tentativa e erro.
Este artigo documenta todo o processo de erros para referência futura e evitar armadilhas.
| Item | Valor |
|---|---|
| Dispositivo | vivo PD2241 (Dimensity 9200 / MT6985), Android 15 |
| Firmware | PD2241_A_15.2.10.2.W10.V000L1 |
| Kernel | 5.15.178-android13-8-gfb31f5bdd612-dirty |
| Bootloader | Bloqueado (ro.boot.flash.locked=1) |
| SELinux | Enforcing |
| panic_on_oops | Ativado → qualquer OOPS do kernel = reinicialização instantânea |
| Exploit | CyberMeowfia — CVE-2026-43499 (IonStack) |
| Árvore de código-fonte | android_15.0_kernel_MT6985 (5.15.178) — não corresponde à versão do dispositivo (código-fonte é android15 GKI, dispositivo executa android13 GKI) |
Extraído de arch/arm64/include/asm/memory.h + include/linux/fs.h + android/abi_gki_aarch64.xml:
KIMAGE_TEXT_BASE = 0xffffffc008000000, VA_BITS=39, DIRECT_MAP=256GBlayout-offset-in-bits)iopoll (android13 GKI), IOCTL=0x48, open=0x68atomic_t usage = 4 bytes, uid=0x04slab_cache=0x18Descoberta-chave: o código-fonte é android15 GKI, o dispositivo é android13 GKI — os offsets do task_struct diferem em 0x40~0x88 bytes, não é possível copiar diretamente do código-fonte.
OTA zip (8.3GB)
→ payload.bin (8.2GB)
→ payload_dumper → boot.img (96MB, cabeçalho v4)
→ Descompressão LZ4 → Image (50MB ARM64)
→ kallsyms-finder → 187810 símbolos
Símbolos extraídos de duas versões de firmware (15.2.7.6 / 15.2.10.2) — o mesmo símbolo difere em 10KB~200KB entre as duas versões, é obrigatório usar a versão correta.
# Em rt_mutex_adjust_pi:
LDR x21, [x19, #0x8b0] → pi_blocked_on = 0x8b0 (valor android15, não frankel)
Isso confirma que o layout do task_struct é do branch android15, não o android13 do frankel.
NDK r29, make PROJECT=android_15.0_kernel_MT6985 → preload.so (150KB).
[+] preload starting pid=25414
[+] p0 profile ... todos os símbolos carregados corretamente
[-] KernelSnitch mm_struct leak failed ← às vezes esta linha não aparece (sucesso ocasional)
[+] slide child context route=pselect ← processo filho de vazamento do slide KASLR iniciado
[panic do kernel] ← rt_mutex_adjust_prio_chain+0x1b0
Instrução do travamento (capstone):
ldar w8, [x27] ; x27 = waiter->lock (carregado de [x28, #0x38])
; valor de x27 é lixo → sem mapeamento na tabela de páginas → falha de tradução
; → die() → panic → reinicialização
KernelSnitch é a porta de entrada de todo o exploit — ele vaza o endereço do mm_struct através da diferença de temporização no bucket do hash do futex:
MT6985 tem CONFIG_KASAN_HW_TAGS=y → o kernel marca alocações de slab com tags MTE
O ponteiro do mm_struct carrega tag KASAN → futex_hash é calculado com base no ponteiro com tag
Mas o bruteforce escaneia o direct map (endereços sem tag) → o hash calculado não corresponde
Mesmo adicionando a varredura de tags MTE (0-14, 15 tipos no total), em sistemas com VA_BITS=39
os bits de tag (bit56-59) se sobrepõem aos bits de extensão de sinal → algumas combinações de tag
produzem endereços inválidos → detecção falha
Dispositivos Pixel não têm KASAN_HW_TAGS, esse mecanismo funciona. MTK não funciona.
CONFIG_PANIC_ON_OOPS é o assassinoPixel: OOPS do kernel → dump_stack → continua executando → exploit pode tentar novamente
MT6985: OOPS do kernel → die() → panic() → reinicialização instantânea → sem espaço para tentativa e erro
Qualquer pequeno desvio na cadeia π causa colapso total; no Pixel, um desvio apenas significa "esta tentativa falhou, tente outro conjunto de endereços".
E o bootloader está bloqueado (flash.locked=1) → não é possível flashar um kernel personalizado para remover essa opção.
Árvore de código-fonte: 5.15.178 android15 GKI
Dispositivo: 5.15.178-android13 (vendor vivo)
Embora o número da versão principal seja o mesmo 5.15.178, os branches GKI são diferentes (android13 vs android15), e os layouts de structs críticas como task_struct/cred não são consistentes. Após alternar repetidamente entre os dois conjuntos de offsets (frankel/android15), só foi possível confirmar através de desmontagem.
| Alteração | Objetivo | Resultado |
|---|---|---|
THRESHOLD_MULT 10→5→3 | Reduzir o limiar de detecção de colisão | <5 falsos positivos demais |
APPENDED_FUTEXES 4096→8192 | Aumentar a diferença na cadeia de hash | Sem efeito |
REPEAT_MEASUREMENT/AVERAGE | Aumentar a precisão da amostragem | Sem efeito |
MTE=1 | Fazer o bruteforce percorrer as tags | Mais lento, na verdade reduziu travamentos |
MM_STRUCT_SZ 0x500→0x400 | Corrigir o passo do mm_struct | Necessário, ABI real é 992 bytes |
IDENTITY_END 64GB→256GB | Expandir o intervalo de varredura | Muito lento (varredura MTE), ainda sem correspondência |
| Offsets TASK: android15↔frankel | Bloquear o offset correto | Desmontagem confirma android15 |
| Offsets FOPS: android15↔frankel | android13 sem iopoll | Usar frankel |
Em exploit/targets/android_15.0_kernel_MT6985/target.h:
| Categoria | Confiabilidade | Método de verificação |
|---|---|---|
| Layout de memória | Correto | Cálculo do memory.h + verificação do _text via kallsyms |
| Offsets de símbolos (22) | Corretos | Extraídos do boot.img 15.2.10.2 |
| Offsets do task_struct | Corretos | ABI XML + desmontagem capstone (pi_blocked_on=0x8b0) |
| Offsets FOPS | Incorretos (corrigidos em 2026-07-31) | Valor original copiado do frankel (android13 sem iopoll); o ABI da árvore de código-fonte na verdade tem iopoll@0x30, ioctl=0x50, open=0x70 — ver VERIFICATION.md |
| Offsets CRED | Corretos (verificados) | ABI XML: uid=0x04, securebits=0x24, caps=0x28, security=0x78 |
A montagem compila, o exploit executa, mas perdeu no último quilômetro.
CONFIG_PANIC_ON_OOPS — ou flashar um kernel personalizado (requer desbloquear o bootloader), ou encontrar um dispositivo MT6985 com essa opção desativada por padrão/proc/self/pagemap — restrito neste dispositivo (retorna tudo zero)/proc/mtk_*) — existem, mas precisam de análise adicional