Google Project Zero 再次证明,攻破一台旗舰 Android 设备不需要复杂武器库——两个漏洞、五行关键代码,就能从零点击上下文一路打到内核任意读写,完成完整提权。这条针对 Pixel 10 的利用链,比去年 Pixel 9 上那条 Dolby + 本地提权的路径更短、更干净,值得每一位做 Android 安全防御的工程师仔细拆解。
从零点击到内核:链路比想象中短
Project Zero 此前披露的 Pixel 9 利用链由一个 Dolby 音频解析的零点击漏洞和一个本地提权漏洞组成。Dolby 漏洞已在 2026 年 1 月被修复,但研究团队没有停下——他们转向 Pixel 10,找到了一条更精简的路径:
零点击入口 → 内核任意读写 → Root
中间没有多余的中间态跳板。两个漏洞就完成了整条链:
- 零点击触发漏洞——攻击者无需用户交互,通过某个可达的远程入口(如消息推送、媒体解析)触发目标进程中的漏洞,获得该进程上下文中的代码执行能力。
- 内核提权漏洞——从已控制的用户态进程直接突破到内核,获得任意地址读写能力,继而修改自身凭证完成 Root。
关键在于第二步:Project Zero 展示的内核任意读写核心操作,浓缩到大约五行代码。这不是夸张——漏洞本身提供了近乎原始的内核内存操作能力,利用代码只需要极少的封装。
五行代码的内核任意读写
核心逻辑可以概括为:
// 伪代码:利用漏洞提供的内核写入原语
write_kernel(addr, val); // 向内核地址写入任意值
read_kernel(addr); // 从内核地址读取任意值
// 提权路径:找到当前进程的 cred 结构,清空权限位
cred = read_kernel(task_struct + cred_offset);
for (int i = 0; i < 8; i++)
write_kernel(cred + uid_offset + i*4, 0); // uid/gid 全部置零 → root
五行的本质是:漏洞给了你 write_kernel 和 read_kernel 两个原语,提权只需要定位 cred 结构然后把 UID 字段全部写零。没有复杂的堆风水、没有 ROP 链、没有堆栈喷射——漏洞本身的"质量"太高,利用几乎不需要额外构造。
这种简洁性恰恰是最危险的信号:当漏洞原语足够强,利用的门槛就极低,防御方在"利用难度"这一维度上几乎没有缓冲空间。
检查你的设备:内核防御机制现状
理解攻击路径之后,更重要的是确认你管理的设备上有哪些缓解机制已经生效。以下命令可以在任何已 root 或通过 adb 调试的 Android 设备上运行:
# 1. 检查内核地址随机化 (KASLR) 状态
cat /proc/cmdline | grep -o 'kaslr=[^ ]*'
# 2. 检查 SELinux 强制模式(Enforcing 才有实际防御力)
getenforce
# 3. 查看当前内核的_cred 相关保护(需要 root 权限)
adb shell su -c "cat /proc/1/status | grep -E 'Uid|Gid|Cap'"
# 4. 检查内核模块加载限制
cat /proc/sys/kernel/modules_disabled
# 5. 查看内核编译时的安全特性(如果你的内核导出了这个)
cat /sys/kernel/security/config 2>/dev/null || \
zcat /proc/config.gz 2>/dev/null | grep -E 'CONFIG_HARDENED|CONFIG_RANDOMIZE|CONFIG_CFI'
运行后重点看三个指标:
| 检查项 | 安全状态 | 危险状态 |
|---|---|---|
| KASLR | kaslr=on 或 cmdline 中有随机化参数 |
kaslr=off 或未启用 |
| SELinux | Enforcing |
Permissive 或 Disabled |
| modules_disabled | 1(禁止加载) |
0(允许加载) |
如果 KASLR 未启用,攻击者定位内核地址的难度大幅降低——上面那五行代码中的 task_struct + cred_offset 就变成了常量,可以直接硬编码。SELinux 如果是 Permissive,即使提权到 Root,SELinux 上下文仍然会限制进程的实际能力,但 Permissive 模式下这些限制只记录不执行,等于没有。
防御视角:为什么两条漏洞就够用
这条链路暴露了几个结构性问题:
零点击入口仍然存在。 消息推送、媒体解析、蓝牙协议处理——这些默认开启、自动处理外部数据的组件,天然是零点击攻击的入口。Pixel 9 的 Dolby 漏洞是音频解析,Pixel 10 的入口虽然 Project Zero 尚未完全公开细节,但大概率属于同类"被动接收并解析"的组件。
内核漏洞的原语强度决定利用复杂度。 如果漏洞只给你一个受限的写原语(比如只能写 0),利用就需要堆风水、UAF 构造等大量辅助工作。但如果漏洞直接给你任意地址任意值写入,五行代码就够了。防御方不能假设"漏洞很难利用"——必须假设最坏情况。
链路缩短意味着中间缓解失效。 Pixel 9 的链路需要从 Dolby 进程跳到另一个本地进程再提权,中间至少有一层沙箱边界。Pixel 10 的链路从零点击进程直接到内核,中间的沙箱边界被绕过或不存在。这意味着 Android 的沙箱分层策略在某些入口点上仍有缺口。
修复之外:工程团队应该做什么
漏洞会被修复,但下一个同类漏洞一定会再来。工程团队可以立即推进的几件事:
-
审计所有零点击入口组件。 列出设备上所有无需用户交互就处理外部数据的进程(MMS、RCS、蓝牙、Wi-Fi 协议、媒体解码器),逐一检查其沙箱隔离等级和 SELinux 策略。
-
强制 KASLR + SELinux Enforcing。 在产品配置中锁定这两项,不允许任何场景降级到 Permissive 或关闭 KASLR。
-
启用内核 CFI(Control Flow Integrity)。 Pixel 10 的内核如果编译时启用了
CONFIG_CFI_CLANG,即使攻击者获得任意读写,也无法直接劫持控制流。检查方法:
# 在设备上检查 CFI 支持
zcat /proc/config.gz 2>/dev/null | grep CFI
# 或在内核源码树中确认
grep CONFIG_CFI arch/arm64/configs/<your_defconfig>
-
限制内核模块加载。 设置
kernel.modules_disabled=1,阻止攻击者加载内核模块来持久化。 -
监控 cred 异常修改。 在内核中注册对
cred结构写操作的审计钩子(需要定制内核),或通过 eBPF 监控异常的 UID 变化事件:
# 示例:用 bpftrace 监控进程 UID 突变(需要内核 5.x+ 和 bpftrace 支持)
bpftrace -e 'tracepoint:sched:sched_process_exec { printf("exec: pid=%d uid=%d\n", pid, uid); }' \
-e 'kprobe:commit_creds { printf("cred change: pid=%d new_uid=%d\n", pid, arg0->uid); }'
这条利用链的核心教训不是"Pixel 10 有漏洞"——而是当漏洞原语足够强,从远程到 Root 的距离可以只有两步。防御的着力点不在增加利用的步骤数,而在降低每一步漏洞能提供的原语强度,以及在每一步之间设置真正生效的硬隔离。