
CVE-2025-38352のトリガーを支援するPoCおよびGDBスクリプト
CVE-2025-38352 の再現可能な概念実証 (PoC) および自動化された GDB オーケストレーションスクリプトです。これは Linux カーネルの POSIX CPU タイマーサブシステム (kernel/time/posix-cpu-timers.c) における Time-of-Check to Time-of-Use (TOCTOU) 競合状態であり、Use-After-Free (UAF) を引き起こします。
免責事項: 本プロジェクトは教育、防御、およびセキュリティ研究のみを目的としています。すべてのテストと再現は、隔離された ARM64 QEMU 仮想マシン内で実施されました。
プロセス A が子プロセス B を生成します。
プロセス B は CPU タイマーを作成し、終了を開始してゾンビ状態に入ります。B が終了しようとしているまさにその時、その CPU コアにタイマー割り込みが到着します。カーネルはタイマーを処理するために割り込みコンテキストに入りますが、デッドロックを避けるために一時的にロックを解放します (unlock_task_sighand)。
まったく同じ時刻に別のコアで、プロセス A が終了中の B を回収 (reap) し、そのシグナルハンドラをクリーンアップ (sighand = NULL) して PID をアンハッシュします。
B の別のスレッドがタイマー削除 (posix_cpu_timer_del) を呼び出します。そのスレッドは PID を介してタスクを検索し、シグナルハンドラを確認しようとしますが、それらはすでにクリーンアップされている / NULL であることを発見します。タスクが完全に死んでおり、タイマーが発火する可能性はないと誤解し、安全であると仮定してメモリからタイマーを解放します。
その間、最初のコアのタイマー割り込みハンドラが再開し、期限切れのタイマーを発火させます。ステップ 4 でタイマーがちょうど解放されたため、それにアクセスすると Use-After-Free が発生し、カーネルがクラッシュします。
poc_gdb_script.py)QEMU の GDB スタブは set non-stop on をサポートしていないため (1 つの vCPU を停止するとすべての vCPU が停止します)、標準的なマルチスレッド同期を単純なブレークポイントで実現することはできません。
本リポジトリは インメモリ命令パッチング によってこの問題を解決します:
ステージ 1 (CPU バリア):
exit_notify (CPU 2)kernel_wait4 (CPU 0)__arm64_sys_timer_delete (CPU 1)handle_posix_cpu_timers (CPU 2)$pc を ARM64 の自己分岐オペコード 0x14000000 (b . 無限ループ) でパッチし、実行を再開します。ステージ 2 (順次オーケストレーション):
set scheduler-locking on)、各 vCPU を順番に前進させます:
handle_posix_cpu_timers を unlock_task_sighand() の先へ進めます。kernel_wait4 を の先へ進めます。コミット 1bf1aa362e6b9573a310fcd14f35bc875b42ba83 の Android Common Kernel ソースツリーをダウンロードまたはクローンします:
# 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() のパッチを元に戻すkernel/time/posix-cpu-timers.c を開き、関数 run_posix_cpu_timers() (およそ 1435~1448 行目) を探し、パッチ行をコメントアウトします:
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_WORK を無効化する注記:
CONFIG_POSIX_CPU_TIMERS_TASK_WORK=yの場合、CPU タイマーはタイマー IRQ ではなくtask_workコンテキストから実行されるため、この競合状態を回避します。したがって、脆弱性を再現するにはこれを無効にする必要があります。
CONFIG_POSIX_CPU_TIMERS_TASK_WORK は上流の Kconfig にプロンプト文字列がないため、デフォルトで y になり、menuconfig で直接切り替えることができません。次のようにして公開できます:
kernel/time/Kconfig (およそ 56 行目) で、プロンプト文字列を追加し、デフォルトを変更します:
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
ARM64 カーネルイメージをコンパイルします:
time ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make -j$(nproc) Image
poc.c をクロスコンパイルします:
aarch64-linux-gnu-gcc -ggdb3 ./poc.c -o poc
4 つの vCPU (-smp 4) で QEMU 仮想マシンを起動し、GDB スタブ (-s フラグ、ポート 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
ホストのターミナルから、GDB を QEMU にアタッチし、オーケストレーションスクリプトをロードします:
gdb-multiarch vmlinux
pwndbg> target remote :1234
pwndbg> source poc_gdb_script.py
pwndbg> continue
QEMU ゲストのシェル内で、コンパイル済みバイナリを実行します:
./poc
[!TIP] タイミングとトラブルシューティング:
対象スレッドがゾンビ状態に到達する前にタイマーが早まって発火した場合、GDB は次のようにログ出力します:[!] ERROR: handle_posix_cpu_timers fired before exit_state == EXIT_ZOMBIE
- オプション 1: 単に
./pocを再実行し、GDB スクリプトを数回 source してください。- オプション 2:
poc.cのTIMER_FIRE_MSを増やし (例:#define TIMER_FIRE_MS 10から20へ)、poc.cを再コンパイルして再度実行してください。これにより、タイマーが期限切れになる前にスレッドがexit_notify()に到達しEXIT_ZOMBIEへ遷移するための追加時間が与えられます。
https://github.com/user-attachments/assets/11481b5c-25f8-46c3-be3a-7b79a06a4948
tsk->sighand = NULLtimer_delete を進めて release_posix_timer() を呼び出させます (sigq を解放)。