一个缺失的零:Payy 跨链桥如何接受伪造的 ZK 证明,损失 192 万美元

发布时间:2026/10/23 10:40:09

一个缺失的零:Payy 跨链桥如何接受伪造的 ZK 证明,损失 192 万美元 一个缺失的零Payy 跨链桥如何接受伪造的 ZK 证明损失 192 万美元2026 年 9 月 24 日Payy 在攻击者从其以太坊跨链桥合约中提走 1,918,793.198148 USDC 之后暂停了整个网络。最容易讲的故事是私钥被盗。但公开证据指向别处。Payy 已经排除了私钥泄露、社会工程攻击以及链下基础设施被利用的可能。攻击者提交了一笔背后没有任何资金支撑的提现并附上一份伪造的零知识证明。Payy 的验证器接受了这份证明。随后又发生了两笔金额较小的伪造提现。根因是 Barretenberg 小群 IPA 检查中的一个可靠性漏洞。所谓可靠性漏洞就是让验证器接受一个虚假陈述的证明。这个检查没有强制某个累加和从零开始。用软件的话说验证器约束不足。这才是真正的问题所在。不是“ZK 坏了”而是一条约束缺失、部署的验证器版本过旧、升级路径又太慢。攻击路径一步一步看Payy 的余额是隐私 note。一笔交易最多花费两个输入 note创建最多两个输出 note链上只公开它们的承诺也就是代表隐藏 note 的指纹。burn 是 Payy 对“提现到以太坊”的术语。它还会公开金额、以太坊收款地址和 burn hash而电路会把 burn hash 设为第一个输入承诺。第一笔伪造 burn 公开了四个零承诺、一个零 burn hash、金额 1,828,589,378,132 个基础单位以及攻击者地址。USDC 有六位小数所以这是 1,828,589.378132 USDC。四个零承诺就是填充 note价值为零。交易前后 note 树的根完全一样。如果直接拿这笔交易去跑 Payy 的 burn 电路它会失败。价值守恒断言会拒绝一笔由零价值 note 支撑的正数提现。note 类型断言也会失败因为填充 note 的类型是 0而消息里写的是 USDC note 类型。Payy 不会对每笔提现重跑电路它依赖的是“这笔交易遵守了电路规则”的密码学证明。攻击者的伪造证明通过了 Payy 的验证器。Payy 的事后报告把这一点归因于 Barretenberg 的可靠性漏洞。Payy 节点内部的验证器首先接受了这份证明。Payy 的第一层聚合电路会用编译进电路的同一个 Barretenberg 验证器再验证每笔交易证明而上层只验证下一层。出于隐私考虑没有任何一层能看到 note 的价值所以没有任何一层能独立重做余额检查。Payy 自己的 prover 提交了这批交易Payy 的验证者签了名rollup 合约随后支付了 burn。rollup 合约没有“burn hash 必须非零”这条规则。这条规则本可以拦下这三条消息但它治不了验证器的病。时间线与损失第一笔伪造 burn 在 UTC 03:25:41 被记录在 Payy 自己的链上高度 33,195,439。UTC 04:21:23Payy 的 prover 在以太坊区块 26,044,909 中结算了这批交易。跨链桥把 1,828,589.378132 USDC 转给 0xAa4985dBDaBfACa344237D40F7E06C4a0BB57E70。八分钟内资金进入第二个钱包并卖出换成约 683 ETH。UTC 04:21:47一个 burn 垫付方支付了攻击者的第二笔 burn金额 1 USDC。垫付方会提前支付提现等提现结算后再收回款项。跨链桥大约五小时后偿还了它。这两笔转账里只有一笔是损失。UTC 09:30:35第二笔结算在区块 26,046,439 中向同一地址支付了 90,202.820016 USDC并偿还了垫付方。总损失1,828,589.378132 1 90,202.820016 1,918,793.198148 USDC。UTC 14:04Payy 宣布遭到攻击并暂停所有网络交易。Payy 追踪到第一笔付款中的 682.3 ETH 在 9 月 26 日通过 19 笔存款进入 Tornado Cash。截至 10 月 11 日最后那笔 90,202.82 USDC 仍在收款钱包里。截至 10 月 11 日没有任何公开信息表明 Payy 已恢复运行。其公开仓库默认分支自 6 月 24 日以来没有新提交。代码问题一个方程两个未知数公开的重构资料描述了 Payy 在提交 2f95947f 中 burn 分支的四条断言。关键逻辑简化后大致如下。这不是生产环境的精确代码但能看出检查的形状。// Payy burn 分支的简化模型非生产代码。fnburn_circuit(inputs:[Note;2],outputs:[Note;2],msg:BurnMsg){letinput_valueinputs[0].valueinputs[1].value;letoutput_valueoutputs[0].valueoutputs[1].value;assert_eq!(input_value,output_valuemsg.value);// 价值守恒assert_eq!(msg.burn_hash,inputs[0].commitment);// burn 绑定第一个输入assert!(msg.value0);// 提现金额为正assert_eq!(inputs[0].kind,msg.kind);// note 类型匹配消息}用四个填充 note 时input_value和output_value都是零。正数的msg.value会通不过价值守恒。类型断言同样会失败。在这个故事里电路本身不是弱点验证器才是。Barretenberg 的小群 IPA 检查会对一个累加和做记账。prover 声称一组数字的总和验证器要在看不到这些数字的情况下确认总和。规则很简单累加和必须从零开始。每个值必须等于前一个值加上列表中的下一个数。最后一个值必须等于声称的总和。旧检查把前两条规则揉进了一个方程。一个方程无法钉死两个未知数。恶意 prover 可以让累加和从错误的值开始并让第一步错出恰好抵消的量。之后每一步都是诚实的之后每个值都偏离同一个常数总和也一样。这个常数由 prover 选总和也就由 prover 选。简化模型能看出这个漏洞。// 简化模型不是 Barretenberg 的实际代码。// 旧检查把两个义务揉进了一个等式。fnold_check(a:[Fr],t:[Fr],s:Fr,g:Fr)-bool{a[0]g*(a[1]-a[0]-t[0])Fr::ZERO(1..t.len()).all(|i|a[i1]a[i]t[i])a[t.len()]s}// 正确的检查起始条件单独约束。fnfixed_check(a:[Fr],t:[Fr],s:Fr)-bool{a[0]Fr::ZEROa[1]a[0]t[0](1..t.len()).all(|i|a[i1]a[i]t[i])a[t.len()]s}旧验证器在这个步骤里检查 prover 承诺值的四条断言。修复后的代码检查五条多出来的就是a[0] 0。效果不是表面功夫。一个 Lean 4 模型用 Lean 4.34.1 和 Mathlib 验证了两条性质。文件里没有sorry除 Lean 三条标准公理外不依赖其他公理。定理和证明一共 57 行。第一条定理表明当位置数至少为二且g ! 0时旧检查对任意数字列表和任意声称的总和都会接受错误的总和。第二条表明当g ! 1时修复后的检查会强制被接受的总和等于真实总和。这并不能证明整个 Barretenberg 是可靠的。它只证明了一个检查在两条假设下的性质承诺把 prover 绑定到一个累加和上且抽查都得到诚实回答在随机点上通过一个组合方程意味着除可忽略概率外三条等式在每个位置都成立。这两条假设把模型和部署的验证器连起来但它们不覆盖 C 代码、Solidity 验证器、Payy 的电路或 Payy 的合约。不过它们说明按模型来看旧检查确实会接受错误总和而修复移除了那一个自由度。Payy 的复现包包含那份 16,256 字节的证明和公开输入。根据事后报告这份证明在 Payy 当时运行的构建下能通过验证。把公开的 burn hash 从 0 改成 1同一份证明就会失败。这确认了验证器会拒绝被修改的输入但并不能说明攻击者最初是怎么构造出那份证明的。修复为什么没进生产Payy 把服务锁定在标记为3.0.0-manual.20251030的 Barretenberg 构建上配套 Noir1.0.0-beta.14。Cargo.toml里有一行、六个 Dockerfile 都指向这个构建。Aztec 在 2026 年 5 月 14 日提交了修复。提交号58bf73a标题fix!: small subgroup ipa bug (#44)。它改了 34 个文件包括原生验证器、递归验证器和 Solidity 验证器模板。Aztec 在 2026 年 7 月 13 日的 v5.0.0 中发布了修复。6 月的两个 v5 候选版本不含此修复6 月 27 日发布的 v4.4.0 也不含。Payy 被攻击发生在 2026 年 9 月 24 日距离含修复的公开版本发布已经 73 天。Payy 在 2026 年 5 月 15 日部署了一个新的链上验证器用来修复自己某个电路里的另一个严重漏洞。合约创建时间比 Aztec 提交记录的时间晚 20 小时 49 分钟。事件报告说这个合约仍由 2025 年 10 月的构建生成。一次全新的部署用的还是旧验证器。Payy 最后一次公开仓库同步在 6 月 24 日仍指向 2025 年 10 月的构建。5 月 15 日部署的验证器到 9 月 24 日仍在使用。对 Payy 这样的 rollup 来说升级 Barretenberg 不是改一行依赖那么简单。修复落在一个新的大版本里。验证器被编译进 Payy 的每一层聚合电路电路、验证密钥和链上验证器合约必须一起重新生成、一起重新部署。Aztec 3 月 10 日的安全公告说修复会打包进 v5 发布对于需要新验证密钥的严重漏洞会先通知其网络门户portal的运营者再在论坛发公告。Payy 不在那个网络上它把 Noir 和 Barretenberg 当作库用在自己的 rollup 上。一个库如果有厂商网络之外的用户就需要一条能触达这些用户的渠道。到 7 月 13 日修复已经公开提交还给协议文档加了一节明确写出这种伪造方式。但 Aztec 仓库里没有任何安全公告点名这个漏洞。10 月 8 日Aztec 针对另一个 Barretenberg 验证器漏洞发布了两份安全公告并建议“直接使用 Barretenberg 的用户”打补丁。直接用户需要的是事前通知不是事后通知。该改什么升级到包含58bf73a或之后提交的 Barretenberg 版本。重新生成电路、验证密钥和链上验证器合约把它们作为一次协调好的发布一起重新部署。效果补上缺失的约束恢复“被接受的总和必须等于真实总和”这条性质。不要把合约层的burnHash ! 0检查当成修复。它能拦下这三条消息但验证器仍然会接受伪造证明。把它当纵深防御加上去而不是根因治理。给单笔提现加上限对大额提现加延迟。效果一份伪造证明无法在一次结算里掏空整个跨链桥团队也有时间暂停和调查。Aztec 3 月 10 日的公告给其网络门户建设者的也是这个建议。给依赖项建立验证器变更监控。对你编译进生产的每一套证明栈都要读上游的安全提交和发布说明。锁版本可以但别锁完就不管。效果可靠性修复会变成一张可执行的工程工单而不是出事之后才看到的博客文章。保持一条能在几天内完成的电路、密钥和验证器合约重建路径。如果某个严重验证器修复需要新大版本和协调重部署那就在需要之前先演练一遍。效果上游发布到生产部署之间的窗口会缩短。对关键验证器检查使用形式化模型。57 行的 Lean 模型本身发现不了这个漏洞但它把检查必须满足的性质写清楚了。对旧检查来说这条性质是假的对修复后的检查来说在给定假设下它成立。效果代码评审有了一个精确的问题要回答而不是“证明系统大概没问题”的模糊感觉。在隐私允许的地方加独立不变量。非零 burn hash、每批最大 burn 额度、验证器版本白名单这些都不是可靠性的替代品而是爆炸半径控制。效果它们限制单次验证器失效能造成的后果。总结Payy 这件事的核心不是私钥被盗。它是一个验证器因为少了一条约束而接受了虚假陈述也是一个生产系统在修复公开 73 天后仍在运行 2025 年 10 月的构建。缺的那一行很小流程上的失败很大。如果你的系统接受证明你跑的不是电路你信的是验证器。把验证器当生产安全代码对待盯住上游每一次变更保持重部署路径随时可用。还要记住在证明系统里一个缺失的零可以值 192 万美元。
延伸阅读

