Skip to content
KitploitKITPLOIT
ツールエクスプロイトブログ
Log in
提出
ツールエクスプロイトブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

フィードお問い合わせプライバシー© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
ghostlock-pfem10 — OPPO Find X5 Pro (PFEM10) 用 GhostLock (CVE-2026-43499) — OPlus watchdog & heap-spray detector reverse engineering | Kitploit
ツール/GitHubGitHub/imeiplus/ghostlock-pfem10
Androidセキュリティ特権昇格メモリフォレンジック脆弱性分析エクスプロイトリバースエンジニアリングモバイルセキュリティペイロード開発バイナリエクスプロイト
GitHubimeiplus/ghostlock-pfem10

ghostlock-pfem10

OPPO Find X5 Pro (PFEM10) 用 GhostLock (CVE-2026-43499) — OPlus watchdog & heap-spray detector reverse engineering

4622日前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有
リポジトリを見る

GhostLock — OPPO Find X5 Pro (PFEM10)

English · 中文

build

ColorOS 16 上の OPPO Find X5 Pro 向け GhostLock (CVE-2026-43499) ポート。uid=0 の子プロセスとロード済みの kernelsu.ko に到達する。root プロセスは傍受される。

脆弱性

CVE-2026-43499 — futex PI の use-after-free。remove_waiter() は、current が requeuer である場合に current->pi_blocked_on をクリアする。これは rt_mutex_start_proxy_lock() の -EDEADLK ロールバックパス上で発生する。

remove_waiter @ 0xffffffc0081ed254 — 修正前の形状。

デバイス

デバイスOPPO Find X5 Pro (PFEM10)
SoCSM8450 / Adreno 730
OSColorOS 16.0.3.520 (CN01)
カーネル5.10.236-android12-9-o-gaf2075ad2c06
ブートローダーlocked, green
VA_BITS39 — KIMAGE_TEXT_BASE = 0xffffffc008000000

ステータス

ステージ
Compact waiter トリガー (CMP_REQUEUE_PI → EDEADLK)動作する
task_struct リーク (perf)動作する
PI 書き込み (8 バイト; 値 = 0 または有効なカーネルアドレス)動作する
task+0x778 または task+0x780 単独 → Uid=root動作する — ただし単一フィールドの着地ではタスクが分岐状態のまま残り、それが潜在的なハード BUG_ON となる。分岐の危険性を参照
両方のフィールドを単一の値で書き込み (一貫したペア)❌ スプレーページでは決して生成されなかった。 グローバルな init_cred エイリアス (09-14, CONTROL=1) でのみ観測された。ランナーは現在これを強制する (SAME_VALUE=1); デバイス上では未実行
資格情報のランドリング (setresgid + setresuid)V12_LAUNDER=1 の背後で実装済み; デバイス上では未実行
kernelsu.ko ロード済み動作する
root プロセスの生存⚠ 未確立 — 以下を参照
再起動メカニズム❌ 未確立。 候補の一つ (分岐) は現在除外されている; 以下を参照
着地基準としての probe_state❌ 誤り — 使用しないこと。 反例が三つある; 以下の表を参照
pstore/ramoops パニックチャネル⚠ 計測器は存在する; チャネルは未検証 (ヌルテストはまだない)
「被害者は純粋なユーザースペースでスピンする」⚠ まだ読み取りなし — uid.stream が utime/stime/nvcsw を記録するようになったので確認可能
pi 側のシングルパス二重書き込み⚠ 未確立; pi.pc/pi.left は fdset_map.h でハードコードされた 0
パス A (UMH / modprobe_path)STATIC_USERMODEHELPER_PATH=""

