
PoC e script GDB auxiliam no acionamento do CVE-2025-38352
Um Proof-of-Concept (PoC) reproduzível e um script de orquestração automatizada de GDB para CVE-2025-38352, uma condição de corrida Time-of-Check to Time-of-Use (TOCTOU) no subsistema de temporizadores POSIX de CPU do kernel Linux (kernel/time/posix-cpu-timers.c) que leva a Use-After-Free (UAF).
Aviso: Este projeto destina-se apenas a fins educacionais, defensivos e de pesquisa em segurança. Todos os testes e reproduções foram conduzidos em uma máquina virtual ARM64 QEMU isolada.
O Processo A cria o Processo filho B.
O Processo B cria um temporizador de CPU e começa a encerrar, entrando no estado zumbi. Assim que B está encerrando, uma interrupção de temporizador chega em seu núcleo de CPU. O kernel entra no contexto de interrupção para processar o temporizador, mas libera temporariamente seu lock (unlock_task_sighand) para evitar deadlocks.
No exato mesmo momento em outro núcleo, o Processo A faz o reap do B em encerramento, limpando seu manipulador de sinais (sighand = NULL) e removendo seu PID da hash.
Outra thread em B chama a exclusão do temporizador (posix_cpu_timer_del). Ela tenta localizar a tarefa através de seu PID e verificar seu manipulador de sinais, mas os encontra já limpos / NULL. Interpretando erroneamente que a tarefa está completamente morta e que nenhum temporizador poderia estar disparando, ela assume que é seguro e libera o temporizador da memória.
Enquanto isso, o manipulador de interrupção do temporizador no primeiro núcleo retoma para disparar o temporizador expirado. Como o temporizador acabou de ser liberado no Passo 4, acessá-lo dispara um Use-After-Free e derruba o kernel.
poc_gdb_script.py)Como o stub GDB do QEMU não suporta set non-stop on (pausar uma vCPU pausa todas as vCPUs), a sincronização multithread padrão não pode ser alcançada via breakpoints simples.
Este repositório resolve o problema via Patch de Instruções em Memória:
Estágio 1 (Barreira de CPU):
exit_notify (CPU 2)kernel_wait4 (CPU 0)__arm64_sys_timer_delete (CPU 1)handle_posix_cpu_timers (CPU 2)$pc com o opcode de auto-desvio ARM64 0x14000000 (loop infinito b .) e retoma a execução.Estágio 2 (Orquestração Sequencial):
set scheduler-locking on) e avança cada vCPU sequencialmente:
handle_posix_cpu_timers além de unlock_task_sighand().Baixe ou clone a árvore de código-fonte do Android Common Kernel no 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()Abra kernel/time/posix-cpu-timers.c, localize a função run_posix_cpu_timers() (por volta das linhas 1435–1448) e comente as linhas do 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, os temporizadores de CPU são executados a partir do contexto detask_workem vez da IRQ do temporizador, contornando esta condição de corrida. Portanto, ele deve ser desabilitado para reproduzir a vulnerabilidade.
Como CONFIG_POSIX_CPU_TIMERS_TASK_WORK não possui uma string de prompt no Kconfig upstream, ele assume o padrão y e não pode ser alternado diretamente no menuconfig. Você pode expô-lo da seguinte forma:
kernel/time/Kconfig (por volta da linha 56), adicione uma string de prompt e altere o padrão:
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
Compile a imagem do kernel ARM64:
time ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make -j$(nproc) Image
Compile cruzadamente poc.c:
aarch64-linux-gnu-gcc -ggdb3 ./poc.c -o poc
Inicie sua máquina virtual QEMU com 4 vCPUs (-smp 4) e habilite o 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
Do seu terminal host, anexe o GDB ao QEMU e carregue o script de orquestração:
gdb-multiarch vmlinux
pwndbg> target remote :1234
pwndbg> source poc_gdb_script.py
pwndbg> continue
Dentro do shell do guest QEMU, execute o binário compilado:
./poc
[!TIP] Timing e Solução de Problemas:
Se o temporizador disparar prematuramente antes que a thread alvo tenha atingido o estado zumbi, o GDB registrará:[!] ERROR: handle_posix_cpu_timers fired before exit_state == EXIT_ZOMBIE
- Opção 1: Simplesmente execute novamente
./poce carregue o script GDB algumas vezes.- Opção 2: Aumente
TIMER_FIRE_MSempoc.c(por exemplo, de#define TIMER_FIRE_MS 10para20), recompilepoc.ce execute novamente. Isso concede à thread tempo extra para atingirexit_notify()e transicionar paraEXIT_ZOMBIEantes que o temporizador expire.
https://github.com/user-attachments/assets/11481b5c-25f8-46c3-be3a-7b79a06a4948
kernel_wait4tsk->sighand = NULLtimer_delete para chamar release_posix_timer() (liberando sigq).