
CVE-2025-21479 (Adreno KGSL) の iQOO Neo8 (SM8475) 向けローカルルートエクスプロイト - 物理メモリの読み書き、SELinuxの無効化、rootシェルの起動
CVE-2025-21479 のローカル権限昇格 Proof of Concept です。対象は vivo V2301A(iQOO Neo8 / PD2301、Snapdragon 8+ Gen 1 / SM8475、Adreno A730、Android 14、GKI カーネル 5.10)上の Qualcomm Adreno GPU(KGSL)です。
原理としては、エクスプロイトは KGSL の CP_SMMU_TABLE_UPDATE コマンドを介して GPU の SMMU を操作し、任意の物理メモリ読み書き能力を得ます。その後、SELinux を無効化し、カーネルシンボルを特定して root シェルを取得します。この間、ファームウェアのフラッシュは一切不要で、Bootloader のロック解除にも依存しません。
自己所有、または使用が許可されたデバイスでのセキュリティ研究に限ります。
and $12, 0x3)には欠陥があり、Self-Draw-State(SDS)パケットを RingBuffer(RB)と誤判定します。そのため、ユーザー空間で偽造した CP_SMMU_TABLE_UPDATE を SDS ブロックに含めて送信すると、GPU の TTBR0 を上書きでき、GPU ページテーブルを任意の物理ページに向けられます。以降、GPU は任意の物理メモリを読み書きできます。stabilize_rw によりマッピングを 2 MB のセルフマッピング領域に強化し、大量の高速読み取り(fast_pread_bytes など)をサポートします。selinux_state.enforcing フィールドをゼロクリアします。続いて固定物理ベースアドレス 0xA8000000 からカーネルイメージをダンプします(デフォルト上限 43 MB)。SM8475 には物理 KASLR がないため、生ダンプから直接 kallsyms 構造を解析します(エンディアンは自動判定)。したがって仮想 KASLR の影響も受けません。prepare_kernel_cred(0) → commit_creds()、さらに seccomp を無効化)で __do_sys_capset をパッチします。fork した子プロセスが capset() を呼び出すと uid 0 になり、上書きしたカーネルコードを復元し、子プロセスは u:r:shell:s0 ドメインで指定したコマンド(デフォルトは sh)を再実行します。最終的にカーネルコードは実行前とバイト単位で一致します。完全なデバイス情報は device_info.txt を参照してください。
├── source/
│ ├── cheese.c # 漏洞利用主程序(include 了下面两个文件)
│ ├── kallsyms_lookup.c # 针对原始内核映像的 kallsyms 解析器
│ └── adrenaline.h # KGSL ioctl / 命令定义(作者 hawkes)
├── exploit # 预编译 arm64 二进制(动态链接,minSdk 21,
│ # NDK r30-beta2)
├── device_info.txt # 用于调参的目标设备信息
└── README.md
注意:cheese.c は kallsyms_lookup.c と adrenaline.h を #include するため、コンパイル時には 3 つのファイルを同じディレクトリに置く必要があります。
比較的新しい Android NDK(r25+)であればどれでも構いません。例:
# 静态链接(自包含、任意 API 等级可运行;体积较大)
aarch64-linux-android34-clang -o cheese source/cheese.c -static
# 动态链接(体积小,依赖设备自带的 libc;API 等级需匹配设备)
aarch64-linux-android34-clang -o exploit_dyn source/cheese.c
# 若要兼容 API 21+ 设备,请改用 aarch64-linux-android21-clang
adb push exploit /data/local/tmp/
adb shell
chmod +x /data/local/tmp/exploit
/data/local/tmp/exploit
詳細チュートリアルはこちら(ワンクリックツールあり)
成功すると uid=0 のシェルが得られ、このセッションでは SELinux が無効化されています。プロセスは権限昇格完了後に argv[1..](デフォルトは sh)を再実行します。実行のたびにカウントダウン("pte wait 3 second")と 43 MB のカーネル読み取り進捗("Read X / 43 MB")が表示されます。

