
CVE-2026-98260(posix-cpu-timers の UAF)に関する Android カーネル LPE 研究:トリガー PoC、偽クレデンシャルおよび実クレデンシャルによる uid-0 シェルチェーン、さらに Shizuku/KernelSU ブートストラップアプリ。
日本語 | 简体中文
posix-cpu-timers の終了パスにおける use-after-free に関する Android ローカル権限昇格の研究。このリポジトリには、トリガーのみの PoC、偽クレデンシャル検証エクスプロイト、実クレデンシャルによる uid-0 シェルステージ、Shizuku ベースの KernelSU アプリ、および脆弱性の事前チェックが含まれています。
ブランチ
magicos-11.0.0.109-6.12.58: このブランチはデュアルプロファイルです。APK とネイティブバイナリは、実行時に正確なuname -rからプロファイルを選択します。MagicOS 10.0.0 / 6.12.38 が検証済みプロファイルであり、MagicOS 11.0.0.109 / 6.12.58 は静的に適応されたもので、デバイス側の実行時検証はまだ保留中です。APK は両方の 226 シンボルのko-symsテーブルを同梱し、一致するものを選択します。
| 項目 | 値 |
|---|---|
| CVE | CVE-2026-98260 |
| クラス | LPE (use-after-free、timerqueue rb-tree 消去) |
| ターゲット | Honor MagicPad3 Pro 13.3 YLP-W00、MagicOS 10.0.0 / 11.0.0.109 (Android 16)、ブランチ magicos-11.0.0.109-6.12.58 |
| カーネル | 6.12.38-android16-5-gfde7767f6ef6-abogki481467632-4k (検証済み)6.12.58-android16-6-g3a7ac1674240-abogki541502914-4k (適応済み) |
| プロファイルステータス | デュアルプロファイル; 6.12.38 は検証済み、6.12.58 は実行時検証待ち |
| SELinux | Enforcing |
| パッチ | テスト済みビルドには存在しない |
| 緩和策 | KASLR オン、SMEP/SMAP/PAN オン、KASAN はコンパイル済みだが無効 |
| ブートローダー | ロック済みでロック解除不可; パーティション書き込みなし |
| コンポーネント | ステータス | 結果 |
|---|---|---|
src/poc.c | 6.12.38 で検証済み | 破壊的トリガー; reap が cleanup_timerqueue でパニック |
src/exp.c | 6.12.38 で検証済み | 偽クレデンシャル; getuid()==0 && geteuid()==0; exec 不可 |
src/uid0.c | 6.12.38 で検証済み | 実クレデンシャル; u:r:shell:s0 での uid-0 シェル |
app/ | 6.12.38 で検証済み | Shizuku + KernelSU ブートストラップ; u:r:ksu:s0 での uid 0 |
src/vuln_precheck.h | 追加済み | プロファイル/環境ゲート; --check / --force |
| 6.12.58 プロファイル | 適応済み | サイト、selinux_state およびその 226 シンボルの ko-syms テーブルを再導出; デバイス実行は保留中 |
| デュアルプロファイル APK | 6.12.38 でテスト済み | uname -r によるプロファイルと ko-syms の選択; 6.12.38 のライブ KSU パスは合格 |
現在のリリース: v1.4.3-app。
v1.4.2-app プレリリースは 2026-10-30 に撤回されました。そのタグは履歴目的でのみ保持されています。リリース記録と既知のギャップについては docs/release-notes.md を参照してください。
src/poc.c trigger only; the reap panics on the tested kernel
src/exp.c fake-cred chain; verifies uid 0 via getuid/geteuid
src/uid0.c real-cred chain; zeroes cred->uid/gid and execs sh
src/kernel_profile.h per-release call sites, symbols and structure offsets
src/trace_ids.h runtime kmem trace event IDs; unprivileged probe
src/vuln_precheck.h shared profile/environment gate
app/ Android root app (Shizuku + KernelSU bootstrap)
docs/analysis.md root cause, exploit chain and device notes
scripts/dump_device.sh root device dump: all .ko/modinfo, kernel/config/policy, perf/tracefs, vendor mount, harness, active boot.img
Makefile single build entry (NDK cross-compile, app APK)
README.zh-CN.md Chinese version of this document
非リーダースレッドが CLOCK_THREAD_CPUTIME_ID の posix タイマーを作成した後、de_thread() の後に execve() が失敗すると、タイマーノードがキューに残り続けます。exec を中断する SIGKILL により、スレッドは exit_itimers() を通じて終了しますが、この関数はタスクがすでに終了処理中のタイマーを解除できません。k_itimer は解放されますが、その timerqueue_node はタスクの posix_cputimers キューにまだリンクされたままです。親プロセスがゾンビを回収すると、release_task() -> posix_cpu_timers_exit() -> cleanup_timerqueue() が解放済みノードに対して timerqueue_del()/rb_erase() を呼び出します。
この消去処理が書き込みプリミティブとなります。rb_erase() は制御された __rb_parent_color 値を制御された rb_left ターゲットに書き込みます。エクスプロイトは slab/ページの回収を巧みに調整し、解放されたノードが攻撃者のペイロードで埋め尽くされたページ内に位置するようにした上で、ターゲットと値を選択します。
観測されたパニックトレース、YLP-W00 のオフセット、tracefs ベースのリーク、SLUB/RCU の調整、2段階の fake-cred チェーン、および1回書き込みの real-cred チェーンについては docs/analysis.md を参照してください。
要件: /opt/android-ndk-r29 にある Android NDK r29(NDK= で上書き可能)。ターゲットは aarch64、デフォルトで API 24 です。アプリにはさらに Android SDK(build-tools 37.0.0、platform 37)、JDK、および ~/.local/share/android-cve-env/cve98260-app 配下の Shizuku jar が必要です(APP_DEPS= で上書き可能)。ビルド成果物は build/ に保持され、git で無視されます。Shizuku jar はユーザーが用意するもので、scripts/fetch-deps.sh が deps.lock(SHA-256)に対して検証し、何もダウンロードしません。
| ターゲット | 出力 / アクション |
|---|---|
make / make poc | build/poc |
make exp | build/exp |
make uid0 | build/uid0(静的; シェルステージが自身を再実行) |
make build-all | poc + exp + uid0 + apk をビルド |
make host | ホスト側 qemu 検証用の build/poc.static |
make emu | qemu で build/poc.static を実行(--force 付き) |
make apk | app/CVE98260Root.apk |
make clean | build/ を削除 |
make help | ターゲットを一覧表示 |
poc、exp、uid0 はカーネルに触れる前にプロファイルゲートを呼び出します。これは正確なプロファイルリリース(6.12.38 検証済み、6.12.58 適応済み)のいずれかを受け入れ、arm64 と 4 KiB ページを要求し、exp/uid0 についてはさらに tracefs/kmem の可用性と(fake-cred チェーンの場合)panic_on_warn=0 をチェックします。その他のリリースは未確認であり、プログラムは終了コード 2 で終了します:
build/uid0 --check # print the verdict, do not trigger
build/uid0 --check --force # same, overriding an unconfirmed environment
--force または CVE98260_FORCE=1 は、カーネルを独立して検証した後にのみ使用してください。--force は環境チェックのみを緩和します。exp、uid0、およびアプリは引き続き kernel_profile.h からの正確なプロファイルを必要とします。このゲートは意図的にランタイムプローブにはしていません。バグに到達するプローブ自体が、古い timerqueue ノードを残してしまうからです。
YLP-W00 では、非特権シェルは /proc/sys/kernel/panic_on_warn や /proc/cmdline を読み取ることができません。既知のプロファイルの場合、ゲートは 6.12.38 で検証済みの値 0 を仮定します。同じ仮定は、ランタイム検証が完了するまで、適応された 6.12.58 プロファイルにも使用されます。未知のリリースはブロックされたままです。
エクスプロイトは通常のシェルプロセス(uid 2000、SELinux u:r:shell:s0)として開始され、su は使用しません。su 経由で起動しないでください。テストされる条件は非特権での起動です。
make uid0
adb push build/uid0 /data/local/tmp/uid0
adb shell chmod 755 /data/local/tmp/uid0
adb shell /data/local/tmp/uid0 # runs id and prints the context
adb shell /data/local/tmp/uid0 shell # interactive sh -i
完全なチェーンを実行する前に、非破壊モードを使用してください:
adb shell /data/local/tmp/uid0 --probe-ids # discover kmem trace event IDs
adb shell /data/local/tmp/uid0 leak # timer/cred leak only; no UAF/reap
kmem の trace イベント ID は起動時に割り当てられ、カーネルビルド間で安定しません(6.12.38 では 318/319/322/324 を使用)。--probe-ids は書き込み可能な rss_stat/trigger を通じて一度に 1 つのイベントを分離し、生のレコードタイプを発見します。通常の実行では自動的にプローブします。leak は [base]、完全な timer_site/slide、および run_plant() に到達せずに cred 割り当てサイトを出力します。
root は静的であり、その子プロセスはプライベートな実 cred を取得するためにバイナリを再実行するため、プッシュされたパスに留まる必要があります(/proc/self/exe 経由で exec されます)。
成功時には出力は次のように終わります:
[child] woke uid=0 euid=2000
[child] setuid(0)=0 errno=0 resuid=0/0/2000
uid=0(root) gid=0(root) groups=0(root),...
context=u:r:shell:s0
ROOT_SHELL_OK
adb セッションが中断される可能性がある場合は、エクスプロイトをデタッチモードで起動してください。待機中のプロセスは決して kill してはなりません(制限事項を参照):
adb shell 'setsid /data/local/tmp/uid0 >/data/local/tmp/uid0.log 2>&1 &'
未検証の状態(victim slab が解放されていない、対象ページが別の割り当てに奪われた、予期しない reap ステータスなど)では、プログラムは reap せずに待機します。古いノードを無制御に reap すると、このカーネルはパニックを起こします。安全なクリーンアップは再起動のみです。
app/ は、YLP-W00 上の非特権 Shizuku シェルから KernelSU をブートストラップする Android UI です。エクスプロイトコアをネイティブライブラリとして再利用し、src/poc.c と src/exp.c は変更しません。UI は意図的に小さく保たれています: Shizuku を認可し、アセットをデプロイし、チェーンを開始し、一時ファイルをクリーンアップし、可視ログをエクスポート/コピーします。
make apk # -> app/CVE98260Root.apk
adb install -r app/CVE98260Root.apk
典型的な流れ:
libmagicroot.so を
/data/local/tmp/cveapp 配下に配置します。kernelsu がすでに稼働中であれば su -c id の検証のみを行います。アプリは実行開始時に以前の認可/デプロイの行をクリアし、リモートの run.sh の出力を
UI にストリーミングします。ネイティブワーカーはさらに
/data/local/tmp/cve98260-worker-<pid>.log に追記するため、UI のパイプが閉じても
証拠が失われることはありません。导出日志 は /sdcard/MagicRoot.log を上書きします。
実行中のジョブは最初の 4 つのボタンを無効化し、ジョブ終了時に再有効化されます。
このアプリは YLP-W00 専用です。マネージャーの KMI モジュール、パッケージ化された
プロファイル別の ko-syms-6.12.38.txt / ko-syms-6.12.58.txt、および選択した
プロファイルの selinux_state アドレスを使用します。汎用的な root アプリではありません。
ネイティブライブラリはチェーン実行前に共有の事前チェックを呼び出し、ライブ実行では
最初に magicroot --check も実行します。