
Android GKI 6.12 デバイス上の CVE-2026-43499 を悪用します: 決定的な任意カーネル読み取り/書き込み、KASLR バイパス、および LD_PRELOAD ペイロードによる完全な root 取得。
Android GKI 6.12 系(Samsung および Pixel デバイス)向け Linux カーネルエクスプロイトで、CVE-2026-43499 を標的とします。これは rt_mutex_start_proxy_lock() 内のバグのある remove_waiter() ロールバックで、waiter::task の代わりに current を使用するため、ウェイターの pi_blocked_on が(後でポップされる)カーネルスタックの rt_mutex_waiter を指したままになります。これに pselect() の fd_set スタック上書きと、コンシューマ側の sched_setattr による偽 rb_tree 走査を組み合わせることで、決定的なカーネル書き込みプリミティブと完全な root を獲得します。
このエクスプロイトは LD_PRELOAD 共有ライブラリ(preload.so)として動作し、root 化後のアーティファクトとして root su デーモンと壁紙をインストールします。
ステータス: 積極開発中。ターゲットごとのスタック深度オフセット(
PSELECT_*ワードシフト)とスライド動作はデバイスによって異なり、実機での検証が続いています。m1q(ZF1)ターゲットが現在のブリングアップの焦点です。
FUTEX_CMP_REQUEUE_PI がウェイターをリキューし、リキューのデッドロック検出チェーン走査が -EDEADLK を返すと、__rt_mutex_start_proxy_lock() は remove_waiter()(rtmutex.c:1535)によってロールバックします。このロールバックは待機ツリーからウェイターを正しくデキューしますが、waiter->task->pi_blocked_on の代わりにリキュー呼び出し元の pi_blocked_on をクリアします。ウェイターの pi_blocked_on は、futex がタイムアウトするとポップされる自身のスタック上の rt_mutex_waiter を指したまま(ダングリング)になります。
タイムアウト後、ウェイターのカーネルスタック領域は再利用されます。core_sys_select() は 3 つの fd_set をそのスタックバッファにコピーします(ZF1 では nfds < 344 のスタックパス)。細工された fd_set ワード配列により、偽の rt_mutex_waiter / 偽タスク / 偽 rt_mutex(ポインタが制御された rb_tree ルートを持つ)が再構築されます。その後、コンシューマスレッドが sched_setattr_tid(waiter) → rt_mutex_adjust_pi() → rb_erase_cached を呼び出し、制御された値の任意アドレス書き込みを引き起こします。
このプリミティブ全体には PI チェーンサイクルが必須です。オーナーが f_pi_target を保持し、さらに(ウェイターが保持する)f_pi_chain でブロックしているため、チェーン走査は owner → chain → waiter → target → owner となり、-EDEADLK で失敗します。このサイクルを取り除くと、リキューはカーネルに何の影響も与えず成功を返します。
sched_blocked_reason(m1q の主要経路): ブロックした kworker の保存済みリターン PC(stack_trace_save_tsk)をリングバッファから読み取り、コンパイル時に埋め込まれた worker_thread オフセットと比較します。SLIDE_LOGGERS_0_1 を植え付けます。リークした値から stext を再構築します。mm_struct サイズのオブジェクトをスプレーし、futex ハッシュの衝突を利用してヒープ上の mm_struct を特定します。偽オブジェクトのスプレーベースとして使用するカーネルヒープページアドレスをリークします。.data の ashmem file_operations の write_iter スロット(+0x30)に configfs_bin_write_iter を書き込みます。その後 ashmem を開くと、configfs のバイナリファイルシステム経路を通じて任意のカーネル読み取り/書き込みが可能になります。オプションの STAGE3=1 フェーズです。ブリッジ子プロセスをフォークし、その mm->pgd をステージングした偽ページテーブル(3 レベル: PGD→L1→L2、RX ループリーフ + RW スタックリーフ)に交換し、子プロセスにレジスタのみのアセンブリブロブを実行させて、2MB の物理スキャンウィンドウを通じて自身の cred をパッチします。configfs/pipe プリミティブを使わない全 RAM 物理読み取り/書き込みです。現在は m1q ターゲットにのみ配線されており、実機では未実行です。
src/targets/<codename>-<build>/ に 41 個のターゲットがあります。各ターゲットには最低限 target.h(カーネルシンボルオフセット、KIMAGE_TEXT_BASE、ダイレクトマップベース、構造体オフセット)が必要です。Pixel ターゲット(comet、tokay、tegu、caiman、komodo、frankel、mustang、rango、stallion、blazer)は共有ソースをオーバーライドし、Samsung ターゲット(m1q-*)はデバイス固有のロジックを追加します。
make list-projects # full list
カーネル構造体のレイアウトはデバイスごとに異なるため、オフセットは各ターゲットの実際のカーネルバイナリ / kallsyms に対して検証する必要があります。
出力: build/<PROJECT>/bin/preload.so(LD_PRELOAD で読み込まれる共有ライブラリ)。
# Default project
CC=clang make
# Specific device target (default: blazer-CP2A.260605.012)
CC=clang make PROJECT=m1q-BP4A.251205.006
# Without CC=clang: uses NDK if found ($NDK_ROOT / $ANDROID_NDK_HOME /
# $ANDROID_NDK_ROOT), otherwise host clang + Android sysroot
make PROJECT=m1q-BP4A.251205.006
# Show build configuration
make info
# Clean
make clean
ビルドでは PIE の su_daemon バイナリ(src/su_daemon.c、build/embed/su_daemon_aarch64_pie としてビルド)と assets/wallpaper.webp を、src/su_blob.S / src/wallpaper_blob.S 経由で preload.so に埋め込みます。
ビルド出力を /data/local/tmp にプッシュし、LD_PRELOAD 環境でエクスプロイトを実行します。ライブのブリングアップターゲットでは tee を使わずに実行してください。tee は自身の stdio をバッファリングするため、カーネルパニック時にログの末尾が失われます(エクスプロイトの stdout はアンバッファリングなので、ファイルに直接リダイレクトすればすべての行が保持されます)。
adb push build/m1q-BP4A.251205.006/bin/preload.so /data/local/tmp/preload.so
adb push build/embed/su_daemon_aarch64_pie /data/local/tmp/su_daemon_aarch64_pie
adb shell "chmod 755 /data/local/tmp/preload.so /data/local/tmp/su_daemon_aarch64_pie"
adb shell "LD_PRELOAD=/data/local/tmp/preload.so \
/data/local/tmp/su_daemon_aarch64_pie \
> /data/local/tmp/output.log 2>&1"
成功すると、デーモンは /data/local/tmp/temp_su.sock で待ち受け、su が /apex/com.android.virt/bin の下にインストールされます。
exploit/
Makefile
assets/ # wallpaper.webp (embedded post-root artifact)
src/
main.c # run_exploit() entry point (shared)
preload.c # LD_PRELOAD constructor, su/wallpaper install
util.c # addr translation, page prep, configfs helpers
pipe.c # pipe-based physical read/write
root.c # cred patching / task walk
slide.c # KASLR slide (tracefs + boot_id routes)
fops.c # pselect corruption + fops manipulation
faketables.c # stage-3 fake page-table builders (3-level)
stage3.c # pgd-swap bridge child (no-op unless configured)
stage3_loop.S, stage3_poll.S # bridge blob + enter trampoline
su_daemon.c # root daemon (compiled as separate PIE)
su_blob.S, wallpaper_blob.S # embedded binaries
offset.h # pulls per-target target.h via -DTARGET_CONFIG_H
kernelsnitch/ # mm_struct futex-hash leak library
targets/<codename>-<build>/ # per-device target.h + optional overrides
poc/poc.c # standalone POC (separate from the exploit)
ターゲットは Makefile の pick_src メカニズムを通じて共有ソースをオーバーライドします。src/targets/<PROJECT>/<file>.c が存在する場合は、共有の src/<file>.c の代わりにそれが使用され、存在しない場合は共有版が使用されます。
-EDEADLK(errno 35)で失敗することに依存しています。サイクルがない場合、カーネルに何の影響もないまま success=1 として現れます。PSELECT_WAITER_WORD_SHIFT は 0 でなければなりません(ZF1 のスタック深度は ELF から検証済み)。他のターゲットはまだデフォルトが 1 です。信頼する前にデバイスごとに検証してください。p0_data_alias() は DIRECT_MAP_BASE(リニアマップ)を使用します。これは ZF1 の P0_PAGE_OFFSET とは異なります。参照解除されたカーネルオブジェクトのデータエイリアスはリニアマップを使用する必要があります。pr_error() は致命的です(exit(-1))。warning/info マクロとは異なります。SLIDE_NFULNL_LOGGER ではなく SLIDE_LOGGERS_0_1 を使用します。このワードはランダム化されていないリニアマップエイリアスであるため、boot_id 経路はスライド非依存です(slide=0 のときのみ使用してください。先に tracefs リークを実行します)。これは Android デバイス向けのエクスプロイトコードであり、セキュリティ研究、許可を得た脆弱性テスト、および自分が所有するか明示的なテスト許可を得たハードウェアに対する防御作業を目的としています。所有していないデバイスでの不正使用は違法です。作者は、文鎮化したデバイス、文鎮化したカーネル、または失われた保証について一切責任を負いません。
preload.c が埋め込み su デーモン(tmpfs として /apex/com.android.virt/bin にマウント。adbd 名前空間版とローカル版もあり)をインストールして壁紙を差し替えます。| 変数 | デフォルト | 目的 |
|---|
PSELECT_ROUTE_SHIFT | compile-time | メイン経路(m1q)の A/B fd_set ワードシフト |
SLIDE_SHIFT | compile-time | スライド経路の A/B fd_set ワードシフト |
PSELECT_ROUTE_DELAY_USEC | 50000 | sched_setattr 前のコンシューマ遅延(0 より大きい必要あり) |
SLIDE_CONSUME_USEC, SLIDE_ENTER_DELAY_USEC, SLIDE_CONSUMER_CORE | — | スライド経路のタイミング / CPU 固定 |
SKIP_SLIDE | 0 | ダイレクトマップフォールバックを使用(slide = 0) |
SLIDE_ONLY | 0 | KASLR スライドのみ実行して終了 |
SLIDE_P0_OFFSET, SLIDE_BOOTID_OFF | — | スライドの p0 エイリアス / boot_id オフセットを上書き |
KSNITCH_COLLISIONS | 4 | KernelSnitch の衝突数 |
STAGE3 | 0 | pgd スワップブリッジ子フェーズを有効化(m1q) |
STAGE3_DRYRUN | 0 | テーブルをステージング/検証してから、スワップ前に中止 |
PAGE_RECLAIM_SENDS, PSELECT_SIMPLE_LAYOUT | — | スプレー / fd-set レイアウトの調整 |
TMP_PAGE_* / TMP_UNAME_* | — | tmp_page 実験経路(m1q) |