2025-12-16 05:07:43
loading...

运行时防护:抵御隐蔽数值攻击的全新防线

摘要
过去一个月里,我们目睹了两起针对密切相关系统的重要漏洞利用事件:Balancer的可组合稳定池和Yearn的yETH。具体细节有所不同——数学原理和代码路径并不完全相同——但故障的形态却惊人地相似:都是在精度边缘出现的微妙数值行为,通过高度复杂的交易序列被利用。反复出现的模式:微妙的数值边界情况最近的这两起事件都有一个

过去一个月里,我们目睹了两起针对密切相关系统的重要漏洞利用事件:Balancer的可组合稳定池和Yearn的yETH。具体细节有所不同——数学原理和代码路径并不完全相同——但故障的形态却惊人地相似:都是在精度边缘出现的微妙数值行为,通过高度复杂的交易序列被利用。

反复出现的模式:微妙的数值边界情况

最近的这两起事件都有一个共同的模式:

在正常情况下,微小的舍入或精度误差在经济上是无关紧要的;但在精心设计的对抗性条件下,这些同样的误差却主导了结果;攻击者可以有意识地将系统驱动到这些边界条件,然后提取价值。

这并非单一设计所独有。任何结合了复杂数学、数值方法和可组合性的协议都面临此类风险。

在下文中,我们将以Balancer漏洞利用作为此模式的具体运行示例,然后展示相同的防御思路——尤其是运行时强制检查——如何更广泛地应用,包括yETH事件和未来的设计。

Balancer漏洞利用:机制

让我们剖析一个看似微小的舍入怪癖是如何级联成大规模漏洞利用的。

微小的舍入误差:问题不大——除非流动性变得极小

大多数人指出的直接原因是向“错误”方向的舍入——比如差了“1 wei”。然而,在一个资金充裕的资金池中,正常交易中相差一个单位是可以忽略不计的——其消耗的Gas费将超过任何点滴收益。

真正重要的不仅仅是舍入误差的方向,还包括其与结果值相比的相对大小。在深度池中难以察觉的误差,在浅池中可能主导结果。

当池流动性极小时,情况就变得危险。想象一个储备为(10 wei, 10 wei)的资金池。如果舍入误差让交易者拿走2 wei而不是1 wei,这额外的1 wei会使储备改变10%,从而实质性地扭曲价格。(这是一个简化的说明——实际的数学原理更为微妙。)

因此,如果攻击者能够使流动性暂时变小,那么方向不利的舍入误差就可能足以扭曲储备比率,从而操纵价格并提取价值。

但在一个健康的去中心化资金池中,暂时地大幅减少流动性是很困难的。你需要说服许多流动性提供者协调退出。

暂时低流动性如何变得可行

在一种情况下这是可行的,即当LP代币可以被任意铸造和借入,或者通过闪电贷获得。在这种情况下,攻击者可以有效地借入LP代币,执行流动性退出以缩小资金池,利用舍入驱动的价格扭曲,然后恢复流动性并以更低的LP代币价值偿还。

在Balancer的可组合稳定池中,两个设计特性相结合,实现了类似LP闪电贷的效果:具有聚合结算的批量交换,系统在最后轧差余额,而不是立即结算每条路径;将LP铸造/销毁视为代币交换,这意味着它们现在可以像普通交易一样参与批量交换。

如果你在批次早期“退出流动性”并在最后“添加流动性”,你实际上在批次持续期间获得了LP代币的闪电贷。

在流动性被暂时压低的情况下,攻击者就可以利用方向性舍入来 skew 储备比率并操纵价格。(我们在此省略了详细的算术——完整的演练请参阅其他文章。)

正是这种组合——而不仅仅是舍入本身——是关键所在。

这种模式——在正常情况下经济上可忽略的误差,但在对抗性诱导的边缘条件下却占主导地位——也正是我们在yETH事件中看到的,尽管具体的公式和代码路径有所不同。

从攻击到防御

理解了机制后,让我们来看看防御措施——首先快速回顾一下基线缓解措施及其不足之处,然后是能够进一步推进防御的运行时强制检查技术。

基线防御

从基础措施开始。

1. 对资金池有利的舍入:这直接解决了Balancer事件的根源。但这并非总是小事一桩:复杂的数学可能需要跨代码路径维护“向上舍入”和“向下舍入”两种变体,并且确定哪个方向对资金池有利可能因上下文而异。随着DeFi数学变得越来越复杂,严密的定向舍入也变得更加困难。

此外,“对资金池有利”的舍入并非没有副作用。因为它系统性地使结果不利于交易者,攻击者有时可以利用这些舍入行为从其他用户而非资金池本身提取价值——ERC4626金库的“通胀攻击”就是这种动态的一个很好的例子。换句话说,让舍入总是有利于资金池并不一定能消除可利用性——它可能只是转移了谁承担精度损失。

2. 虚拟流动性:一种减轻低流动性脆弱性的方法是引入有利于协议的虚拟储备偏移量,并保持流动性计算的数值稳定性。这种方法有助于在真实储备变薄时维持最低的有效深度。

另一个更简单的变体是向每个资金池注入相当数量的初始流动性,并永久锁定。它以最小的成本实现了类似的稳定效果——在大多数情况下可以忽略不计,但如果大规模自动创建资金池,则可能会累积起来。

这两种方法都在流动性稀薄的情况下增强了精度和稳定性,但它们以简单性换取了要么增加数学复杂性,要么轻微的资本锁定。

强制高精度定点算术

