此漏洞影响到了我们提供服务的一家客户。本仓库是我们对原始研究的贡献:为团队提供一个集中起点,以便当影响该组件的类似漏洞再次出现时,我们已有前期基础。它汇集了漏洞背后的理论、原始研究员发现的文档化总结,以及一组用于在实验室环境中验证暴露情况的实用工具。
注意:我本想分享更多研究内容,但由于公司限制,我无法进一步披露。这里包含的所有内容都已通过审查,且不违反我所受的任何协议。因此,本仓库以其当前状态归档。
2026 年 4 月 1 日,Google 发布了一次 Chrome 安全更新,修复了 21 个漏洞,其中 CVE-2026-5281 在披露时已被野外积极利用。三天后,CISA 将其列入已知被利用漏洞目录,并发布了一项具有约束力的操作指令,要求联邦机构进行修补。到那时,它已经影响到了我们。
本仓库的存在只有一个原因:当下次发生类似情况时,我们有一个起点,而不是从零开始。它汇集了:
想理解一个漏洞为何存在,就要从系统的设计目的及其所基于的假设入手。
WebGPU 通过 API 在图形处理单元(GPU)上执行渲染和计算等操作。WebGPU 并不是要对外暴露 OpenGL 或 OpenGL ES(嵌入式系统)。它是一个基于 Direct3D 12、Metal 和 Vulkan 等现代 API 思想构建的全新 API。
WebGPU 是 WebGL(浏览器多年来使用的旧 GPU API)的现代替代品。关键区别在于,WebGPU 从设计之初就着眼于安全性和显式资源管理。你需要自己声明每个缓冲区、纹理和管线的生命周期。浏览器充当 JavaScript 与 GPU 硬件之间的验证层。
处于此漏洞中心的对象,按创建顺序如下:``` GPUAdapter ← represents a physical GPU or software fallback └─ GPUDevice ← your logical connection to the adapter; owns everything ├─ GPUBuffer ← a chunk of GPU-accessible memory ├─ GPUShaderModule ← a compiled WGSL shader program ├─ GPUComputePipeline ← a shader wired to a pipeline layout ├─ GPUBindGroup ← binds buffers as inputs to a pipeline ├─ GPUCommandEncoder ← records a sequence of GPU commands └─ GPUQueue ← submits recorded commands to hardware
The rule that matters here: every object is owned by the GPUDevice. Destroying a buffer while the device still has commands in flight that reference it is explicitly illegal under the spec. The Dawn implementation is supposed to detect and reject that. CVE-2026-5281 is a case where it did not.
---
---
---
<div id='whatisdawn'/>
## ***⚙️ 什么是 Dawn?***
- **[Dawn - 开源 WebGPU 实现](https://dawn.googlesource.com/dawn)**
> Dawn 是正在制定中的 WebGPU 标准的开源跨平台实现。它提供了一个原生 C++ API,镜像了 WebGPU IDL,并带有一些扩展。
Dawn 是 Chrome 内部的 C++ 库,负责将 WebGPU JavaScript 调用转换为平台原生的 GPU 命令。在 Windows 上,它面向 D3D12;在 macOS 上,面向 Metal;在 Linux 上,面向 Vulkan。它位于 Chrome 的 JavaScript 引擎与硬件驱动程序之间,负责四件事:验证 API 调用、序列化命令、跟踪对象生命周期,以及将错误返回给 JavaScript。
CVE-2026-5281 存在于生命周期跟踪部分。具体来说,问题在于当引用缓冲区对象的命令仍在硬件队列中等待执行时,Dawn 将这些 GPU 缓冲区对象保持存活的时长。```
JavaScript (V8)
│ WebGPU API calls
▼
Dawn (C++) - validates, serializes, tracks lifetimes, reports errors
│
▼
D3D12 (Windows) - Metal (macOS) - Vulkan (Linux)
│
▼
GPU hardware driver
│
▼
Physical GPU - shader cores, VRAM
在理解 Use-After-Free 之前,你需要对内存中各对象的存放位置有一个清晰的思维模型。``` High addresses ┌────────────────────────────────────┐ │ Kernel space │ The OS and drivers live here. │ │ User-mode code cannot touch it. ├────────────────────────────────────┤ │ Stack │ Function call frames. Fast. │ (grows downward) │ Freed automatically when the │ │ function returns. ├────────────────────────────────────┤ │ Heap │ Dynamic allocations - malloc, new, │ (grows upward) │ smart pointers like Ref. │ │ Freed only when you say so. ├────────────────────────────────────┤ │ BSS / Data / Text │ Globals, constants, compiled code. └────────────────────────────────────┘ Low addresses
Dawn的C++对象,例如支撑GPUBuffer的内部对象,存活于堆上。它们采用引用计数:智能指针统计有多少持有者引用了该对象。当计数降为零时,析构函数运行,内存归还给分配器。
GPUBuffer同时具有两种表示形式,一种在CPU侧,一种在GPU侧:```
CPU side (Dawn, system RAM)
└─ C++ object - metadata, state flags, and a hardware handle
│
│ handle: ID3D12Resource* (D3D12) - MTLBuffer (Metal) - VkBuffer (Vulkan)
▼
GPU side (driver, VRAM)
└─ Actual memory allocation on the graphics card
当 JavaScript 调用 buffer.destroy() 时,预期行为是:将对象标记为已销毁,递减引用计数,释放硬件句柄,并释放 VRAM。CVE-2026-5281 中的漏洞会导致 VRAM 被释放,而 GPU 命令队列仍持有对该硬件句柄的引用,这意味着 GPU 正在主动读取或写入不再属于它的内存。
引用已释放的内存可能导致程序崩溃、使用意外值或执行代码。使用先前已释放的内存可能产生任何数量的不利后果,从破坏有效数据到执行任意代码。
Use-After-Free(释放后使用)遵循固定的三步模式,是浏览器安全中最常被利用的内存安全漏洞类别之一:```
在第 2 步之后,分配器可以将同一块内存区域交给一个完全不同的分配。如果攻击者能够控制被释放区域中随后被放入的内容(这种技术称为堆布局操纵 heap grooming),他们就能控制悬垂指针读回的内容。这就是内存安全漏洞如何变为代码执行。
GPU 端的 UAF 比 CPU 端的 UAF 更难观察,因为:
- “分配器”是 GPU 驱动程序的 VRAM 分配器,而不是系统的 malloc。
- “悬垂指针”是仍被命令队列引用的硬件句柄。
- GPU 异步执行命令,CPU 在崩溃发生很久之前就已经继续运行了。
---
---
---
<div id='thevulnerability'/>
## ***🕳️ 漏洞***
---
<div id='thevulnerability-whatweknow'/>
### ***📋 我们从公开来源了解到的信息***
以下内容完全基于已被公开确认的信息。
- **[NVD: CVE-2026-5281](https://nvd.nist.gov/vuln/detail/CVE-2026-5281)**
> Google Chrome 146.0.7680.178 之前版本中 Dawn 组件存在释放后使用(UAF)漏洞,允许已攻破渲染进程的远程攻击者通过特制 HTML 页面执行任意代码。
- **[The Hacker News: 2026年4月1日](https://thehackernews.com/2026/04/new-chrome-zero-day-cve-2026-5281-under.html)**
> Google 已获悉 CVE-2026-5281 存在野外利用。
- **[Help Net Security: 2026年4月1日](https://www.helpnetsecurity.com/2026/04/01/google-chrome-zero-day-cve-2026-5281/)**
> CVE-2026-5281 由一位化名漏洞猎人(86ac1f1587b71893ed2ad792cd7dde32)报告,此人此前还报告过两个已在 2026 年 3 月 23 日发布的 Chrome 更新中修复的漏洞:WebGL 中的堆缓冲区溢出(CVE-2026-4675)以及 Dawn 中的另一个释放后使用漏洞(CVE-2026-4676)。该漏洞猎人这次还报告了 Dawn 中的第三个释放后使用漏洞(CVE-2026-5284),该漏洞也已在此次更新中修复。
---
<div id='thevulnerability-executionlayers'/>
### ***🔗 从 JavaScript 到硬件***
要理解 Dawn 中的 UAF 可能源于何处,最好具体看看 WebGPU 调用如何从一行 JavaScript 一路传递到物理硬件:```
JavaScript
↓ navigator.gpu → adapter → device → buffer / pipeline / encoder
↓ queue.submit([commandBuffer]) ← validation happens here
↓ buffer.destroy() ← if this races GPU execution, UAF
Dawn (C++) - validates API calls, serializes commands, tracks lifetimes
↓ translates WebGPU calls to platform-native API calls
D3D12 (Windows)
↓ ID3D12CommandQueue::ExecuteCommandLists()
↓ hardware handle for the buffer passed to the driver
GPU hardware
↓ shader cores execute the queued commands
↓ if the buffer was freed prematurely → they access freed VRAM ← UAF
根本矛盾在于,queue.submit() 和 buffer.destroy() 都是会立即返回的 JavaScript API 调用,但 GPU 会异步执行提交的命令,可能在这两个调用返回后很久才执行。Dawn 需要在 GPU 执行的整个持续时间内保持 buffer 对象存活,而不仅仅是直到 JavaScript 调用返回。
当 UAF 触发时,GPU 会遇到 D3D12 所说的 "Device Removed" 事件。顺序如下:```
这也是本仓库中自动化测试运行器所检测的内容:它会监视这些精确的控制台信号,以判断该漏洞在特定 Chrome 版本中是否可触发。
---
<div id='thevulnerability-impact'/>
### ***💥 影响与利用条件***
NVD 描述中明确指出一个重要的限制条件:利用该漏洞要求攻击者已经攻破了渲染器进程。这意味着 CVE-2026-5281 并非从冷启动即可实现的独立一键 RCE,而是一种沙箱逃逸,会成为攻击链的一部分。
在实践中,完整的攻击链大致如下:```
Initial access ← some other vulnerability gets code running in the renderer
↓
CVE-2026-5281 ← UAF in Dawn used to escape the renderer sandbox
↓
Arbitrary code ← execution in a higher-privilege Chrome process or OS context
这正是使浏览器 GPU 漏洞具有高价值的利用模型:一旦进入渲染进程,Dawn 便是顺理成章的下一目标之一,因为它以产生这类时序窗口的异步复杂性来处理硬件级内存。
披露时确认的影响是任意代码执行,而 Vulners 数据库还记录了数据损坏和浏览器崩溃等其他观察到的影响。
CVE-2026-5281 并非孤立出现。它是 2026 年第四个 Chrome 零日漏洞,而这一年早在第一季度结束前,就已有望超过 2025 年共八个零日漏洞的纪录。
| 日期 | 事件 |
|---|---|
| 2026年2月 | CVE-2026-2441 已修补,Chrome 的 CSS 组件中的 UAF,已被积极利用 |
| 2026年3月10日 | CVE-2026-3909 和 CVE-2026-3910 已修补,均为已被积极利用的零日漏洞 |
| 2026年3月23日 | CVE-2026-4675(WebGL 堆缓冲区溢出)和 CVE-2026-4676(Dawn 中的 UAF)已修补,报告者与 CVE-2026-5281 相同 |
| 2026年4月1日 | Google 发布 Chrome 146.0.7680.177/178,修补 21 个漏洞,CVE-2026-5281 确认已在野外被利用 |
| 2026年4月1日 | CISA 将 CVE-2026-5281 添加至已知被利用漏洞目录 |
| 2026年4月3日 | Google 确认存在针对 35 亿 Chrome 用户的活跃利用 |
报告 CVE-2026-5281 的同一匿名研究人员在同一时间窗口内还报告了另外三个漏洞(CVE-2026-4675、CVE-2026-4676、CVE-2026-5284,后两个同样是 Dawn 中的 UAF)。这表明存在一项专门针对 Dawn 内存管理的、专注且持续的研究工作。
本仓库中的工具包基于原始安全研究构建,该研究在实验室环境中记录了该漏洞的行为。以下是该研究的摘要、用于触发 UAF 的策略以及观察到的结果。
研究人员触发 UAF 的方法旨在同时满足三个使竞态窗口可达的条件:足够的 GPU 队列压力以延迟执行、销毁与调度之间足够紧凑的时序,以及相同大小的缓冲区重新分配,以最大化观察到损坏的可能性。
该策略分为五个步骤:
步骤 1 - 数量与压力:分配了 200 个临时 WebGPU 存储缓冲区,大小随机(按 WebGPU 规范要求均为 4 字节的倍数)。这不是为了填满显存,而是为了制造足够的待处理工作,使 GPU 无法立即执行命令。
步骤 2 - 计算线程饱和:排队了 32 个并行计算管线,负载繁重,内层循环运行 1000 次迭代,调度大小为 4096 个工作组。目标是将 GPU 队列保持深度积压,使提交与执行之间的窗口保持足够长的时间以进行竞态。
步骤 3 - 陷阱:在提交所有命令缓冲区后,立即对所有 200 个缓冲区调用 destroy()。此时 GPU 已收到命令但尚未执行。Dawn 已经通过了其提交时验证。显存已被释放。
步骤 4 - 触发:使用与刚释放的缓冲区完全相同的大小进行 32 次新的缓冲区分配。如果显存分配器返回相同的物理地址(由于大小匹配,这种情况经常发生),那么 GPU 的待处理命令现在就拥有一个指向属于另一个仍处于活动状态的分配的内存硬件句柄。
步骤 5 - 重用已提交的命令:使用新分配的缓冲区再提交一轮命令缓冲区。此时队列中有两套命令引用着曾经相同的内存,而 GPU 仍在处理第一套。
结果是显存层上一个经典的 UAF:已释放的内存正被正在执行的着色器主动读取。
研究人员在易受攻击和已修补的 Chrome 安装上运行了 PoC,并观察到了清晰的行为差异:
易受攻击的运行(Chrome < 146.0.7680.178):``` [INFO] CVE-2026-5281 AGGRESSIVE PoC Loaded [INFO] Initializing WebGPU context... [INFO] WebGPU device initialized [INFO] Starting aggressive UAF attacks... [ERROR] UNCAUGHT GPU ERROR: device lost due to internal error [CRASH] GPU DEVICE LOST: destroyed [CRASH] [!!!] CRASH DETECTED! Check console for details.
目标 Chrome 进程完全停止渲染。操作系统经历了短暂的视觉冻结,这与显示驱动程序在 GPU 故障后不得不重置或停止处理的情况一致。设备丢失事件被映射为致命 GPU 错误,而非标准的 WebGPU API 验证错误,这证实了损坏的内存布局已到达硬件层,而未被 Chrome 的 JavaScript 侧沙箱捕获。
**已修补的运行(Chrome >= 146.0.7680.178):**```
[INFO] CVE-2026-5281 AGGRESSIVE PoC Loaded
[INFO] Initializing WebGPU context...
[INFO] WebGPU device initialized
[INFO] Starting aggressive UAF attacks...
[INFO] Max attempts reached without crash
[INFO] Either browser is patched or target build not affected
在所有尝试中均未出现崩溃、设备丢失或致命 GPU 信号。该修复依然有效。
以下截图是在实验室测试期间拍摄的,测试对象为一台搭载 Intel gen-12lp 集成 GPU 的 Windows 机器上安装的易受攻击版与已修补版 Chrome for Testing。每个工具均针对两个目标运行,以验证行为差异。
01 - Version Detector
| 易受攻击版 (< 146.0.7680.178) | 已修补版 (>= 146.0.7680.178) |
|---|---|
![]() | ![]() |
02 - Vulnerability Checker
| 易受攻击版 | 已修补版 |
|---|---|
![]() | ![]() |
03 - Local Scanner
| 易受攻击版 | 已修补版 |
|---|---|
![]() | ![]() |
04 - Fleet Scanner
| 易受攻击版 | 已修补版 |
|---|---|
![]() | ![]() |
05 - UAF Trigger
| Chrome | Firefox |
|---|---|
![]() | ![]() |
此处包含 Firefox 作为对照。Firefox 使用自己的 WebGPU 实现,不受此漏洞影响。无论版本如何,它都能完成所有尝试且不产生任何崩溃信号,这符合预期行为。
06 - UAF Trigger + Automated Runner
| GPU 设备丢失 |
|---|
![]() |
要获得可见的崩溃信号并不总是那么容易。根据硬件和环境的不同,触发器可能需要一些调优才能产生可观察的结果。在我们的案例中,该行为是可重现的,但若不调整工作负载,则无法稳定地暴露出来。
我们在受控的实验室环境中成功重现了针对易受攻击版 Chrome 安装的拒绝服务攻击。UAF 触发器导致 GPU 利用率饱和至 100%,因为大量积压的命令队列使驱动程序无法处理新的内存管理请求。在部分运行中,GPU 进程进入了不可恢复的故障状态,产生了以下可观察到的效果:
GPU 饱和以及偶尔出现的异常证实内存损坏已触及硬件层:已释放的缓冲区句柄被正在执行的着色器访问,GPU 发生故障,D3D12 的 TDR 机制将其呈现为设备移除事件。已修补版本则干净地完成了相同的工作负载,未出现任何类型的崩溃信号。
DoS 重现证实了该漏洞。正在进行的工作集中于对补丁进行二进制级分析,具体是比较最后一个易受攻击版本与 146.0.7680.178 之间 Dawn 的命令缓冲区提交路径,以准确了解引用计数修复是在何处以及如何应用的。
关于自动化运行器:最初的研究人员在其 PoC 中附带发布了一个运行器脚本。我们的版本需要经过修改才能在本地实验室环境中可靠运行,具体来说是将 Chrome 切换到新的无头模式,并添加一个选项以允许 GPU 崩溃信号从 GPU 进程传播到渲染器。如果没有这两个标志,Chrome 会静默吸收 GPU 进程的崩溃,易受攻击版与已修补版构建之间的行为差异便无法从 JavaScript 中观察到。
由于逆向工程和二进制比对工作仍在进行中,自动化运行器尚未发布在此仓库中。待补丁分析完成后,它将被包含在后续更新中。