「root プロセスの生存」について: evidence/kill.log の実行は uid=0 に到達し kernelsu.ko をロードし、実際にそれをポーリングした実行では KernelSU マネージャープロセスが 120 秒間生存し、/proc/modules 内で kernelsu は依然 Live であった。後の実行では、同じチェーンが Android フレームワークサービスを到達不能にした (Can't find service: package/power/input/phone/wifi) が、モジュールは依然 Live であった。[ROOTCHECK-*] カーネル行も $$sys_call_number@@ ペイロードもこれまで捕捉されていないため、後の実行の状態の原因は特定されていない。evidence/notes.md §2.3, §2.4, §7 を参照。

オフセット

task_struct

フィールドオフセット
real_cred / cred0x778 / 0x780
キャッシュされた syscallno0xdf8
キャッシュされた uid / euid / gid / egid0xe00 / 0xe08 / 0xe10 / 0xe18

thread_info

フィールドオフセット
flags0x0
addr_limit0x8
ttbr00x10
preempt_count0x18

cred

フィールドオフセットフィールドオフセット
uid0x4cap_inheritable0x28
gid0x8cap_permitted0x30
suid0xccap_effective0x38
sgid0x10cap_bset0x40
euid0x14cap_ambient0x48
egid0x18
fsuid / fsgid0x1c / 0x20

エクスプロイトフロー```

LT perf leak target task_struct → file W7 stage 1 task+0x778 = V (real_cred → private sprayed page; V observed) W7 stage 2 task+0x780 = V (cred) ★ V12_W7_VALUE=V — THE SAME VALUE, not a new page W7 stage 3 V+8 = 0 (LOCAL repair of the page that was installed, ZERO shape) LT child fexecve(memfd of loader) — no execve of a /data path loader ksud late-load → kernelsu ... Live

**ステージ2にはステージ1の値を明示的に渡さなければならない。** ステージ1とステージ2はそれぞれ独自のスプレーを持つ独立した2つのプロセスであるため、「credページを両方のスロットに書き込む」というのは罠である。素朴に読むと `(pageA, pageB)` が生成され、`commit_creds` は**ポインタ**を比較するため、両方の書き込みが成功したとしてもこのペアは不一致となる。これは仮定の話ではない — まさにこれが実行3と実行9で起きたことである:```
run 9   0x778 shot  write value = 0xffffff88679bade0
        0x780 shot  write value = 0xffffff8785d6ade0     <- a different page
run 3   0x778 shot  write value = 0xffffff8787b5ade0
        0x780 shot  write value = 0xffffff881bad2de0     <- a different page

run_bootA.sh はそれゆえステージ2を V12_W7_VALUE=<ステージ1で観測された値> で発火させ、その値が復元できなければ一切発火させない。HOLD はステージ2より長生きしなければならない。さもなければステージ1のページが解放・再確保され、「同じ値」がダングリングポインタになる。詳細は同一値ルールを参照。

1回のブートにつき1ページが修復される。 ステージ3は V+8 をゼロにする。2つの異なるページがある場合、両方をゼロにすると gid/suid スタンプ(後述)が消去され、不一致が一致のように見えてしまうため、ランナーは実際にインストールされたページのみを修復し、2つの値が一致しなければ停止する。

cred ページは payload.c によって構築される。8つの id フィールドはすべてゼロ、5つのケイパビリティセットはすべてフル、そして user / user_ns / group_info は root_user / init_user_ns / init_groups を指す。ステージ3が存在するのは、書き込みの副作用がインストールする cred の cred+8(gid/suid)を常に破壊するためである。

init_cred について — 明示的な二分法

ここには以前、互いに矛盾する2つの記述があった(「グローバルの init_cred は決して使わない」対「CONTROL=1 はセル2を再現する」、そしてセル2はまさに init_cred である)。両方の記述は異なる役割について真である:

  • ターゲットとしては禁止。 init_cred ポインタを書き込むと、副作用が init_cred+8 をグローバルに破壊する — init_cred はすべてのカーネルスレッドで共有されており、Uid: 0 0 4294967176 0 はまさにその破壊である。コードは V12_ALLOW_INIT_CRED=1 が意図的に設定されていない限り、このパスを拒否する。
  • 唯一証明された一貫性のあるペアとして保持。 ksud に到達した 09-14 のチェーンは、1つの固定アドレス(0xffffff802a7e0be0)を両方のスロットに書き込んだため、構造上 real_cred == cred となった — それが execve まで生き残った理由である。CONTROL=1 はこれを再現する。これはコントロールであり、構築の土台とすべき設定ではない。

perf リーク: PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_CLOCK、PERF_SAMPLE_REGS_INTR、exclude_user=1。 [0xffffff8400000000, 0xffffff90000000) を受け入れ、投票は ≥ 15%。

書き込みプリミティブとその副作用

UAF は rb_erase_cached の Case 1-left を通じて駆動される。これにより2つのストアが発生する(1つではない):``` *(write_target) = write_value // the store you aim *(write_value + 0x08) = write_target // unavoidable side effect

`write_value` はビット 0 がクリアされた 8 バイト境界に整列されている必要がある — それは `0` か、有効なカーネルアドレスのいずれかである。**これが `g_boot_state` をこのプリミティブで設定できない理由である**: `1` になる必要があるバイトは、整列要件によってその下位ビットが `0` に強制され、`write_value` は副作用が到達するアドレスと同じ量である。

### 副作用は `write_value` が指す先に書き込む

`write_value` は*格納される値*であり、同時に*副作用が書き込むアドレス*(`+8` の位置)でもある。これをグローバルカーネルオブジェクトに向けると、そのオブジェクトを破壊することになる。

**W7 はまさにこれをしていた** — `write_value` を `init_cred` エイリアスに向けて — そしてそれはリードバックで確認できる。`out/t5_w7_778.txt` より:```
shape shift=0 wps=5: in[0]=0xffffff802a7e0be0 (write_value) in[2]=0xffffff8800cdd178 (write_target)
W7[W7] write_target= 0xffffff8800cdd178
Uid:	0	0	4294967176	0

write_value は init_cred のエイリアスであり、write_target は child_task+0x778 でした。 init_cred+8 は gid/suid であるため、副作用として 0xffffff8800cdd178 が そこに格納されました: init_cred.gid = 0x00cdd178 および init_cred.suid = 0xffffff88 = 4294967176 — まさに上記の Uid: 行の4番目の awk フィールドです。init_cred+8 を ゼロクリアすることで修復されました(out/t5_repair.txt: Uid: 0 0 4294967176 0 → Uid: 0 0 0 0)。これが「W7 stage 3」のすべてでした。

このパスはコード内で拒否されるようになりました。 V12_W7_INIT_CRED=1 は V12_ALLOW_INIT_CRED=1 も設定されていない限り、説明とともに中止します。また W2/W6/LTC パスは プライベート cred ページが存在しない場合に init_cred へフォールバックしなくなりました — 代わりに 中止します。デフォルト、そして唯一まともなパスは、スプレーされた cred ページです。

副作用自体は回避できません: write_value は cred ポインタでなければならないため、 cred+8 は常に書き込みターゲットで上書きされます。選択できるのはその場所だけです — そして 修復は今や cred_page+8 のローカルなゼロクリア(stage 3)であり、グローバルオブジェクトへの 書き込みではありません。

ゴミのような groups= の読み取り値は、これとは別の症状です。これは gid と egid がクリーンに読み戻された実行で見られたため、init_cred+8 の副作用から 生じることはあり得ません。これは偽の cred 自身の group_info フィールドを指しています。 evidence/notes.md §10.6 を参照してください。

★ 単一フィールドの着地はタスクを分岐させたままにする — そしてそれはハードな BUG_ON である

このプリミティブは1パスにつきちょうど1つのアドレスに格納します。task+0x778 (real_cred)と task+0x780(cred)は別々のアドレスであるため、0x778 のみ、または 0x780 のみの書き込みが着地すると、タスクは cred != real_cred のままになります — 分岐状態です。

ツールをダウンロード