远程 Spectre 攻击 Cloudflare Workers:树形 PLRU 放大与远程计时器
Spectre 类攻击利用 CPU 推测执行泄漏数据,通常被认为难以远程实施。Cloudflare 安全团队发布文章,详细复现了对 Cloudflare Workers 的端到端远程 Spectre 攻击:用树形 PLRU 缓存替换策略放大单个缓存事件、用远程 WebSocket 计时器区分比特、靠 Durable Objects 让隔离环境存活数小时,最终在真实生产环境中以 99% 以上准确率、最高 12 bit/s 的速度从受害者 Worker 的 JavaScript 堆中逐位泄出 JWT token。
攻击链的四个关键环节
放大信号。CPU 缓存是 Spectre 攻击的核心信道。Cloudflare 复现的攻击利用树形 PLRU(Pseudo-LRU)缓存替换策略:每个缓存组是一棵二叉树,节点指向最近最少使用的一侧,CPU 沿指针驱逐。攻击者用特定访问模式,让目标缓存行无限期驻留——访问其树邻居即可。单个缓存事件的计时差异被任意放大:未缓存时访问模式产生大量 L1 命中(更快),已缓存时四条缓存线挤进三个节点产生大量 L1 未命中。
远程计时器。信号放大后,带噪声的远程计时器就足以区分编码比特。攻击用 WebSocket 连接外部服务器获取高分辨率时间戳(可以放在 Cloudflare 或与目标数据中心同址),Worker 请远程计时器为事件打时间戳并计算增量。论文评估了多种计时器设置,跨越较大拓扑距离也能以少量样本可靠达到亚毫秒级分辨率。
免驱逐的测量循环。单次测量不足以可靠区分时序编码数据,生产机器噪声大,攻击者需要重复测量并用统计判别器。重复意味着要重置缓存状态:让分支依赖值和探针行都被逐出。经典做法是构造驱逐集,但精确构造需要大量计时测量,而远程计时器有噪声。解决方案来自鸽笼原理:分配远超缓存容量的数据(256KB L2 对应分配 64MB),随机缓存行几乎必然未缓存,于是从不驱逐,每轮随机挑选新的已驱逐位置。频繁循环访问大对象数组还带来自动驱逐效果。
保持隔离环境存活。攻击需要攻击者和受害者隔离环境在同一台边缘服务器的同一进程中调度。Cloudflare Workers 设计上可在任意边缘服务器执行,攻击者 fetch("https://victim.example") 大多会让调度器在同一进程内拉起受害者实例,并靠定时子请求保持存活。更妙的是 Durable Objects:运行时把每个入站 WebSocket 消息当作一次调用,重置 CPU 时间和请求限制。攻击者通过持久 WebSocket 发送保活消息,单线程隔离环境在同步代码期间不会看到保活(不会重置 CPU 时间),于是作者选择周期性让出控制权,把隔离环境存活时间从几分钟拉到 5 到 20 小时以上——这同时规避了 DyPrIs 的检测:它在调用结束后才隔离可疑脚本,而攻击在隔离生效前数小时已完成泄漏。
端到端验证
作者在 Cloudflare Workers 生产环境中对自己控制的 Worker 完成了完整攻击:先用 64 位指针的推测类型混淆从攻击者 Worker 泄漏隔离根,再读取 vDSO 区域确认 64 位泄漏,最后把 JWT 放入受害者 Worker 逐位泄漏。分类使用双侧检验加多数投票和百分位阈值,生产环境达到最高 12 bit/s、准确率 99% 以上。
修复方向
Cloudflare 的响应聚焦三块:继续加固 V8、提供更强的检测与缓解、以及生态层面的协作。这次攻击说明,即便有进程级隔离的运行时,CPU 微架构侧信道依然是不可忽视的威胁面;对构建多租户运行时和依赖沙箱隔离的开发者,这篇分析也是一份有价值的防御参考。
来源:A revisit of remote Spectre attacks on Cloudflare Workers - Cloudflare Blog