零时科技 || Cetus 攻击事件分析

  • 事件概述:2025年5月22日,SUI链上DeFi协议Cetus遭遇攻击,损失达2.23亿美元。攻击者利用协议漏洞,通过操纵流动性池价格和函数溢出检测缺陷获利。

  • 攻击流程

    • 攻击者通过flash_swap将haSUI兑换为SUI,导致池中haSUI价格暴跌至原值的0.00009%。
    • 在异常价格区间(tick=-138185)创建流动性头寸,并注入巨额流动性(10^34量级)。
    • 利用checked_shlw函数的溢出检测漏洞(错误mask值),仅支付1个token即可添加超量流动性。
    • 移除流动性后偿还闪电贷,最终获利约576万SUI和1002万haSUI。
  • 漏洞根源

    • 关键函数checked_shlw的溢出检测逻辑错误,导致左移64位时未正确拦截溢出数据,攻击者借此绕过流动性添加的token校验。
  • 修复措施

    • Cetus团队通过两次PR修复漏洞,最终调整mask值为2^256以完全覆盖溢出风险。
  • 安全建议

    • 项目方需加强经济模型验证,合约审计应引入多机构交叉审查,避免单点失效风险。
总结

背景介绍

2025年5⽉22⽇,我们监控到 SUI 上针对Cetus的攻击事件:

https://suiscan.xyz/mainnet/tx/DVMG3B2kocLEnVMDuQzTYRgjwuuFSfciawPvXXheB3x

攻击共造成223M USD 的损失

攻击及事件分析

攻击者⾸先通过 flash_swap 来将 haSUI 兑换为 SUI , flash_swap 是 swap 和 flashloan 的变体,在 token0 对 token1 的 flash_swap 中,可以先获得 token1 ,再通过 repay_flash_swap 来⽀付 token0 。

零时科技 || Cetus 攻击事件分析

通过上述操作,攻击者获得了 5,765,124.79 SUI ,需要在同⼀个 transaction 中⽀付 10,024,321.29 haSUI 。且该池⼦的 haSUI 的 sqrtPriceX64 由 18,956,530,795,606,879,104 , tick = 545 ,变为了 18,425,720,184,762,886 , tick =-138185 ,相当于价格由原先的 1.056 降低到了 0.0000009977 ,价格⼤幅降低⾄原来的 0.00009% 。

随后,攻击者通过 open_position 创建了⼀个 Liquidity Position ,其中该 Position 的 tickLower 为 300000 , tickUpper 为 300200 。

零时科技 || Cetus 攻击事件分析

零时科技 || Cetus 攻击事件分析

tick 区间对应的 price 的区间为:

零时科技 || Cetus 攻击事件分析

随后,攻击者在该价格区间内添加流动性 10,365,647,984,364,446,732,462,244,378,333,008 。

零时科技 || Cetus 攻击事件分析

我们看⼀下添加流动性的代码:

零时科技 || Cetus 攻击事件分析

可以看出,添加流动性需要的 amount_a 和 amount_b 由 get_amount_by_liquidity 得出,接下来,我们看⼀下get_amount_by_liquidity 的具体实现和对应的参数。

零时科技 || Cetus 攻击事件分析

由于 arg2 为 currentTick ,且 currentTick = -138185 ⼩于 arg0 lowerTick 。所以,接下来代码会走到

零时科技 || Cetus 攻击事件分析

由于 arg0 为 lowerTick ,所以 cetus::tick_math::get_sqrt_price_at_tick(arg0) = 60257519765924248467716150 ,且 arg1 为 upperTick , cetus::tick_math::get_sqrt_price_at_tick(arg1) = 60,863,087,478,126,617,965,993,239 。

函数 get_delta_a 的具体实现如下:

零时科技 || Cetus 攻击事件分析

Cetus 的核⼼问题出在 checked_shlw 函数上,我们看该函数的具体代码:

零时科技 || Cetus 攻击事件分析

该函数的逻辑很简单,将⼀个数左移动⼀个 word ,就是64位。如果发⽣溢出,就返回0,如果没发⽣溢出就返回左移后的数。所以,检测左移后是否溢出需要判断 input > 2 ^ (256 - 64) - 1 ,然⽽,代码中却判断 input > 0xffffffffffffffff <<192 ,由于 (0xffffffffffffffff << 192) > 2 ^ (256 - 64) ,所以该代码会导致 input 在 2 ^ (256 - 64) < input <(0xffffffffffffffff << 192) 的数字被截断返回,且溢出检测失效。此时,攻击者构造的数据为:

零时科技 || Cetus 攻击事件分析

所以, input = 10365647984364446732462244378333008 * 605567712202369498277089 =6277101735386680763835789423207666416102355444464034512896 , input ⼤于 2^(256-64) ,但是 input ⼩于 0xffffffffffffffff << 192 。所以,左移时必然发⽣溢出,但是程序的溢出检测失败。发⽣溢出后, v1 = liquidity * (upperSqrtPriceX64 - lowerSqrtPriceX64) - 2 ^ (256 - 64) =491983144293873864340816 。

由于,该值远⼩于 lowerSqrtPriceX64 * upperSqrtPriceX64 ,最终 get_delta_a 的返回值为0。因此,攻击者需要⽀付的token_a 为1(p + 1),即可添加 10365647984364446732462244378333008 的 liquidity 。

零时科技 || Cetus 攻击事件分析

随后,攻击者通过 remove_liquidity 将添加的流动性移除,并通过 repay_flash_swap ⽀付 flash_swap 未⽀付的 token 后完成攻击,攻击者获利 5,765,124 SUI 和 10,024,321 haSUI 。最后,Cetus团队通过两个PR完成了对该漏洞的修复:

https://github.com/CetusProtocol/integer-mate/pull/6/files

零时科技 || Cetus 攻击事件分析

第⼀次修复没有完全修复该漏洞, mask = 1 << 192 = 2 ^ 256 ,所以只有 n >= mask 或 n > mask - 1 ,才能完全修复,避免左移64位后发⽣溢出截断:

https://github.com/CetusProtocol/integer-mate/pull/7/files

零时科技 || Cetus 攻击事件分析

总结

本次漏洞的成因是函数在针对左移溢出检测时, mask 的值选择错误,导致溢出检测失败,对应的值被截断。最终,导致攻击者以不符合代码逻辑的极⼩的 token 添加了极⼤的流动性。建议项⽬⽅在设计经济模型和代码运⾏逻辑时要多⽅验证,合约上线前审计时尽量选择多个审计公司交叉审计。

分享至:

作者:零时科技

本文为PANews入驻专栏作者的观点,不代表PANews立场,不承担法律责任。

文章及观点也不构成投资意见

图片来源:零时科技如有侵权,请联系作者删除。

关注PANews官方账号,一起穿越牛熊
推荐阅读
2025-12-07 06:24
2025-12-05 14:50
2025-12-05 14:15
2025-12-05 09:22
2025-12-03 13:00
2025-12-02 06:50

热门文章

行业要闻
市场热点
精选读物

精选专题

App内阅读