$(find /data/app -type f -name "libksud.so" | grep resukisu) late-load --kmi android12-5.10
デバイス関連の定数は source/cheese.c の先頭にあります。
KERNEL_PHYS_BASE — カーネル物理ベースアドレス(デフォルト 0xA8000000、SM8475)IDMAP_PG_DIR_OFFSET — idmap_pg_dir オフセット(デフォルト 0x245a000)selinux_state_offset — selinux_state を特定するためのオフセット(デフォルト 0x729030、実際の物理アドレス = idmap_pg_dir の物理アドレス + このオフセット)gPhyAddrs[] — 順に試行する候補物理ベースアドレスDEFAULT_KERNEL_SIZE_MB — カーネル読み取りのフォールバック値 / 上限前提条件:Adreno A730/A7xx シリーズ GPU(影響を受けるファームウェア)、物理ダンプから kallsyms を解析できるカーネル、かつ物理 KASLR がないこと。
can't get GPU r/w — GPU 初期化に失敗。まずしばらく待ち、それでもダメなら CHEESE_ATTEMPT=1..n を試すか、CHEESE_PHYADDR を手動で指定してください。1 回だけ試行するデバッグ時は CHEESE_NO_RETRY=1 を追加します。CHEESE_READ_FULL_KERNEL=1 で完全なイメージを読み取り(CHEESE_KALLSYMS_MAX_MB で上限を拡大)、それでもダメなら CHEESE_FORCE_MANUAL_PATCHFINDER=1 を検討してください。cheese エクスプロイトの作者adrenaline.h KGSL ヘッダーファイル。ioctl インターフェース定義を提供zhuowei/cheese をベースに改変(元リポジトリにライセンスなし)。 完全な原理は脆弱性公告を参照してください。
このリポジトリは教育およびセキュリティ研究のみを目的としています。自己所有、または使用が許可されたデバイスでのみ使用してください。 作者は不正使用に対していかなる責任も負いません。本ツールの使用により、保証が無効になったり、関連する利用規約に違反したりする可能性があります。
| モデル | vivo V2301A(iQOO Neo8、コードネーム PD2301、ボード taro) |
| SoC | SM8475(Snapdragon 8+ Gen 1)+ Adreno 730 |
| Android | 14(UP1A.231005.007、SDK 34、qssi-user) |
| カーネル | 5.10.198-gki-gfb2f5774872c |
| SELinux | Enforcing(実行時に本ツールで無効化) |
| 変数 | 説明 |
|---|
CHEESE_PHYADDR=<phys> | カーネル物理ベースアドレスを強制指定します(内蔵の gPhyAddrs 探索テーブルをスキップ)。 |
CHEESE_ATTEMPT=<n> | gPhyAddrs[0] ではなく gPhyAddrs[n] を使用します。GPU 初期化に失敗すると maybe_retry が自動的にインクリメントして再試行します。 |
CHEESE_SWAPPER_PG_DIR_OFF=<off> | swapper_pg_dir のスキャンをスキップし、この絶対オフセットを直接使用します。 |
CHEESE_READ_FULL_KERNEL=1 | カーネルイメージ全体を読み取ります(デフォルトの読み取り上限は DEFAULT_KERNEL_SIZE_MB = 43 MB)。 |
CHEESE_KALLSYMS_MAX_MB=<MB> | kallsyms の読み取り上限を上書きします。 |
CHEESE_FORCE_MANUAL_PATCHFINDER=1 | 手動 patchfinder のフォールバックを強制します(init_cred、prepare_kernel_cred)。 |
CHEESE_SKIP_GPU=1 | GPU の読み書きフェーズをスキップします(デバッグ専用)。 |
CHEESE_NO_RETRY=1 | 失敗時に再試行せず即終了します。 |