
PoC et script GDB facilitant le déclenchement de CVE-2025-38352
Un Proof-of-Concept (PoC) reproductible et un script d'orchestration GDB automatisé pour CVE-2025-38352, une condition de course Time-of-Check to Time-of-Use (TOCTOU) dans le sous-système de minuteries CPU POSIX du noyau Linux (kernel/time/posix-cpu-timers.c) menant à un Use-After-Free (UAF).
Avertissement : Ce projet est destiné uniquement à des fins éducatives, défensives et de recherche en sécurité. Tous les tests et reproductions ont été effectués dans une machine virtuelle ARM64 QEMU isolée.
Le processus A crée le processus enfant B.
Le processus B crée une minuterie CPU et commence à se terminer, entrant dans l'état zombie. Juste au moment où B se termine, une interruption de minuterie arrive sur son cœur CPU. Le noyau entre en contexte d'interruption pour traiter la minuterie, mais libère temporairement son verrou (unlock_task_sighand) pour éviter les interblocages.
Exactement au même moment sur un autre cœur, le processus A récupère B en cours de terminaison, nettoie son gestionnaire de signaux (sighand = NULL) et retire son PID de la table de hachage.
Un autre thread de B appelle la suppression de minuterie (posix_cpu_timer_del). Il tente de retrouver la tâche via son PID et de vérifier son gestionnaire de signaux, mais les trouve déjà nettoyés / NULL. Interprétant à tort que la tâche est complètement morte et qu'aucune minuterie ne peut se déclencher, il suppose qu'il est sûr de libérer la minuterie de la mémoire.
Pendant ce temps, le gestionnaire d'interruption de minuterie sur le premier cœur reprend pour déclencher la minuterie expirée. Comme la minuterie vient d'être libérée à l'étape 4, y accéder déclenche un Use-After-Free et fait planter le noyau.
poc_gdb_script.py)Comme le stub GDB de QEMU ne prend pas en charge set non-stop on (arrêter un vCPU arrête tous les vCPUs), la synchronisation multi-thread standard ne peut pas être réalisée via de simples points d'arrêt.
Ce dépôt résout le problème via le Patch d'instructions en mémoire :
Étape 1 (Barrière CPU) :
exit_notify (CPU 2)kernel_wait4 (CPU 0)__arm64_sys_timer_delete (CPU 1)handle_posix_cpu_timers (CPU 2)$pc avec l'opcode d'auto-branchement ARM64 0x14000000 (boucle infinie b .) et reprend l'exécution.Étape 2 (Orchestration séquentielle) :
set scheduler-locking on) et fait avancer chaque vCPU séquentiellement :
handle_posix_cpu_timers au-delà de unlock_task_sighand().Téléchargez ou clonez l'arborescence des sources de l'Android Common Kernel au 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()Ouvrez kernel/time/posix-cpu-timers.c, localisez la fonction run_posix_cpu_timers() (vers les lignes 1435–1448) et commentez les lignes du 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_WORKRemarque : Lorsque
CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y, les minuteries CPU sont exécutées depuis le contextetask_workau lieu de l'IRQ de minuterie, contournant ainsi cette condition de course. Par conséquent, elle doit être désactivée pour reproduire la vulnérabilité.
Comme CONFIG_POSIX_CPU_TIMERS_TASK_WORK n'a pas de chaîne d'invite dans le Kconfig amont, elle vaut y par défaut et ne peut pas être basculée directement dans menuconfig. Vous pouvez l'exposer comme suit :
kernel/time/Kconfig (vers la ligne 56), ajoutez une chaîne d'invite et changez la valeur par défaut :
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
Compilez l'image du noyau ARM64 :
time ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make -j$(nproc) Image
Compilez poc.c en cross-compilation :
aarch64-linux-gnu-gcc -ggdb3 ./poc.c -o poc
Lancez votre machine virtuelle QEMU avec 4 vCPUs (-smp 4) et activez le stub GDB (option -s, port 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
Depuis votre terminal hôte, attachez GDB à QEMU et chargez le script d'orchestration :
gdb-multiarch vmlinux
pwndbg> target remote :1234
pwndbg> source poc_gdb_script.py
pwndbg> continue
Dans le shell de l'invité QEMU, exécutez le binaire compilé :
./poc
[!TIP] Timing et dépannage :
Si la minuterie se déclenche prématurément avant que le thread cible n'ait atteint l'état zombie, GDB enregistrera :[!] ERROR: handle_posix_cpu_timers fired before exit_state == EXIT_ZOMBIE
- Option 1 : Relancez simplement
./pocet sourcez le script GDB quelques fois.- Option 2 : Augmentez
TIMER_FIRE_MSdanspoc.c(par exemple, de#define TIMER_FIRE_MS 10à20), recompilezpoc.cet relancez. Cela accorde au thread un temps supplémentaire pour atteindreexit_notify()et passer àEXIT_ZOMBIEavant l'expiration de la minuterie.
https://github.com/user-attachments/assets/11481b5c-25f8-46c3-be3a-7b79a06a4948
kernel_wait4 au-delà de tsk->sighand = NULL.timer_delete pour appeler release_posix_timer() (libérant sigq).