
CVE-2026-43499 exploit adapter for MT6985 MediaTek Dimensity 9300 (vivo PD2241)
| 项目 | 值 |
|---|
| 设备 | 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 符号
从两版固件 (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 布局是 android15 分支,不是 frankel 的 android13。
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 子进程启动
[内核 panic] ← 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 地址) → 算出来的 hash 对不上
即使加上 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 两套偏移之间切换,最终靠反汇编才锁定。
| 改动 | 目的 | 结果 |
|---|---|---|
THRESHOLD_MULT 10→5→3 | 降低碰撞检测阈值 | <5 假阳性太多 |
APPENDED_FUTEXES 4096→8192 | 增大 hash 链差异 | 无效果 |
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 |
汇编能过, 跑得起来, 输在最后一公里。
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.solayout-offset-in-bits 是编译器算的, 比手工数 5000 字节准 100 倍CONFIG_ANDROID_VENDOR_OEM_DATA=y, CONFIG_SCHED_INFO=y, CONFIG_RSC_* → 偏离标准 GKIboot.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, lived to tell the tale
拿到 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(不再依赖本路径)。