#3 Balancer V2 事件:四舍五入不一致破坏不变量并跨链传播

#3 Balancer V2 事件:四舍五入不一致破坏不变量并跨链传播

#3 Balancer V2 事件:舍入不一致破坏了不变量并跨链传播

2025 年 11 月 3 日,Balancer V2 的组合稳定池以及多个链上的多个分叉项目遭遇了协同攻击,导致总损失超过 1.25 亿美元,据报道约有 4500 万美元被追回。根本原因是由于向上缩放和向下缩放操作之间的舍入不一致导致的不变量计算中的精度损失,从而使得 BPT(Balancer 池代币)定价逻辑出现偏差。

此次事件不仅因损失规模巨大,也因其底层 bug 的隐蔽性,成为 2025 年十大安全事件之一。此外,此次攻击迅速蔓延至多条链,影响了 Balancer 及其分叉项目,凸显了共享代码库和可组合 DeFi 基础设施如何显著放大系统性风险。

我们发布了一份详细的报告《深度分析:Balancer V2 攻击事件》[1],其中提供了详细的技术 breakdown。下面,我们对此次事件进行简要说明。

背景

Balancer V2 的组合稳定池

此次攻击中受影响的组件是 Balancer V2 协议的组合稳定池[2]。这些池设计用于预期保持接近 1:1 比例(或以已知汇率交易)的资产,并允许进行大额交换,对价格影响极小,从而显著提高了同类或相关资产的资本效率。每个池都有自己的 Balancer 池代币 (BPT),代表流动性提供者在池中的份额,以及相应的底层资产。

  • 该池采用稳定币数学模型(基于 Curve 的 StableSwap 模型),其中不变量 D 代表池的虚拟总价值。

  • BPT 价格可以近似表示为:

Price(BPT)DtotalSupplyPrice(BPT) \approx \frac{D}{totalSupply}

从上述公式可以看出,如果 D 在账面上可以变小(即使没有实际资金损失),BPT 的价格就会显得更便宜。

`batchSwap()` 和 `onSwap()`

Balancer V2 提供了 batchSwap() 函数,允许在 Vault[3] 内进行多跳交换。根据传递给该函数的参数,可以确定两种交换类型:

  • GIVEN_IN(“给定输入”):调用者指定确切的输入代币数量,池计算相应的输出数量。
  • GIVEN_OUT(“给定输出”):调用者指定所需的输出数量,池计算所需的输入数量。

通常,batchSwap() 由通过 onSwap() 函数执行的多个代币到代币的交换组成。这个过程不可避免地涉及与不变量 D[1] 相关的数量计算。

缩放与舍入

为了统一不同代币余额的计算,Balancer 执行以下两个操作:

  • 向上缩放:在执行计算之前,将余额和数量向上缩放到统一的内部精度。
  • 向下缩放:将结果转换回其原生精度,应用定向舍入(例如,输入金额通常向上舍入,以确保池不会少收费用,而输出金额通常向下舍入)。

向上缩放和向下缩放理论上是配对操作:分别对应乘法和除法。但是,这两个操作的实现存在不一致。 具体来说,向下缩放操作有两个变体或方向:“divUp”和“divDown”。相比之下,向上缩放操作只有一个方向,即“mulDown”。

漏洞分析

根本问题源于 BaseGeneralPool._swapGivenOut() 函数中向上缩放期间执行的向下舍入操作。特别是,_swapGivenOut() 通过 _upscale() 函数错误地将 swapRequest.amount 向下舍入。由此产生的舍入值随后在通过 _onSwapGivenOut() 计算 amountIn 时用作 amountOut这种行为与协议受益的舍入原则相悖。

因此,对于给定的池(wstETH/rETH/cbETH),计算出的 amountIn 低估了实际所需的输入。这允许用户以较少的某种底层资产(例如 wstETH)兑换另一种资产(例如 cbETH),从而由于有效流动性的降低而导致不变量 D 减少。因此,相应 BPT(wstETH/rETH/cbETH)的价格被人为压低,因为 BPT 价格 = D / totalSupply。

攻击分析

攻击者执行了一个分为两个阶段的攻击,以尽量减少被发现的风险:

  • 在第一阶段,核心攻击在一次交易内完成,没有立即获利。
  • 在第二阶段,攻击者通过另一次交易提取资产来获利。

第一阶段可进一步细分为两个阶段:参数计算批量交换。下面,我们将以 Arbitrum 上的一次攻击交易(TX)为例,来说明这些阶段。

参数计算阶段

在此阶段,攻击者结合链下计算和链上模拟,根据组合稳定池的当前状态(包括缩放因子、放大系数、BPT 费率、交易费用以及其他参数),精确地调整下一阶段(批量交换)的每个跳跃参数。攻击者还部署了一个辅助合约来协助这些计算,这可能是为了减少被抢跑的风险。更多细节请参阅[1]。

批量交换阶段

然后,batchSwap() 操作可分为三个步骤:

步骤 1:攻击者将 BPT(wstETH/rETH/cbETH)兑换为底层资产,以精确地将一种代币(cbETH)的余额调整到舍入边界的边缘(数量 = 9)。这为下一步的精度损失创造了条件。

步骤 2:攻击者随后使用一个精心设计的数量(= 8)在另一种底层资产(wstETH)和 cbETH 之间进行交换。由于在缩放代币数量时向下舍入,计算出的 Δx 略微减小(从 8.918 到 8),导致 Δy 被低估,从而导致不变量(来自 Curve 的 StableSwap 模型 D)减小。由于 BPT 价格 = D / totalSupply,BPT 价格被人为压低。

步骤 3:攻击者将底层资产反向兑换回 BPT,恢复平衡,同时从被压低的 BPT 价格中获利。

总结

此次事件涉及一系列协同攻击交易,目标是 Balancer V2 的组合稳定池以及跨不同链上的多个分叉部署,导致巨额损失。在第一次攻击之后,模仿攻击交易迅速涌现,表明一旦攻击模式暴露,其传播速度有多快。

关键教训:

  • 舍入与精度处理:所有代币金额的缩放和精度操作都应该朝着有利于协议的方向舍入。_upscale()(仅向下舍入)与向下缩放操作(定向舍入)之间单一的不一致,就足以造成可被利用的价格失真。
  • 安全军备竞赛:攻击者将操纵和利润提取拆分成独立的交易以逃避检测。检测系统应该关联相关交易,而不仅仅是标记单个交易。
  • 操作安全:一旦攻击模式公开,模仿者在几分钟内就在跨链复制了该模式。共享代码库的协议需要协调监控和快速跨链暂停能力。

参考

  1. https://blocksec.com/blog/in-depth-analysis-the-balancer-v2-exploit

  2. https://docs-v2.balancer.fi/concepts/pools/composable-stable.html

  3. https://docs-v2.balancer.fi/reference/swaps/batch-swaps.html


关于 BlockSec

BlockSec 是一家全栈式区块链安全与加密合规提供商。我们构建的产品和服务,旨在帮助客户在其协议和平台的整个生命周期中,进行代码审计(包括智能合约、区块链和钱包)、实时拦截攻击、分析事件、追踪非法资金,并满足 AML/CFT 合规要求。

BlockSec 已在知名会议上发表多篇区块链安全论文,报告了多起 DeFi 应用的零日攻击,阻止了多起黑客攻击,挽救了超过 2000 万美元的资产,并确保了数十亿美元加密货币的安全。

Sign up for the latest updates