更多相关文章

2026/10/23 10:40:09

Ubuntu 18.04.5镜像安装全攻略:从下载校验到虚拟机与U盘实践

简介:Ubuntu 18.04.5桌面版amd64架构ISO镜像的下载指南文档,面向需要获取或安装Ubuntu桌面版的Linux用户,尤其适合初学者参照操作。文档提供了百度网盘链接与提取码,并说明该镜像基于Linux 4.15内核、包含GNOME桌面环境&#xff0…

2026/10/23 10:40:09

老硬件装机首选:Ubuntu 18.04.5 ISO镜像下载与安装详解

简介:一份专门面向需要获取Ubuntu 18.04.5桌面版amd64架构ISO镜像的Linux用户的docx文档,旨在解决镜像下载入口难找、安装前需要确认系统要求等实际问题。整个资源仅包含1个docx文件,大小仅13KB,内容围绕ubuntu-18.04.5-desktop-a…

2026/10/23 10:40:09

DeepSeek本地部署实战:Ollama+Distill-Llama轻量推理全指南

简介:本资源是一份面向技术开发者、企业IT人员及AI爱好者的本地化大语言模型部署指南,聚焦DeepSeek模型在私有环境中的安全可控落地,解决敏感数据不出域、模型自主调优与垂直场景适配等核心需求。内容覆盖DeepSeek技术特性、多档位硬件配置建…

