在 2025 年 5 月 22 日,Cetus Protocol,作为 Sui 上最大的集中流动性 DEX,遭受了一次重大的漏洞攻击,导致多个池中的流动性被耗尽 [1],估计损失高达 2.23 亿美元。
此次漏洞源于一个自定义的防溢出函数(即 **checked_shlw()**)中的缺陷 [2]。由于常量和比较不正确,该函数在执行用于定点 u256 数学运算的左移之前未能检测到不安全状况。这导致在关键的 delta 计算(例如,计算给定流动性所需的代币输入量)过程中,最高有效位被静默截断。通过仔细选择参数(流动性大小和 tick/价格范围设置),攻击者能够使协议计算所需的存款量本质上为1 单位代币,同时该头寸仍被记入巨额流动性。一旦拥有这个膨胀的头寸记录在链上,攻击者随后移除流动性并提取真实储备,从而耗尽了池子。
背景
Cetus 采用集中流动性做市商 (CLMM) 设计,其中流动性提供者 (LP) 仅在选择的价格范围内(一个 tick 区间)提供流动性。LP 不是将资金均匀地存入所有价格,而是将资本集中在较低和较高边界之间。这提高了资本效率,但使得协议在转换以下两者之间时,高度依赖于精确的定点数学:
- 计入头寸的流动性数量,和
- 必须存入(或可以提取)的实际代币数量。
从宏观上看,当用户添加流动性时,协议会根据当前价格和选择的范围计算代币 delta(所需的底层代币数量)。当用户移除流动性时,协议会执行反向计算,以确定该头寸应有权提取多少。
如果“您必须存入多少”的计算可以被做得过小,同时头寸仍被记入巨额流动性,攻击者就可以稍后移除该记入的流动性并从池子中提取真实储备。
漏洞分析
根本原因在于一个辅助函数中存在一个错误,该函数旨在安全地执行用于定点 u256 算术运算的左移(通常是 << 64 以应用 2^64 的缩放因子)。在基于 Move 的系统中,溢出检查并非在所有操作中都得到统一强制执行,而位移是开发人员经常添加手动保护措施的领域之一。
Cetus 实现了一个防溢出辅助函数(即 checked_shlw()),以确定将 u256 值向左移动 64 位是否会超出 256 位边界。然而,由于常量和比较不正确,对于某些本应被拒绝的大输入,该检查可能会被绕过。
具体来说,get_delta_a 用于计算在两个价格(sqrt_price_0 和 sqrt_price_1)之间提供给定流动性所需的底层代币(Token A)的数量。在 get_delta_a 内部,调用 checked_shlw() 以确保 Token A 计算中使用的分子在移位时不会溢出。
然而,checked_shlw() 中的溢出检查是错误的。它使用了一个掩码 0xffffffffffffffff << 192(即 2^256 - 2^192),这远大于正确的阈值。因此,一个大于 **2^192** 但小于此掩码的输入值可以通过检查,即使将其左移会导致超出 u256 范围。然后,移位结果会被截断,产生一个不正确(较小)的值。
下图比较了易受攻击的实现和已修复的版本。正确的边界应该是 1 << 192,而不是 0xffffffffffffffff << 192。攻击者利用了此有缺陷的检查,通过仅存入 1 wei 的 Token A 来铸造异常大量的 LP 代币。
攻击分析
尽管攻击者在多个池中使用了相同的技术,但底层流程是一致的。以此交易为例,攻击过程如下。
1)操纵池子价格
攻击者使用闪电贷迅速获得了 10,024,321.28 haSUI,然后卖出了 5,765,124.79 SUI,将池子价格从 18,956,530,795,606,879,104 压低到 18,425,720,184,762,886。这一价格变动使得攻击者能够开立一个 CLMM 头寸,该头寸仅需要极少量的其中一种代币,从而利用了集中流动性设计中的“单边/近乎单边代币”流动性行为。
2) 以“几乎免费”的存款添加流动性
接下来,攻击者选择了一个非常狭窄的 tick 范围(例如,300000–300200),并仔细调整了目标流动性。在 CLMM 系统中,代币 delta 的计算取决于范围边界处的平方根价格,而狭窄的范围会使某些中间值变得极其敏感。
通过调整这些参数,攻击者导致一个内部乘法产生一个 u256 中间值,该值在左移时本应溢出,但仍通过了有缺陷的 checked_shift 保护。结果,在不安全的移位引起的截断之后,协议将所需的 Token A 金额计算为1 单位,同时仍然铸造/记录了具有**巨额流动性(即 10,365,647,984,364,446,732,462,244,378,333,008)**的头寸。
3) 移除流动性以提取真实储备
由于链上头寸似乎持有膨胀的流动性,攻击者像该头寸已得到充分资助一样,移除了流动性并提取了资产。这是池子真实储备被耗尽的步骤。
4) 在多个池子中重复
在验证了漏洞利用的原始方法后,攻击者在多个池子中复制了相同的流程,迅速累积了总损失。
总结
根本原因是定点数学路径中的 u256 左移周围存在一个错误的溢出检查,这允许溢出的移位静默地截断高位,从而使得所需的存款近乎为零,但仍向 LP 头寸计入巨额流动性,从而实现了储备的提取。
经验教训
- 在定点算术的移位、缩放因子、舍入和边界条件方面要格外严格。
- 优先选择经过验证的安全数学原语(或形式化不变量),而不是临时的辅助函数;仔细验证常量/阈值。
- 为最大值、边界 tick 和对抗性参数组合添加边缘情况 + 基于属性的测试。
参考
关于 BlockSec
BlockSec 是一家全栈区块链安全和加密合规提供商。我们构建产品和服务,帮助客户在协议和平台的整个生命周期中执行代码审计(包括智能合约、区块链和钱包),实时拦截攻击,分析事件,追踪非法资金,并满足 AML/CFT 义务。
BlockSec 在著名的会议上发表了多篇区块链安全论文,报告了 DeFi 应用的多次零日攻击,阻止了多次黑客攻击并挽救了超过 2000 万美元,并保障了数十亿美元的加密货币。
-
🔗 BlockSec 审计服务 : 提交请求



