一个针对 CVE-2025-8045 的本地权限提升漏洞利用程序,该漏洞是 Arm Mali
GPU 内核驱动程序(kbase)中的释放后使用漏洞。一个非特权本地进程与
CSF KCPU 队列转储路径竞争,释放并重新回收 cpu_queue.buffer,将悬空分配
转变为其所拥有的页表(“脏页表”),并利用该原语实现任意内核物理内存读写——
最终获得一个 SELinux 设为宽容模式的 root shell。
已在 Pixel 7 Pro 上成功利用(版本
BP3A.250905.014)。exploit.c中硬编码的偏移量 是针对该目标校准的;其他设备/版本需要重新推导 偏移量(参见调优与可移植性)。
CVE-2025-8045 是 Arm Valhall 与第 5 代 GPU 架构内核驱动程序(r53p0–r54p1)中的
释放后使用漏洞。一个本地非特权进程可以操纵 GPU 处理操作,使驱动程序对已释放的
内存执行操作,从而为内核内存泄露、破坏和代码执行打开大门。
本漏洞利用程序通过 /dev/mali0 暴露的 CSF KCPU 队列接口触达该漏洞:
CQS_WAIT,随后紧跟一个
FENCE_SIGNAL。该等待使队列停滞,从而使栅栏定时器稍后在
可预测的定时器轮桶边界处触发。KBASE_IOCTL_CS_CPU_QUEUE_DUMP 驱动 kbase_csf_cpu_queue_dump_buffer()。
将队列的状态转换(ISSUED → COMPLETE/PENDING)与转储路径竞争,
在 cpu_queue.buffer 仍被引用时将其释放——即 UAF。该漏洞利用是一个时序型利用:它首先校准截止时间,然后在每一轮 预测截止时间,并触发一“波”无害的转储调用,以扩大释放发生时刻附近的竞争窗口。
由于一个页面最终会同时成为 Mali 分配和存活的 CPU 页表, 该进程将永久停放而不是退出——释放它将构成双重释放并使内核崩溃。
# Android / arm64 (NDK)
aarch64-linux-android31-clang -O2 -o exploit exploit.c
./exploit
成功后将获得一个 SELinux 为宽容模式的 root shell(uid=0)。示例流程:
[*] pid=... cpus=8 wave=64 harvest cpu=7 rounds=256
[*] round 1 calibrate deadline t0+... ms phase ... ms
[*] round N read +... us
[+] UAF page: spray region R page P @ ..., 1 live descriptor
[+] entry 0 = ... -- a CPU page table, writable through the GPU
[+] alias: dirty pagetable
[+] selinux_state.enforcing = 0 @ phys ...
[+] sel_read_enforce patched @ phys ...
[+] root shell: uid=0 euid=0
如果运行完所有轮次仍未成功,dmesg 中的 cpu_queue.c:71 表明
竞争已命中但回收未成功——请调整回收/波次参数。
exploit.c 中的数值是为 Pixel 7 Pro(版本
BP3A.250905.014)校准的。偏移量和常量均为设备/内核专用,必须
为任何其他目标重新推导:
KERNEL_PA_BASE、SEL_READ_ENFORCE、INIT_CRED、COMMIT_CREDS、
SELINUX_STATE — 内核符号/物理偏移量。ARM64_PTE_FLAGS — 目标 MMU 配置下的末级描述符模板。WAVE_*、SWEEP_*、BUCKET_NS、WAIT_NS、喷射大小 — 竞争时序与堆整形调节参数。请升级到 Arm 发布的已修复驱动程序版本以完成修复。
本代码仅用于防御性安全研究、检测工程与教育目的。请勿针对您不拥有或 未获明确书面授权测试的系统运行它。作者对滥用或造成的任何损害不承担任何责任。 未经许可对系统使用它属于违法行为。
| 阶段 | 说明 |
|---|
| 校准 | 第 1 轮测量 ISSUED→COMPLETE 边沿,以获知定时器轮桶相位。 |
| 预测 + 扫描 | 后续轮次预测截止时间,并在 ±ms 范围内扫描读取偏移量,以精确落在释放时刻。 |
| 波次 | 64 个线程将 CS_CPU_QUEUE_DUMP 调用压到 csf.lock 上,以包围截止时间并拉长窗口。 |
| 回收 | 预先排空 UNMOVABLE 空闲链表,然后喷射带标记的 GPU 页面,使被释放的内存块回到我们的控制之下。 |
| 脏页表 | 再次释放,并立即向同一页面喷射 CPU 页表;此时 GPU 分配与一个存活的末级页表互为别名。 |
| 任意读写 | 将一个永不复用的窗口 PTE 指向内核物理页面;通过对大型临时映射执行 PROT_NONE 往返,强制刷新 TLB。 |
| Root | 清除 selinux_state.enforcing,用 commit_creds(init_cred) 修补 sel_read_enforce,释放一个已停放的子进程以执行 root shell。 |