
CVE-2025-38352 exploit noyau pour les téléviseurs intelligents LG webOS (ARM64). Obtient un root persistant sur du matériel grand public réel avec des techniques d'exploitation innovantes. Divulgué de manière responsable à LG.
Exploit du noyau pour CVE-2025-38352 (condition de course / use-after-free du timer CPU POSIX) permettant d'obtenir un accès root persistant sur les téléviseurs intelligents LG webOS exécutant le noyau 5.4.268 sur ARM64. Testé sur 4 modèles de téléviseurs avec plusieurs versions de firmware. Divulgué de manière responsable au programme de recherche en sécurité de LG (février 2026).
Basé sur Chronomaly par farazsth98. La logique de course UAF de l'étape 1 et l'infrastructure cross-cache ont été portées et adaptées pour ARM64 ; les étapes 2 à 5 ont été repensées avec des techniques d'exploitation inédites et des solutions aux contraintes matérielles réelles qui n'existent pas dans les environnements émulés. Développé avec l'aide de Claude Opus 4.6.
Voir VULNERABILITY_REPORT.md pour l'analyse complète de la vulnérabilité, la description détaillée de la chaîne d'exploitation et les mesures d'atténuation recommandées.
prisoner (uid=5038)Cet exploit a été développé dans le cadre d'une recherche en sécurité responsable et signalé au programme de recherche en sécurité de LG le 7 février 2026. Il est publié à des fins éducatives uniquement. Utilisez-le de manière responsable et uniquement sur des appareils que vous possédez. Les auteurs ne sont pas responsables des dommages, briques, pertes de données ou annulations de garantie résultant de l'utilisation de ce logiciel. Ce logiciel est fourni tel quel, sans aucune garantie.
La primitive de décrémentation arbitraire de l'original est lente et bruyante : elle pulvérise 1 000 objets struct cred via des processus fork, puis décrémente le champ EUID d'un cred cible N fois. Chaque décrémentation est une opération distincte. Cela est acceptable dans QEMU où le timing est indulgent, mais peu fiable sur du matériel réel où la récupération de pages pilotée par les interruptions peut voler les pages du tampon de tube entre les opérations.
Cela a été remplacé par une seule écriture arbitraire via list_del_init(). L'exploit écrase les pointeurs list_head.next et list_head.prev du sigqueue UAF via le tampon de tube. Lorsque le noyau retire le signal en attente (collect_signal() → list_del_init()), il exécute prev->next = next (écrit l'adresse du cred factice dans task_struct->cred) et next->prev = prev (écriture secondaire contrôlée). Une seule écriture remplace le pointeur cred du processus par un pointeur vers une structure cred factice contenant des champs uid/gid tous à zéro. Pas de pulvérisation de cred, pas de processus fork, déterministe.
peek_pipe() via tee() pour des lectures non destructivesL'original utilise des appels read() destructifs sur les tampons de tube tout au long de l'exploit. Dans QEMU, cela fonctionne bien car les pages ne sont pas volées entre les opérations. Sur du matériel réel avec 4 cœurs physiques, la liste de pages par CPU (pcplist) du noyau récupère agressivement les pages libérées. Une lecture destructive libère la page de support du tampon de tube, qui peut être immédiatement volée par une interruption matérielle avant que l'exploit ne puisse la réallouer.
La solution est une primitive de lecture de tube non destructive utilisant tee(). L'appel système tee() duplique les données du tube entre deux tubes sans les consommer, gardant la page de support du tampon de tube d'origine épinglée. Cela permet à l'exploit de lire les données du noyau à partir du tampon de tube cross-cache à plusieurs reprises sans risque de perte de page. Cela a été critique pour la fiabilité sur du matériel réel.
L'original pulvérise des objets cred et espère en placer un à un emplacement prévisible. Cette version construit la structure cred factice à une adresse connue en effectuant un second cross-cache : allouer un nouveau sigqueue (via tkill(SIGRTMIN+1)), apprendre son adresse à partir de la fuite de tas du premier tampon de tube, puis cross-cacher la page de slab de ce sigqueue dans un second tampon de tube. Le cred factice est écrit dans le second tampon de tube à l'offset exact de la page de l'adresse du sigqueue divulguée. Le résultat est un cred factice à une adresse noyau déterministe, sans incertitude.
L'original retire SIGUSR2 tôt à l'étape 2 pour divulguer l'adresse du sigqueue UAF. Cela consomme le signal, donc l'original a besoin d'un mécanisme différent pour l'écriture finale. Cette version n'a jamais besoin de l'adresse propre du sigqueue UAF (la fuite de tas provient des pointeurs sigqueue adjacents dans le tampon de tube). SIGUSR2 est maintenu en attente à travers les cinq étapes et son retrait est utilisé comme déclencheur d'écriture arbitraire finale. Le signal qui a créé l'UAF est le même signal dont le retrait l'exploite.
modprobe_path + socket(44)La structure cred factice a des pointeurs user_ns, user et group_info NULL (car le tampon de tube est initialisé à zéro au-delà des champs uid/gid). Appeler setresuid(), fork() ou exec() déréférencerait ces pointeurs NULL et provoquerait un panique du noyau. L'original évite cela car sa pulvérisation de cred utilise de véritables objets cred avec des pointeurs valides.
La solution : écraser /proc/sys/kernel/modprobe pour pointer vers un script de payload (/tmp/pwn), puis déclencher call_usermodehelper via socket(44, SOCK_STREAM, 0) (demande d'une famille de protocole inexistante). Le noyau exécute l'assistant modprobe avec init_cred (les propres credentials root du noyau, complètement valides), contournant ainsi le cred corrompu. Le payload s'exécute en tant que root complet et peut effectuer des opérations arbitraires.
La fenêtre critique à l'étape 4 (écriture de pointeurs malveillants dans le tampon de tube, puis déclenchement du retrait du signal) est vulnérable aux interruptions matérielles qui volent la page du tampon de tube depuis la liste de pages par CPU. Cela ne se produit pas dans QEMU. Sur du matériel réel, cette fenêtre est protégée avec la priorité SCHED_FIFO (lorsqu'elle est disponible) et sched_yield() pour laisser les travaux en attente se terminer sur le CPU avant d'entrer dans la section critique, ainsi que des contenus de tampon pré-préparés pour minimiser le temps entre l'écriture et le déclenchement. L'exploit se replie gracieusement lorsque SCHED_FIFO n'est pas disponible (comme sur webOS où l'utilisateur prisoner n'a pas CAP_SYS_NICE).
task_struct pour ARM64