
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 adicionalsymbols/kallsyms_PD2241_15.2.10.2.txt — tabela de símbolos completa do 15.2.10.2, utilizável diretamente por outrosdevice_config.txt — configuração real do kernel do dispositivo, mostra o que o vendor alterouexploit/targets/android_15.0_kernel_MT6985/target.h — offsets de structs verificadosscripts/server_compile.py — compilação automatizada, recompilação rápida após alterar parâmetrostar -xf pode falhar com zips grandes, use Python zipfile ou descompacte manualmente primeiroupdate_metadata_pb2.py gerado requer protobuf 5.x, é necessário remover manualmente a linha de importação runtime_version./preload.so diretamente causa segfault: é obrigatório usar /system/bin/linker64 /data/local/tmp/preload.solayout-offset-in-bits é calculado pelo compilador, 100x mais preciso que contar 5000 bytes manualmenteCONFIG_ANDROID_VENDOR_OEM_DATA=y, CONFIG_SCHED_INFO=y, CONFIG_RSC_* → desvia do GKI padrãoboot.img → kernel.bin → descompressão LZ4 → Image → kallsyms-finder → tabela de símbolos07/28 Download do repositório CyberMeowfia + código-fonte MT6985
07/29 Análise do código-fonte (memory.h, fs.h, ABI XML, várias structs)
Descompactação do firmware (payload.bin → boot.img → Image)
Extração de símbolos (kallsyms-finder → 187810 símbolos)
Múltiplas compilações + múltiplos travamentos + verificação por desmontagem
5 tentativas automáticas → todas falharam
Escrita deste artigo
-------------------------------------------
Total: ~68M tokens, 0 shells root
2026-07-29, sobrevivi para contar a história
Após obter android_15.0_kernel_MT6985.tar.gz (árvore de código-fonte 5.15.178 + ABI XML), foi feita
validação cruzada completa, detalhes em VERIFICATION.md. Resumo:
target.h foi corrigido, e a auto-verificação em
dispositivo real via leak_kernel_base() embutida no exploit foi usada como salvaguarda.KSNITCH_MTE_ENABLED=1 realmente
tem efeito (o util.c original tinha mte=0 codificado);rt_mutex_adjust_prio_chain+0x1b0 está na fase da cadeia pselect/pi, antes da
auto-verificação FOPS; após as correções acima, vale a pena testar novamente no dispositivo.Dispositivo (PD2241, compiler251203103903) — 8+ rodadas de teste em hardware real:
MM_STRUCT_SZ=0x400 (o valor original 0x500 causava desalinhamento
da grade de varredura, produzindo apenas falsos positivos) + varredura de tags MTE 0..15 (a tag do ponteiro mm
do dispositivo muda) + remoção de tag em endereços falsificados. Agora encontra o mm_struct real de forma estável.rt_mutex_adjust_prio_chain+0x1b0: corrida de temporização na cadeia pselect/pi
(o waiter falsificado não cai no offset correto da pilha do kernel). O escalonador RSC da vivo modificou o caminho
futex/pi, possivelmente quebrando essa corrida por completo. O alinhamento da pilha do slide foi parametrizado
como variável de ambiente SLIDE_SHIFT, varredura não concluída.Escolha final: rebaixar para desbloqueio pago do bootloader (não depender mais deste caminho).