
MT6985 MediaTek Dimensity 9300(vivo PD2241)向け CVE-2026-43499 エクスプロイトアダプター
| 項目 | 値 |
|---|
| デバイス | vivo PD2241 (Dimensity 9200 / MT6985), Android 15 |
| ファームウェア | PD2241_A_15.2.10.2.W10.V000L1 |
| カーネル | 5.15.178-android13-8-gfb31f5bdd612-dirty |
| Bootloader | ロック済み (ro.boot.flash.locked=1) |
| SELinux | Enforcing |
| panic_on_oops | 有効 → カーネル OOPS 発生 = 即再起動 |
| Exploit | CyberMeowfia — CVE-2026-43499 (IonStack) |
| ソースツリー | android_15.0_kernel_MT6985 (5.15.178) — デバイスバージョンと不一致 (ソースは android15 GKI、デバイスは android13 GKI を実行) |
arch/arm64/include/asm/memory.h + include/linux/fs.h + android/abi_gki_aarch64.xml から抽出:
KIMAGE_TEXT_BASE = 0xffffffc008000000, VA_BITS=39, DIRECT_MAP=256GBlayout-offset-in-bits)iopoll なし (android13 GKI)、IOCTL=0x48、open=0x68atomic_t usage = 4 バイト、uid=0x04slab_cache=0x18重要な発見: ソースは android15 GKI、デバイスは android13 GKI — task_struct のオフセットは 0x40〜0x88 バイト異なり、ソースをそのまま流用できない。
OTA zip (8.3GB)
→ payload.bin (8.2GB)
→ payload_dumper → boot.img (96MB, v4 header)
→ LZ4 展開 → Image (50MB ARM64)
→ kallsyms-finder → 187810 シンボル
2 つのファームウェアバージョン (15.2.7.6 / 15.2.10.2) からシンボルを抽出 — 同じシンボルでもバージョン間で 10KB〜200KB の差があり、正しいバージョンを使用する必要がある。
# rt_mutex_adjust_pi 内:
LDR x21, [x19, #0x8b0] → pi_blocked_on = 0x8b0 (android15 の値、frankel ではない)
これにより task_struct レイアウトが frankel の android13 ではなく android15 ブランチであることが確認された。
NDK r29、make PROJECT=android_15.0_kernel_MT6985 → preload.so (150KB)。
[+] preload starting pid=25414
[+] p0 profile ... 全シンボル正常ロード
[-] KernelSnitch mm_struct leak failed ← この行がない場合もある (稀に成功)
[+] slide child context route=pselect ← slide KASLR leak 子プロセス起動
[カーネルパニック] ← rt_mutex_adjust_prio_chain+0x1b0
クラッシュ命令 (capstone):
ldar w8, [x27] ; x27 = waiter->lock ([x28, #0x38] からロード)
; x27 の値がガベージ → ページテーブルにマッピングなし → translation fault
; → die() → panic → 再起動
KernelSnitch は exploit 全体の入口 — futex ハッシュバケットのタイミング差で mm_struct アドレスをリークする:
MT6985 は CONFIG_KASAN_HW_TAGS=y → カーネルは MTE タグで slab 割り当てをマーク
mm_struct のポインタに KASAN tag が付く → futex_hash は tagged pointer に基づいて計算
しかし bruteforce は direct map (untagged アドレス) をスキャン → 計算されたハッシュが一致しない
MTE tag の走査 (0-14 の 15 種類) を追加しても、VA_BITS=39 のシステムでは
tag ビット (bit56-59) が符号拡張ビットと重なる → 一部の tag 組み合わせで無効なアドレスが発生 → 検出漏れ
Pixel デバイスには KASAN_HW_TAGS がなく、このメカニズムは機能する。MTK では機能しない。
CONFIG_PANIC_ON_OOPS が致命的Pixel: カーネル OOPS → dump_stack → 実行継続 → exploit は再試行可能
MT6985: カーネル OOPS → die() → panic() → 即再起動 → 試行錯誤の余地なし
π チェーン破壊が少しでもずれると全体がクラッシュ。Pixel ではずれても「今回は失敗、別のアドレスセットで再試行」で済む。
さらに bootloader はロック済み (flash.locked=1) → このオプションを無効化するカスタムカーネルをフラッシュできない。
ソースツリー: 5.15.178 android15 GKI
デバイス: 5.15.178-android13 (vivo vendor)
メジャーバージョン番号はどちらも 5.15.178 だが、GKI ブランチが異なる (android13 vs android15) ため、task_struct/cred などの重要な構造体レイアウトが一致しない。frankel/android15 の 2 セットのオフセット間で何度も切り替え、最終的に逆アセンブルで確定した。
| 変更 | 目的 | 結果 |
|---|---|---|
THRESHOLD_MULT 10→5→3 | 衝突検出閾値の低下 | <5 では偽陽性が多すぎる |
APPENDED_FUTEXES 4096→8192 | ハッシュチェーン差の拡大 | 効果なし |
REPEAT_MEASUREMENT/AVERAGE | サンプリング精度の向上 | 効果なし |
MTE=1 | bruteforce にタグ走査を追加 | 遅くなるだけで、クラッシュは減少 |
MM_STRUCT_SZ 0x500→0x400 | mm_struct ステップサイズの修正 | 必要、ABI は実際には 992 バイト |
IDENTITY_END 64GB→256GB | スキャン範囲の拡大 | 遅すぎる (MTE 走査)、依然として不一致 |
| TASK offsets: android15↔frankel | 正しいオフセットの特定 | 逆アセンブルで android15 を確認 |
| FOPS offsets: android15↔frankel | android13 には iopoll なし | frankel を使用 |
exploit/targets/android_15.0_kernel_MT6985/target.h 内:
| カテゴリ | 信頼度 | 検証方法 |
|---|---|---|
| メモリレイアウト | 正しい | memory.h 計算 + kallsyms _text 検証 |
| シンボルオフセット (22 個) | 正しい | 15.2.10.2 boot.img から抽出 |
| task_struct オフセット | 正しい | ABI XML + capstone 逆アセンブル (pi_blocked_on=0x8b0) |
| FOPS オフセット | 誤り(2026-07-31 に修正済み) | 元の値は frankel から流用 (android13 に iopoll なし);ソースツリー ABI には実際に iopoll@0x30、ioctl=0x50、open=0x70 がある — VERIFICATION.md 参照 |
| CRED オフセット | 正しい(検証済み) | ABI XML:uid=0x04、securebits=0x24、caps=0x28、security=0x78 |
アセンブルは通る、実行もできる、最後の 1 キロメートルで負けた。
CONFIG_PANIC_ON_OOPS を無効化 — カスタムカーネルをフラッシュするか (bootloader のロック解除が必要)、デフォルトでこのオプションが無効な MT6985 デバイスを見つける/proc/self/pagemap — 本デバイスでは制限済み (全ゼロを返す)/proc/mtk_*) — 存在するがさらなる分析が必要symbols/kallsyms_PD2241_15.2.10.2.txt — 完全な 15.2.10.2 シンボルテーブル、後続は直接使用可能device_config.txt — デバイスの実際のカーネル設定、vendor が何を変更したか確認可能exploit/targets/android_15.0_kernel_MT6985/target.h — 構造体オフセットは検証済みscripts/server_compile.py — 自動コンパイル、パラメータ変更後の再コンパイルが高速tar -xf は大きな zip で失敗する可能性がある。Python zipfile を使用するか、先に手動で解凍するupdate_metadata_pb2.py は protobuf 5.x を要求するため、runtime_version インポート行を手動で削除する必要がある./preload.so を直接実行すると segfault: /system/bin/linker64 /data/local/tmp/preload.so を使用する必要があるlayout-offset-in-bits はコンパイラが計算したもので、5000 バイトを手動で数えるより 100 倍正確CONFIG_ANDROID_VENDOR_OEM_DATA=y、CONFIG_SCHED_INFO=y、CONFIG_RSC_* → 標準 GKI から乖離boot.img → kernel.bin → LZ4 展開 → Image → kallsyms-finder → シンボルテーブル07/28 CyberMeowfia リポジトリ + MT6985 ソースをダウンロード
07/29 ソース解析 (memory.h, fs.h, ABI XML, 各種 struct)
ファームウェア展開 (payload.bin → boot.img → Image)
シンボル抽出 (kallsyms-finder → 187810 シンボル)
複数回のコンパイル + 複数回のクラッシュ + 逆アセンブル検証
5 回の自動再試行 → 全て失敗
本記事を執筆
-------------------------------------------
合計: ~68M tokens、0 root shells
2026-07-29、生きて物語を語る
android_15.0_kernel_MT6985.tar.gz(5.15.178 ソースツリー + ABI XML)を入手後、
全量の相互検証を実施。詳細は VERIFICATION.md を参照。要約:
target.h を修正し、exploit 内蔵の
leak_kernel_base() による実機自己検証をフォールバックとして利用。KSNITCH_MTE_ENABLED=1 を実際に
有効化(元の util.c は mte=0 をハードコード);rt_mutex_adjust_prio_chain+0x1b0 は pselect/pi チェーン段階にあり、
FOPS 自己検証より前;上記修正後、再実機検証の価値あり。デバイス(PD2241、compiler251203103903)実機 8+ ラウンドテスト:
MM_STRUCT_SZ=0x400(元の 0x500 はスキャングリッド
の位置ずれを引き起こし、偽陽性のみ発生)+ MTE tag 0..15 走査(デバイスの mm ポインタ tag は変化する)+ 偽装アドレス
untag。現在は実際の mm_struct を安定して発見可能。rt_mutex_adjust_prio_chain+0x1b0 でクラッシュ:pselect/pi チェーンの
タイミング競合(偽装 waiter がカーネルスタックの正しいオフセットに配置できない)。vivo RSC スケジューラが futex/pi パスを
変更しており、この race を根本的に破壊している可能性がある。slide スタックアライメントは SLIDE_SHIFT 環境変数として
パラメータ化済みだが、スキャンは未完了。最終選択:有料で bootloader のロック解除を依頼(本パスに依存しない)。