2026/10/23 12:41:14

粒子群算法动态参数机制:惯性权重与学习因子自适应设计

简介:本资源是一份面向算法研究者与MATLAB初学者的粒子群优化(PSO)进阶实践材料,聚焦惯性权重与学习因子的动态协同调整策略,解决标准PSO易早熟收敛、探索-开发失衡等核心问题,适用于智能优化、工程参数寻优…

2026/10/23 12:41:14

Ubuntu下Anaconda虚拟环境全攻略:conda安装、隔离与迁移实践

做开发这几年,Ubuntu 一直是我的主力系统,但最早用 Python 的时候,我并没有认真对待“环境管理”这件事。系统全局的 Python 里装了一堆包,今天给 A 项目装 pandas,明天给 B 项目装 tensorflow,半年后自己都…

2026/10/23 12:41:14

UE4项目运行时显示鼠标指针:蓝图Show Mouse Cursor配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/22 11:29:28

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/23 0:02:26

Verilog实现FPGA密码锁:矩阵键盘与状态机设计实战

说实话,这几年接触了不少想入门FPGA的朋友,如果你问哪个项目能一口气把Verilog的核心基本功串起来,我通常会推荐电子密码锁。这个项目看着不起眼,却把矩阵键盘扫描、按键消抖、状态机设计、动态显示刷新、外设控制这些数字逻辑里的…

2026/10/23 0:02:26

数据服务监控运维实战:从指标体系到告警治理与故障排查

周末凌晨两点,手机震了。第一反应是“数据服务挂了”。打开电脑看了下,果然,某张核心报表的数据产出时间已经超过SLA两小时,下游三个业务方连着发来问询。这种场景,做过大数据运维的人都不会陌生。数据服务的监控与运维…

2026/10/23 0:02:26

FreeRTOS任务通知实战详解:内核原理、API与避坑指南

裸机编程切到RTOS之后,任务之间的通信和同步永远是躲不开的坎。前面聊过队列、信号量、互斥锁、事件组,这次把FreeRTOS任务通知(Task Notification)单独拎出来讲透。原因很简单:任务通知是FreeRTOS里最容易用错、也最容…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