
CVE-2025-38352 exploit de kernel para Smart TVs LG webOS (ARM64). Logra root persistente en hardware de consumo real con técnicas de explotación novedosas. Divulgado responsablemente a LG.
Exploit de kernel para CVE-2025-38352 (condición de carrera de temporizador POSIX CPU / use-after-free) que consigue root persistente en Smart TVs LG webOS con kernel 5.4.268 en ARM64. Verificado en 4 modelos de TV en múltiples versiones de firmware. Divulgado responsablemente al Programa de Investigadores de Seguridad de LG (febrero de 2026).
Construido sobre Chronomaly por farazsth98. La lógica de la carrera UAF de la Etapa 1 y la infraestructura de cross-cache se trasladaron y adaptaron para ARM64; las Etapas 2–5 se rediseñaron con técnicas de explotación novedosas y soluciones a limitaciones de hardware real que no existen en entornos emulados. Desarrollado con la asistencia de Claude Opus 4.6.
Consulte VULNERABILITY_REPORT.md para obtener el análisis completo de la vulnerabilidad, el recorrido de la cadena de explotación y las mitigaciones recomendadas.
prisoner (uid=5038)Este exploit fue desarrollado como parte de una investigación de seguridad responsable y se reportó al Programa de Investigadores de Seguridad de LG el 7 de febrero de 2026. Se publica únicamente con fines educativos. Úselo de manera responsable y solo en dispositivos que posea. Los autores no se hacen responsables de daños, bloqueos, pérdida de datos o anulación de garantía derivados del uso de este software. Este software se proporciona tal cual, sin garantía alguna.
El decremento arbitrario del original es lento y ruidoso: rocía 1000 objetos struct cred mediante procesos bifurcados, luego decrementa el campo EUID de un cred objetivo N veces. Cada decremento es una operación separada. Esto es aceptable en QEMU donde el tiempo es indulgente, pero poco fiable en hardware real donde la reclamación de páginas impulsada por interrupciones puede robar páginas del búfer de tubería entre operaciones.
Se reemplazó con una única escritura arbitraria mediante list_del_init(). El exploit sobrescribe los punteros list_head.next y list_head.prev del sigqueue con UAF a través del búfer de tubería. Cuando el kernel desencola la señal pendiente (collect_signal() → list_del_init()), realiza prev->next = next (escribe la dirección de cred falsa en task_struct->cred) y next->prev = prev (escritura de efecto secundario controlada). Una sola escritura reemplaza el puntero cred del proceso con un puntero a una estructura cred falsa que contiene todos los campos uid/gid en cero. Sin rociado de cred, sin procesos bifurcados, determinista.
peek_pipe() mediante tee() para lecturas no destructivasEl original usa llamadas read() destructivas en los búferes de tubería durante todo el exploit. En QEMU, esto funciona bien porque las páginas no se roban entre operaciones. En hardware real con 4 núcleos físicos, la lista de páginas por CPU del kernel (pcplist) reclama agresivamente las páginas liberadas. Una lectura destructiva libera la página de respaldo del búfer de tubería, que puede ser robada inmediatamente por una interrupción de hardware antes de que el exploit pueda reasignarla.
La solución es una primitiva de lectura de tubería no destructiva usando tee(). La llamada al sistema tee() duplica los datos de la tubería entre dos tuberías sin consumirlos, manteniendo fija la página de respaldo del búfer de tubería original. Esto permite que el exploit lea datos del kernel desde el búfer de tubería cross-cache repetidamente sin riesgo de perder la página. Esto fue crítico para la fiabilidad en hardware real.
El original rocía objetos cred y espera que uno caiga en una ubicación predecible. Esta versión construye la estructura cred falsa en una dirección conocida realizando un segundo cross-cache: asigna un nuevo sigqueue (mediante tkill(SIGRTMIN+1)), aprende su dirección desde la fuga de heap del primer búfer de tubería, luego hace cross-cache de la página de slab de ese sigqueue a un segundo búfer de tubería. La cred falsa se escribe en el segundo búfer de tubería en el desplazamiento de página exacto de la dirección del sigqueue filtrada. El resultado es una cred falsa en una dirección determinista del kernel sin conjeturas.
El original desencola SIGUSR2 al principio de la Etapa 2 para filtrar la dirección del sigqueue con UAF. Esto consume la señal, por lo que el original necesita un mecanismo diferente para la escritura final. Esta versión nunca necesita la dirección del propio sigqueue con UAF (la fuga de heap proviene de punteros sigqueue adyacentes en el búfer de tubería). SIGUSR2 se mantiene pendiente a lo largo de las cinco etapas y su desencolado se utiliza como disparador final de la escritura arbitraria. La señal que creó el UAF es la misma señal cuyo desencolado lo explota.
modprobe_path + socket(44)La estructura cred falsa tiene punteros NULL en user_ns, user y group_info (ya que el búfer de tubería se inicializa a cero más allá de los campos uid/gid). Llamar a setresuid(), fork() o exec() desreferenciaría estos punteros NULL y causaría un pánico del kernel. El original evita esto porque su rociado de cred usa objetos cred reales con punteros válidos.
La solución: sobrescribir /proc/sys/kernel/modprobe para que apunte a un script payload (/tmp/pwn), luego activar call_usermodehelper mediante socket(44, SOCK_STREAM, 0) (solicitando una familia de protocolo inexistente). El kernel ejecuta el helper modprobe con init_cred (las credenciales root del propio kernel, completamente válidas), evitando la cred corrupta. El payload se ejecuta como root completo y puede realizar operaciones arbitrarias.
La ventana crítica en la Etapa 4 (escribir punteros maliciosos en el búfer de tubería, luego activar el desencolado de señal) es vulnerable a que las interrupciones de hardware roben la página del búfer de tubería de la lista de páginas por CPU. Esto no sucede en QEMU. En hardware real, esta ventana está protegida con prioridad SCHED_FIFO (cuando esté disponible) y sched_yield() para permitir que el trabajo pendiente se complete en la CPU antes de entrar en la sección crítica, además de contenido del búfer preparado de antemano para minimizar el tiempo entre escritura y disparador. El exploit también falla con gracia cuando SCHED_FIFO no está disponible (como en webOS donde el usuario prisoner carece de CAP_SYS_NICE).
task_struct en ARM64