
CVE-2025-38352 exploit del kernel per smart TV LG webOS (ARM64). Raggiunge root persistente su hardware consumer reale con tecniche di sfruttamento innovative. Divulgato responsabilmente a LG.
Exploit del kernel per CVE-2025-38352 (condizione di gara del timer CPU POSIX / use-after-free) che ottiene root persistente su Smart TV LG webOS con kernel 5.4.268 su ARM64. Verificato su 4 modelli di TV in diverse versioni firmware. Divulgato responsabilmente al programma LG Security Researcher (febbraio 2026).
Basato su Chronomaly di farazsth98. La logica di race UAF dello Stage 1 e l'infrastruttura cross-cache sono state portate e adattate per ARM64; gli Stage 2–5 sono stati riprogettati con nuove tecniche di sfruttamento e soluzioni ai vincoli hardware reali che non esistono in ambienti emulati. Sviluppato con l'assistenza di Claude Opus 4.6.
Vedi VULNERABILITY_REPORT.md per l'analisi completa della vulnerabilità, la descrizione della catena di exploit e le mitigazioni raccomandate.
prisoner (uid=5038)Questo exploit è stato sviluppato come parte di una ricerca sulla sicurezza responsabile e segnalato al programma LG Security Researcher il 7 febbraio 2026. Viene pubblicato solo a scopo educativo. Usalo in modo responsabile e solo su dispositivi di tua proprietà. Gli autori non sono responsabili per eventuali danni, brick, perdita di dati o annullamento della garanzia derivanti dall'uso di questo software. Questo software è fornito "così com'è" senza garanzia.
Il decremento arbitrario dell'originale è lento e rumoroso: spruzza 1000 oggetti struct cred tramite processi fork, poi decrementa il campo EUID del cred target N volte. Ogni decremento è un'operazione separata. È accettabile in QEMU, dove i tempi sono indulgenti, ma inaffidabile su hardware reale, dove il recupero delle pagine guidato dagli interrupt può rubare le pagine del buffer della pipe tra un'operazione e l'altra.
È stato sostituito con una singola scrittura arbitraria tramite list_del_init(). L'exploit sovrascrive i puntatori list_head.next e list_head.prev del sigqueue UAF attraverso il buffer della pipe. Quando il kernel rimuove il segnale in sospeso (collect_signal() → list_del_init()), esegue prev->next = next (scrive l'indirizzo del cred fittizio in task_struct->cred) e next->prev = prev (scrittura collaterale controllata). Una singola scrittura sostituisce il puntatore cred del processo con un puntatore a una struttura cred fittizia contenente tutti i campi uid/gid a zero. Nessuno spray di cred, nessun processo fork, deterministico.
peek_pipe() tramite tee() per letture non distruttiveL'originale utilizza chiamate read() distruttive sui buffer della pipe durante tutto l'exploit. In QEMU funziona bene perché le pagine non vengono rubate tra le operazioni. Su hardware reale con 4 core fisici, la lista delle pagine per CPU del kernel (pcplist) recupera aggressivamente le pagine liberate. Una lettura distruttiva rilascia la pagina di supporto del buffer della pipe, che può essere immediatamente rubata da un interrupt hardware prima che l'exploit possa riallocarla.
La soluzione è una primitiva di lettura non distruttiva della pipe tramite tee(). La chiamata di sistema tee() duplica i dati della pipe tra due pipe senza consumarli, mantenendo la pagina di supporto del buffer della pipe originale bloccata. Ciò consente all'exploit di leggere i dati del kernel dal buffer della pipe cross-cached ripetutamente senza rischiare la perdita della pagina. È stato fondamentale per l'affidabilità su hardware reale.
L'originale spruzza oggetti cred e spera di posizionarne uno in una posizione prevedibile. Questa versione costruisce la struttura cred fittizia a un indirizzo noto eseguendo una seconda cross-cache: alloca un nuovo sigqueue (tramite tkill(SIGRTMIN+1)), ne apprende l'indirizzo dalla prima perdita di heap del buffer della pipe, quindi esegue il cross-cache della pagina slab di quel sigqueue in un secondo buffer della pipe. Il cred fittizio viene scritto nel secondo buffer della pipe all'esatto offset di pagina dell'indirizzo del sigqueue trapelato. Il risultato è un cred fittizio a un indirizzo kernel deterministico senza alcuna ipotesi.
L'originale rimuove SIGUSR2 all'inizio dello Stage 2 per far trapelare l'indirizzo del sigqueue UAF. Questo consuma il segnale, quindi l'originale necessita di un meccanismo diverso per la scrittura finale. Questa versione non ha mai bisogno dell'indirizzo del sigqueue UAF stesso (la perdita di heap proviene dai puntatori sigqueue adiacenti nel buffer della pipe). SIGUSR2 viene mantenuto in sospeso per tutti e cinque gli stage e la sua rimozione viene utilizzata come attivatore finale della scrittura arbitraria. Il segnale che ha creato l'UAF è lo stesso segnale la cui rimozione lo sfrutta.
modprobe_path + socket(44)La struttura cred fittizia ha puntatori user_ns, user e group_info NULL (poiché il buffer della pipe viene inizializzato a zero oltre i campi uid/gid). Chiamare setresuid(), fork() o exec() dereferenzierebbe questi puntatori NULL e causerebbe un kernel panic. L'originale evita questo perché il suo spray di cred utilizza oggetti cred reali con puntatori validi.
La soluzione: sovrascrivere /proc/sys/kernel/modprobe per puntare a uno script payload (/tmp/pwn), quindi attivare call_usermodehelper tramite socket(44, SOCK_STREAM, 0) (richiedendo una famiglia di protocolli inesistente). Il kernel esegue l'helper modprobe con init_cred (le credenziali root del kernel stesso, completamente valide), bypassando del tutto il cred corrotto. Il payload viene eseguito come root completo e può eseguire operazioni arbitrarie.
La finestra critica nello Stage 4 (scrittura di puntatori malevoli nel buffer della pipe, quindi attivazione della rimozione del segnale) è vulnerabile agli interrupt hardware che rubano la pagina del buffer della pipe dalla lista di pagine per CPU. Questo non accade in QEMU. Su hardware reale, questa finestra è protetta con priorità SCHED_FIFO (quando disponibile) e sched_yield() per consentire il completamento del lavoro pendente sulla CPU prima di entrare nella sezione critica, oltre a contenuti del buffer pre-preparati per ridurre al minimo il tempo tra scrittura e attivazione. L'exploit torna anche in modo elegante quando SCHED_FIFO non è disponibile (come su webOS dove l'utente prisoner non ha CAP_SYS_NICE).
task_struct su ARM64La scrittura arbitraria ha come target task_struct->cred, che richiede di conoscere l'offset in byte da task_struct->pending (il cui indirizzo viene trapelato dal buffer della pipe) a task_struct->cred. Questo offset dipende dalla configurazione del kernel. L'offset 0x80 (128 byte) è stato calcolato manualmente dal codice sorgente del kernel LG webOS, tenendo conto di CONFIG_KEYS=y, CONFIG_SYSVIPC=y e del layout e allineamento delle strutture specifici di ARM64. L'offset x86_64 nell'originale è diverso a causa del diverso packing e delle opzioni di configurazione.