
CVE-2026-43499 Google TV搭載Chromecast(sabrina)向けGhostLock rootエクスプロイト
Chromecast with Google TV(sabrina)における CVE-2026-43499 を悪用した root エクスプロイト -- Linux カーネルの futex PI(優先度継承)サブシステムにおける use-after-free です。
Android 14、カーネル 5.15.170(PGO+BOLT+LTO、clang 17)を搭載したブートローダーロック状態のデバイスで root を取得します。
GhostLock は、rt_mutex_start_proxy_lock() の -EDEADLK ロールバックパスから呼び出される remove_waiter() のバグを悪用します。この関数は、ウェイタータスクのものではなく、current->pi_blocked_on(requeuer のもので、すでに NULL)をクリアしてしまいます。ウェイターの pi_blocked_on は決してクリアされず、カーネルスタック上の解放済み rt_mutex_waiter へのダングリングポインタが残ります。
その後の sched_setattr が rt_mutex_adjust_pi -> rt_mutex_adjust_prio_chain をトリガーし、ダングリングポインタをたどって、再利用されたヒープページ上の攻撃者制御データを対象に PI チェーンをたどります -- これにより rb_erase を介した任意書き込みプリミティブが得られます。
| ステージ | 手法 |
|---|---|
| KASLR リーク | PERF_SAMPLE_IP を用いた perf_event_open(TID ゲート付き) — 最小カーネル IP は .entry.text(+0x10000)内にあり、_text は 2MB アラインメントで KASLR スライドは 2MB の倍数であるため、min_ip & ~0x1fffff により実行時の _text を正確に復元できる |
| タスク構造体リーク | PERF_SAMPLE_REGS_INTR を用いた perf_event_open(TID ゲート付き、リニアマップアドレスのモード) |
| シンボルテーブル検証 | setpriority(-20) ストーム中に security_capable() のレジスタ引数から perf-samples で &init_user_ns を取り出し、kaslr_base + off_init_user_ns と照合する — これにより、実行中のカーネルの .data/.bss レイアウトが、ブラインド書き込み前にオフセットテーブルと一致することを証明する |
| mm_struct リーク | KernelSnitch -- futex ハッシュ衝突タイミングのサイドチャネル |
| ヒープスプレー | SLUB 破棄の振り付け(memfd-close、CPU パーシャルオーバーフロー)+ io_uring_setup(256) による order-2 ページ回収 |
| スタックオーバーレイ | AF_UNIX SOCK_SEQPACKET sendmsg -- move_addr_to_kernel が 128 バイトの sockaddr をカーネルスタックにコピーし、ダングリングウェイターの task/lock/prio フィールドを上書きする |
| ウォークトリガー | 単調な nice ラダー(7 -> 14 -> 19)を伴う sched_setattr が PI チェーンウォークを発火させる(オーバーレイのラウンドごとに 1 回) |
| 書き込みプリミティブ | rb_erase ケース 1: `{pc=(TARGET-8) |
| SELinux 無効化(ウォーク 0) | selinux_state への 8 バイトゼロ書き込みにより enforcing、checkreqprot、initialized、policycap[0..4] がクリアされる — avc_denied() は決して拒否せず(enforcing=0)、security_compute_av() は allow-all にショートサーキットする(!initialized)ため、スワップ後のカーネル SID(u:r:kernel)は問題にならなくなる |
| 資格情報スワップ(ウォーク 1+2) | task->cred = fake_cred および task->real_cred = fake_cred(uid=0、caps FULL、スプレーページ上の自己完結型フェイク user_namespace。両方の書き込みが必要 — exec 時の commit_creds() は cred == real_cred でない限り BUG_ON する) |
| root バッテリー | raw システムコールのみのプローブ(uid/SELinux//proc/1//dev/kmsg/wifi/packages//data/data/kallsyms)を /data/local/tmp/.ghostlock_out に出力 |
| root シェル | execve("/system/bin/sh") — exec パス内の commit_creds が fake_cred をクリーンな slab 資格情報にコピーする。計画されたすべての消去が完了したことを条件とする |
rb_right = 0 を伴う rb_erase プリミティブは、任意のカーネルアドレスに対して単一のクリーンな 8 バイトゼロストアを実行します(サイドストアなし、不正なデリファレンスなし)。selinux_state の最初の qword をゼロ化すると二重パーミッシブ(enforcing=0 + !initialized → allow-all)になり、カーネル SID にもかかわらず root 取得後のすべてのファイル I/O がブロック解除されますsecurity_capable() 経路から &init_user_ns をリークし、kaslr_base + off_init_user_ns と一致しない限り cred のみのフローへ中止しますucounts = NULL を持たせます -- cap_capable は最初のイテレーションで一致し、inc_rlimit_ucounts は 1 回のループで終了するため、&init_user_ns をリークする必要がなくなりますPERF_SAMPLE_TID フィルタにより、呼び出し元スレッドのレジスタスナップショットのみがカウントされ、ホットなシステムサービスがモード投票を支配するのを防ぎます5.15.170-android14-11-gf4a1f03072af(aarch64、PGO+BOLT+LTO、clang 17.0.2)CONFIG_FUTEX_PI=y、CONFIG_IO_URING=y、perf_event_paranoid=-1、SELinux enforcing、panic_on_oops=1、ユーザー名前空間なしexport ANDROID_NDK_HOME=/path/to/android-ndk
make -j$(nproc)
Android NDK が必要です(r27 でテスト済み)。静的にリンクされた aarch64 バイナリを生成します。
adb push ghostlock /data/local/tmp/
adb shell "cd /data/local/tmp && ./ghostlock --cred"
エクスプロイトは約25秒かかります(KASLR/タスク/レイアウトのリーク + ヒープスプレー + KernelSnitchのブルートフォース + 3秒のオーバーレイを3ラウンド)。成功すると、rootバッテリーを /data/local/tmp/.ghostlock_out に、マーカーを /data/local/tmp/.ghostlock_root に書き込み、寛容なカーネル下で uid=0 の /system/bin/sh を exec します。
環境変数による調整:
GHOST_SELINUX=0 — SELinux書き込みをスキップし、実績のあるcredのみのルート(モード6)を実行GHOST_SELINUX_FORCE=1 — デバイスのシンボルレイアウト検証が失敗してもSELinux書き込みを有効化GHOST_EXEC=0 — バッテリーを書き込んでシェルをexecする代わりに exit(99) するCRED_ATTEMPTS=n — 実行がreclaimを逃した場合のフルスプレー再試行回数(デフォルト3)このエクスプロイトは Claude Opus 4.6(Anthropic)によって GLM-5.3(Z.ai)をカーネルエクスプロイトコンサルタントとして迎えて移植・開発されました。
kernel/futex/、kernel/locking/rtmutex.c、lib/rbtree.cこのエクスプロイトはセキュリティ研究および教育目的で公開されています。対象は研究者が所有するデバイスです。所有していないデバイス、または許可なく使用しないでください。
MIT