虽然相对未经测试,但对除法运算本身强制精度要求可能是合理的。这将涉及测量除法运算的输出,并确保其高于指定的精度阈值。通过这样做,协议可以确保所有关键的舍入操作都足够精确,使得所有舍入误差在数学上都可以忽略不计。

在采用安全数学作为默认设置之后,DeFi协议中的数值误差就是舍入误差。舍入误差源于一种运算——除法。那么,我们如何确保除法引入的误差小于某个特定比例呢?我们检查余数。

说明性伪代码

为什么这个条件检查精度损失

从数学上讲,任何分子为n、分母为d、商为q、余数为r的除法可以写成 n = q × d + r,其中 0 ≤ r < d。

真实的(数学的)除法结果 v 是 n / d = q + r / d,而整数除法结果(在Solidity中执行)是 ⌊v⌋ = q。

因此,向下舍入的相对精度损失为:

我们希望这个损失小于一个选定的精度阈值,例如 ≤ 1/10^p。

于是得到:

因此,检查 `require(remainder <= numerator / 10**precision)` 正是强制执行这个条件——它保证除法运算的相对精度损失小于 1/10^p。

如果余数相对于分子过大,则操作回退,确保所有影响协议状态的除法运算至少保持指定的精度。

局限性

这种方法的一个局限性是它只限制单次除法的精度损失。

在迭代或复合计算中——例如,计算 (1 + r)^n——小的舍入误差可能在多个步骤中累积,最终即使每个单独的操作都在界限内,也会超过精度阈值。

解决这个问题需要跟踪整个计算路径上的累积精度损失,这并非易事,并且会增加开销。

注意事项与权衡

这种运行时强制检查对于安全性非常有益——它在异常行为发生之前就阻止它们——但它也可能矫枉过正。

如果条件过于严格,可能会出现活跃性问题:无害的交易可能仅仅因为略微超出界限而回退。一旦发生这种情况,资金可能会被卡住。

为避免这种情况,请确保你的强制阈值不要比实际需要的更紧。并且作为备份,考虑使用可升级合约或管理员控制的强制检查开关,这样如果发生资金锁定,可以解除限制。

即使有这些注意事项,对关键操作强制精度要求直接针对了我们刚刚在两起独立漏洞利用事件中反复看到的“边缘条件下的微妙数值行为”模式。

安全关键不变量的运行时断言

当数学复杂性或复合精度损失使得难以完全控制舍入行为时——例如跨多个操作的累积误差——最后一道防线是将协议不变量编码为运行时断言。如果某个操作违反了不变量,它应立即回退。

关键的是,这适用于这两起事件:尽管具体公式不同,但在每种情况下,核心的经济不变量在价值提取过程中都被破坏了。如果该不变量被明确编码并在运行时强制执行,那么无论利用了哪种特定的算术怪癖,漏洞利用路径都会被阻断。

以AMM为例,有两个核心属性应该成立:

1. 总流动性不应减少(流动性退出除外)。例如,对于恒定乘积市场,这意味着 k = x * y 保持不变(因此得名)或随着费用累积而略有增加(部分原因也在于对资金池有利的舍入)。更一般地说:资金池的总价值不应在正常(非退出)操作下缩水。

2. LP代币价值不应减少。也就是说,“总流动性 / 总LP供应量”作为任何操作的结果都不应下降——即使在流动性退出之后也是如此。

每一个主要的AMM漏洞利用最终都违反了这些属性之一。

当然,形式化验证这些属性是理想的,但对于复杂的数学往往不切实际。(在Balancer的案例中,形式化验证并未捕获这些特定属性。)当编译时证明不可行时,在运行时强制不变量可以作为一个强大的安全网。

运行时强制检查的说明性伪代码:

这种方法——被称为运行时强制检查或运行时验证——在传统软件工程中早已存在,但在DeFi中亟需更广泛的采用。

请求审计人员帮助识别安全关键属性,并尽可能多地将它们编码为代码内断言。你无法捕获所有问题,但你可以阻断一整类漏洞利用——包括那些植根于尚未被发现的全新漏洞类别的攻击——只要任何此类攻击都需要打破你在运行时强制执行的某个不变量。

话虽如此,这里需要同样的谨慎:虽然运行时强制检查对于安全性很强大,但过于严格的不变量可能会引入活跃性问题——即使不存在真正风险,良性交易也会回退。为避免锁定资金或扰乱正常的资金池操作,不变量必须源自严格的分析并忠实地实现,然后进行广泛测试。并且作为后备方案,考虑使强制检查逻辑可配置,这样如果出现活跃性或资金锁定问题,可以放宽或暂时解除限制。

***

最近的事件表明,微妙的数值不稳定性如何转化为真实的漏洞——以及使它们可被利用的条件是多么难以提前检测。

对数值精度和安全关键不变量进行运行时强制检查,提供了一种互补的——且越来越必要的——最后防线:一种确保即使 unforeseen 的数学微妙之处被遗漏,协议仍将拒绝任何违反核心经济属性的状态的机制。

通过将数值规范与运行时强制的不变量相结合,我们可以构建即使在对抗性条件下——包括我们尚未遇到的情况——也能保持正确和弹性的DeFi系统。

我们也在探索一项社区工作,旨在为主要的DeFi设计编目可运行时检查的安全属性——我们欢迎任何有兴趣帮助推动这项工作的人。

声明:文章不代表币圈网观点及立场,不构成本平台任何投资建议。投资决策需建立在独立思考之上,本文内容仅供参考,风险自担!转载请注明出处!侵权必究!
币圈快讯
查看更多
回顶部