
adb駆動のKernelSUローダー(ストックGoogle Pixel向け):CVE-2026-43499(GhostLock)による一時的なカーネルR/Wを利用し、実行中のKMIに一致する署名検証済みのkernelsu.koを後からロードします。マネージャー非依存。
adb駆動のツールで、ロックされたブートローダーを搭載したストックのGoogle Pixelを、ブートローダーのロック解除やブートイメージの変更なしにKernelSUルート化されたデバイスに変えます。ホストからデバイス上の非特権ユーザースペースカーネルエクスプロイトを実行して一時的なカーネル読み書きを取得し、そのプリミティブを使ってルート資格情報をパッチし、実行中のGKIカーネルにKernelSUローダブルカーネルモジュール(kernelsu.ko)を後からロードして、すでにインストールされているKernelSUマネージャー(KernelSU、KernelSU-Next、SukiSU、または他のバリアント)に制御を委ねます。Android 17のPixelをGKI 6.1および6.6カーネルで対象とし、adb shell経由でフロー全体を駆動して、決定的なシーケンスとタイムスタンプ付きログを実現します。
デバイス側のペイロードは、CVE-2026-43499(「GhostLock」)のベンダー提供フルチェーンLPEで、kernel/locking/rtmutex.cにおけるfutex/rtmutex優先度継承スタックのuse-after-freeです。requeue-PIロールバックパスでは、remove_waiter()が実際のウェイターではなくrequeuer(current)のpi_blocked_onをクリアするため、解放されたカーネルスタックスロットにrt_mutex_waiterを保持していたダングリングポインタが残ります。このバグは通常の非特権プロセスから到達可能です:
owner、waiter、consumer)がPIチェーンを構築します。ウェイターはFUTEX_WAIT_REQUEUE_PIで待機し、メインスレッドがFUTEX_CMP_REQUEUE_PIを発火し、consumerからのsched_setattrがロールバックを駆動します。pselect()/select()(一部の6.1ターゲットではTCP_ZEROCOPY_RECEIVEルート)によって再利用され、そのfd_setワードがウェイター構造体の上に配置され、偽造されたフラットなrt_mutex_waiterが書き込まれるため、ダングリングポインタが攻撃者制御のrb-treeおよびロックフィールドを辿ります — 単一の制御されたポインタ書き込みプリミティブです。mm_struct/sk_buff/pipe_bufferオブジェクトを保持するスラブページを特定します。ルートとSELinuxの状態は、パイププリミティブを通じてパッチされます:ルート子タスクのcredはuid/gid 0にゼロ化され完全なケーパビリティセットが設定され、そのSELinux osid/sidはSECINITSID_KERNELに設定され、seccompはクリアされ、selinux_state.enforcingは0に設定されます。
エクスプロイトチェーン、KASLRオラクル、およびKernelSnitchサイドチャネルは、NebuSecのIonStack Part II — GhostLock研究(NebuSec/CyberMeowfia PoC、Apache-2.0)に由来し、Pixel/aarch64用にここで適応されています。帰属とライセンスを参照してください。
チェーンのちょうど1つのステージだけがカーネルをパニックさせる可能性があります:KASLRスライド導出で、これは回収したと期待するページと競合します。他のすべてのステージは再試行可能で、カーネルテキストベースは単一ブートの存続期間中固定されています。ホストフローはこの特性に基づいて分割されます:
KASLR_BASEなしで実行されます。偽造ウェイター書き込みがrandom_table sysctl ctl_table.dataを既知のカーネルテキストポインタに再ポイントします。/proc/sys/kernel/random/boot_idの読み取りがproc_do_uuid()を通じてそれをリークし、イメージオフセットを差し引くと_stext/KASLRベースが得られます。restore_slide_boot_id()が破損したctl_table.dataを修復します。失われた競合はデバイスを再起動するため、すべての試行の前にブート待機が行われ、生存チェックが消失をパニックとして分類します。成功すると、デバイスログはslide-kaslr-ok pid=<pid> base=<hex>を出力し、ベースは現在のブートに固定されます。KASLR_BASE=0x<base>をエクスポートして再実行されます。このパスは決してパニックせず、一時的なsuを通じてidがuid=0を報告するまでループされます。接続されたデバイスは実行時にdata/targets.jsonに対して解決されます。デバイスにハードコードされたものはありません。2つの独立した解決が行われます:
uname -r)から取得され、一致したターゲットエントリから、またはリリース文字列(例:android14-6.1)から導出されます。多くのデバイスにわたる1つのペイロードの再利用は、GKI/KMI構造に従います。同じGKIビルド上のすべてのデバイスは、バイト単位で同一のvmlinuxを実行し、構造体フィールドオフセット(task_struct->cred、cred->uidなど)は、KMI型契約とMODVERSIONS CRC強制によってKMIブランチの存続期間中固定されます。対照的に、絶対カーネルシンボルアドレスはリンカーがab<NNN>ビルドごとに決定するため、エクスプロイトの固定オフセットは特定の1つのvmlinuxに属します。したがって、異なるカーネルイメージは、KMIが一致していても異なるペイロードを必要とします。data/targets.jsonはまさにこれをエンコードしています:多くのデバイスがカーネルイメージをキーとする1つのペイロードに重複排除される一方、異なるカーネルイメージは独自のペイロードを取得します。
LKM後期ロードには、ローダブルモジュールサポートを備えたGKIカーネル(5.10+)とKMI一致の.koが必要です。KernelSUカーネルモジュールは、カーネル内でマネージャーAPKのv2署名ブロックを検証し、署名証明書のSHA-256を.koにコンパイルされたKSU_EXPECTED_SIZE/KSU_EXPECTED_HASHペアと比較することでマネージャーを認証します。したがって、マネージャーリリース内に同梱されるkernelsu.koとそのマネージャーのAPKは1つの署名アイデンティティを共有します。不一致のksudはドライバーをロードしますが、マネージャー認証ビットを設定することはなく、デバイスに使用可能なルートが残りません。
フローはこの結合を尊重します:インストールされたマネージャーのAPKパス(pm path <manager package>)を解決し、APKをプルし、lib/arm64-v8a/libksud.soをksudバイナリとして抽出し、マネージャーがインストールされていない場合は中止します。一時的なルートを保持した状態で、そのksudはルート所有の実行可能ファイルとしてステージングされ、ksud late-load --kmi <kmi> --package-name <manager package>として呼び出されます。後期ロードは現在のKMIを検出し、埋め込みアセットから"{kmi}_kernelsu.ko"をプルし、手動シンボル再配置を実行し(各SHN_UNDEFシンボルを/proc/kallsymsに対して解決し、エントリをSHN_ABSに書き換え)、パッチされたバッファに対してinit_module(2)を呼び出します。その後、initが行う残りのブートパイプラインを実行します(ksudのインストール、restorecon、sepolicy.ruleとルートプロファイルのロード、post-fs-data/ステージスクリプトの実行、モジュールオーバーレイのマウント)。
後期ロードはデーモン化し、フォークした子プロセスでSELinuxを再強制します。これによりエクスプロイトの一時suデーモンが破棄されます。したがって検証はsuを経由してはなりません。代わりに、ロードされたドライバーはそのシステムコールサーフェスを通じて直接クエリされ、ルートなしの通常のシェルから到達可能です:ksud debug versionがポーリングされ、報告されたカーネルバージョンが解析されます。空でない非ゼロのバージョンは、ドライバーが常駐して応答していることを確認します。ドライバーインストールパスはreboot(2)マジック → インストールfd → KSU_IOCTL_GET_INFOメカニズムです(reboot(0xDEADBEEF, 0xCAFEBABE, 0, &fd)が匿名の[ksu_driver] fdをインストールし、GET_INFOが{version, flags, features, uapi_version}を返します)。レガシーのprctl(0xDEADBEEF, …)チャネルはフォールバックとしてプローブされます。起動時に実行される同じプローブは、モジュールが現在のブートですでに常駐している場合、フロー全体をショートサーキットします。
エクスプロイトは一時的なsuを/apex/com.android.virt/bin/suにステージングします。これはadbdのマウント名前空間内のそのapex binディレクトリ上にマウントされたtmpfs上で、そのディレクトリがシェルPATHで/system/binより前にあるためです — フロー実行中はadb shellの裸のsuが一時的なものに到達します。後期ロードはその後、シャドウを削除せずに一時suデーモンを破棄します((e)を参照)。これにより、裸のadb shell suが孤立したクライアントを実行し、ルートが機能しているにもかかわらずsu: connect daemon: Permission deniedで失敗し、apexの実際のバイナリ(crosvm、virtmgr、vmなど)が隠されたままになります。検証がライブドライバーを報告すると、フローはステージングtmpfsをアンマウントします — エクスプロイトのデーモンはすでに消えているため、KernelSU自身の/system/bin/suを通じて — 一時suクライアント、ソケット、ログを削除し、通常のadb shellが現在どのsuを解決するかを報告します。これはベストエフォートです:失敗時は手動のumountコマンドで警告し、実行を失敗させず、再起動でマウントはどのみちクリアされます。
adbで、デバイスが承認されていること(USBデバッグ有効)。kernelsu.koのソースです。artifacts/exploits/内のプリビルドエクスプロイトペイロード(ペイロードのビルドを参照)。# adb上の1台のデバイス、マネージャーインストール済み、ペイロードビルド済み。
bin/pixel-ksu-root
ドライバーはdata/targets.jsonに対してデバイスを解決し、2フェーズKASLRフローを実行し、マネージャー派生のksudを通じてモジュールを後期ロードし、ドライバーシステムコール経由で検証します。デバイスに対してペイロードが解決されない場合、マネージャーがインストールされていない場合、または検証がライブドライバーを報告しない場合は非ゼロで終了します。
KASLR_BASE=0x<hex> — フェーズB中にデバイスペイロードに渡され、固定された既導出のブートごとのベースに対してリプレイします。フェーズA中は未設定で、ペイロードがベース自体を導出します。ANDROID_NDK_HOME — Android NDKへのパス。ペイロードのビルド時のみ必要です。API — ペイロードビルド時のNDKツールチェーンのAndroid APIレベル(デフォルト35)。pixel-ksu-root/
├── bin/ ホストドライバーエントリポイント(adb駆動フロー)
├── data/
│ └── targets.json デバイス→ペイロードおよびデバイス→KMI解決テーブル
├── exploit/ ベンダー提供CVE-2026-43499ペイロードソース
│ ├── Makefile ターゲットごとのaarch64 NDKビルド
│ ├── src/ android15-6.6ベースラインソースセット
│ │ ├── main.c slide.c fops.c pipe.c root.c preload.c util.c
│ │ ├── su_daemon.c チェーンによって生成される一時suヘルパー
│ │ ├── kernelsnitch/ Futexハッシュ占有サイドチャネルヘッダー
│ │ └── targets/ デバイス+ビルドごとのtarget.h(カーネルオフセット)
│ └── src/61/ android14-6.1ソースセット(slide61.c、TCPルート)
├── lib/ ホスト側の共有シェル/ヘルパー関数
├── scripts/
│ └── build-payloads.sh ペイロードセットをビルドして重複排除
├── artifacts/
│ └── exploits/ ビルド済み、重複排除されたペイロード.soファイル
└── docs/ 設計および分析ノート
scripts/build-payloads.shはターゲットごとのexploit/Makefileをラップし、data/targets.jsonで指定された重複排除されたペイロードセットをartifacts/exploits/に出力します。デバイスごとに1つではなく、一意のオフセットグループごとに1つの.soをビルドします(そのグループのbuild_fromターゲットから)。
export ANDROID_NDK_HOME=/path/to/android-ndk # aarch64 NDKツールチェーンを含む必要があります
scripts/build-payloads.sh # data/targets.json内のすべてのペイロードをビルド
MakefileはANDROID_NDK_HOMEからaarch64 NDK Clangツールチェーンを選択し、一度に1つのターゲットをコンパイルします。API(デフォルト35)がaarch64-linux-android<API>-clangドライバーを選択します。ソースセットはカーネルファミリーによって選択されます — android15-6.6ターゲットはsrc/ベースラインをコンパイルし、android14-6.1ターゲットはsrc/61/をコンパイルします — 各ターゲットの絶対カーネルオフセットはsrc/targets/<codename>-<build>/target.hから取得されます。単一ターゲットを直接ビルドするには:
make -C exploit TARGET=husky-CP2A.260705.006
data/targets.jsonは18のPixelモデルをカバーする19のデバイス/ビルドエントリをリストしています(bluejayは2つのファームウェアビルドに登場)、5つのカーネルオフセットペイロードにグループ化されています。選択はカーネルイメージによって行われるため、vmlinuxを共有するデバイスは1つのペイロードに重複排除され、異なるカーネルイメージは独自のペイロードを取得します。
共有のandroid14-6.1-aカーネルイメージ(6.1.157-android14-11-gbd23337e42e7-ab14791245)上のデバイスは、Pixel 6/6 Pro/6a、7/7 Pro/7a、8/8 Pro、および9/9 Pro/9 Pro XL/9 Pro Foldファミリーにわたります。android14-6.1-bとandroid14-6.1-akitaは、同じKMI上でカーネルビルドまたはオフセットが異なるモデルを分離します。android15-6.6はPixel 10ファミリーをカバーします。
NebuSec/CyberMeowfiaリポジトリでApache-2.0のもと公開。発見はNebuSecのVEGAツールによるもので、2026-07-07に開示されました。exploit/ツリーは、NebuSecエクスプロイトの上にandroid14-6.1およびandroid15-6.6ターゲットオフセットとKernelSU後期ロードデーモンを追加します。独自のライセンスは持たず、上流のApache-2.0条項を継承します。ksud、およびマネージャー認証モデルを提供します。exploit/下のベンダー提供ソースは上流のライセンス(NebuSec派生エクスプロイトのApache-2.0)を保持します。このプロジェクトはマネージャー非依存です:インストールされているどのKernelSUバリアントのマネージャーでも後期ロードし、特定のフォークを対象としたりバンドルしたりしません。
ksud後期ロードパスinit_moduleローダーとドライバー検出apk_sign.rsashmem_miscs[0].fopsを偽造されたfile_operationsで上書きします。そのすべてのスロットは、実際のプロトタイプ互換カーネル関数(configfs_bin_write_iter、configfs_read_iter、copy_splice_read、ashmem_ioctl、noop_llseekなど)を指すため、フォワードエッジCFIは満たされつつ、ashmem fdでのread/write/spliceが制約付きカーネルR/Wを提供します。pipe_buffer構造体を偽造します(pageはvmemmap↔ダイレクトマップ変換を介して任意のターゲットを指し、ops = anon_pipe_buf_ops、PIPE_BUF_FLAG_CAN_MERGE)。これにより、パイプ上の通常のread()/write()が任意のカーネルアドレスとの間でバイトを移動します — 安定した任意カーネルR/Wです。/proc/sys/kernel/random/boot_id| ペイロード | KMI | ビルド元 | デバイス |
|---|
android14-6.1-a | android14-6.1 | bluejay-CP2A.260705.006 | oriole, raven, bluejay (CP2A), panther, cheetah, comet, shiba, husky, lynx, caiman |
android14-6.1-b | android14-6.1 | komodo-CP2A.260705.006 | komodo, tegu, tokay |
android14-6.1-akita | android14-6.1 | akita-CP2A.260805.005 | akita |
android14-6.1-cp1a | android14-6.1 | bluejay-CP1A.260405.005 | bluejay (CP1A) |
android15-6.6 | android15-6.6 | blazer-CP2A.260705.006 | frankel, blazer, mustang, rango |