
Exploit de kernel CVE-2025-38352 para Smart TVs LG webOS (ARM64). Obtém root persistente em hardware de consumo real com técnicas de exploração inovadoras. Divulgado de forma responsável à LG.
Exploit de kernel para CVE-2025-38352 (condição de corrida do timer POSIX CPU / use-after-free) alcançando root persistente em Smart TVs LG webOS rodando kernel 5.4.268 em ARM64. Verificado em 4 modelos de TV em múltiplas versões de firmware. Divulgado responsavelmente ao Programa de Pesquisadores de Segurança da LG (Fevereiro de 2026).
Construído sobre o Chronomaly por farazsth98. A lógica de corrida UAF do Estágio 1 e a infraestrutura cross-cache foram portadas e adaptadas para ARM64; os Estágios 2 a 5 foram reprojetados com técnicas de exploração inovadoras e soluções para restrições de hardware real que não existem em ambientes emulados. Desenvolvido com a assistência do Claude Opus 4.6.
Veja VULNERABILITY_REPORT.md para a análise completa da vulnerabilidade, explicação da cadeia de exploração e mitigações recomendadas.
prisoner (uid=5038)Este exploit foi desenvolvido como parte de pesquisa de segurança responsável e relatado ao Programa de Pesquisadores de Segurança da LG em 7 de fevereiro de 2026. É publicado apenas para fins educacionais. Use com responsabilidade e apenas em dispositivos que você possui. Os autores não são responsáveis por qualquer dano, brick, perda de dados ou anulação de garantia resultante do uso deste software. Este software é fornecido "como está", sem garantia.
O decremento arbitrário original é lento e ruidoso: ele pulveriza 1000 objetos struct cred via processos bifurcados e depois decrementa o campo EUID do cred alvo N vezes. Cada decremento é uma operação separada. Isso é aceitável no QEMU, onde o timing é tolerante, mas não confiável em hardware real, onde a recuperação de páginas orientada por interrupções pode roubar páginas do buffer do pipe entre as operações.
Isso foi substituído por uma única escrita arbitrária via list_del_init(). O exploit sobrescreve os ponteiros list_head.next e list_head.prev do sigqueue UAF através do buffer do pipe. Quando o kernel desenfileira o sinal pendente (collect_signal() → list_del_init()), ele executa prev->next = next (escreve o endereço de cred falso em task_struct->cred) e next->prev = prev (escrita controlada de efeito colateral). Uma única escrita substitui o ponteiro cred do processo por um ponteiro para uma estrutura cred falsa contendo todos os campos uid/gid zero. Sem pulverização de cred, sem processos bifurcados, determinístico.
peek_pipe() via tee() para Leituras Não-DestrutivasO original usa chamadas read() destrutivas nos buffers do pipe durante todo o exploit. No QEMU, isso funciona bem porque as páginas não são roubadas entre as operações. Em hardware real com 4 núcleos físicos, a lista de páginas por CPU do kernel (pcplist) recupera agressivamente páginas liberadas. Uma leitura destrutiva libera a página de suporte do buffer do pipe, que pode ser imediatamente roubada por uma interrupção de hardware antes que o exploit possa realocá-la.
A solução é uma primitiva de leitura não-destrutiva usando tee(). A chamada de sistema tee() duplica dados do pipe entre dois pipes sem consumi-los, mantendo a página de suporte do buffer do pipe original fixada. Isso permite que o exploit leia dados do kernel do buffer do pipe cross-cached repetidamente sem risco de perda de página. Isso foi crítico para a confiabilidade em hardware real.
O original pulveriza objetos cred e espera que um deles caia em uma posição previsível. Esta versão constrói a estrutura cred falsa em um endereço conhecido realizando um segundo cross-cache: alocar um novo sigqueue (via tkill(SIGRTMIN+1)), aprender seu endereço a partir do vazamento de heap do primeiro buffer do pipe e, em seguida, fazer o cross-cache da página slab desse sigqueue em um segundo buffer do pipe. O cred falso é escrito no segundo buffer do pipe no deslocamento exato da página do endereço do sigqueue vazado. O resultado é um cred falso em um endereço de kernel determinístico, sem adivinhação.
O original desenfileira SIGUSR2 no início do Estágio 2 para vazar o endereço do sigqueue UAF. Isso consome o sinal, então o original precisa de um mecanismo diferente para a escrita final. Esta versão nunca precisa do endereço do próprio sigqueue UAF (o vazamento de heap vem dos ponteiros adjacentes do sigqueue no buffer do pipe). O SIGUSR2 é mantido pendente em todos os cinco estágios e seu desenfileiramento é usado como o gatilho de escrita arbitrária final. O sinal que criou o UAF é o mesmo sinal cujo desenfileiramento o explora.
modprobe_path + socket(44) EscalaçãoA estrutura cred falsa tem ponteiros NULL em user_ns, user e group_info (já que o buffer do pipe é zerado além dos campos uid/gid). Chamar setresuid(), fork() ou exec() desreferenciaria esses ponteiros NULL e causaria pânico no kernel. O original evita isso porque sua pulverização de cred usa objetos cred reais com ponteiros válidos.
Solução: sobrescrever /proc/sys/kernel/modprobe para apontar para um script de payload (/tmp/pwn) e, em seguida, acionar call_usermodehelper via socket(44, SOCK_STREAM, 0) (solicitando uma família de protocolo inexistente). O kernel executa o helper modprobe com init_cred (as credenciais de root do próprio kernel, totalmente válidas), contornando o cred corrompido. O payload é executado como root completo e pode realizar operações arbitrárias.
A janela crítica no Estágio 4 (escrever ponteiros maliciosos no buffer do pipe e, em seguida, acionar o desenfileiramento do sinal) é vulnerável a interrupções de hardware que roubam a página do buffer do pipe da lista de páginas por CPU. Isso não acontece no QEMU. Em hardware real, essa janela é protegida com prioridade SCHED_FIFO (quando disponível) e sched_yield() para permitir que o trabalho pendente seja concluído na CPU antes de entrar na seção crítica, além de conteúdos de buffer pré-preparados para minimizar o tempo entre a escrita e o gatilho. O exploit também recai graciosamente quando SCHED_FIFO não está disponível (como no webOS, onde o usuário prisioneiro não tem CAP_SYS_NICE).
task_struct em ARM64A escrita arbitrária tem como alvo task_struct->cred, o que requer saber o deslocamento de bytes de task_struct->pending (cujo endereço é vazado do buffer do pipe) até task_struct->cred. Esse offset depende da configuração do kernel. O offset de 0x80 (128 bytes) foi calculado manualmente a partir do código-fonte do kernel LG webOS, levando em conta CONFIG_KEYS=y, CONFIG_SYSVIPC=y e o layout e alinhamento específicos da estrutura ARM64. O offset x86_64 no original é diferente devido ao empacotamento e opções de configuração diferentes.