背景介紹
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 來⽀token00 來。

透過上述操作,攻擊者獲得了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 ,變為了18,425,720,184,762,886 ,降低到了0.0000009977 ,價格⼤幅降低⾄原來的0.00009% 。
隨後,攻擊者透過open_position 創建了⼀個Liquidity Position ,其中該Position 的tickLower 為300000 , tickUpper 為300200 。


tick 區間對應的price 的區間為:

隨後,攻擊者在該價格區間內添加流動性10,365,647,984,364,446,732,462,244,378,333,008 。

我們看⼀下添加流動性的程式碼:

可以看出,加入流動性所需的amount_a 和amount_b 由get_amount_by_liquidity 得出,接下來,我們看⼀下get_amount_by_liquidity 的具體實作和對應的參數。

由於arg2 為currentTick ,且currentTick = -138185 ⼩於arg0 lowerTick 。所以,接下來程式碼會走到

由於arg0 為lowerTick ,所以cetus::tick_math::get_sqrt_price_at_tick(arg0) = 60257519765924248467716150 ,且arg1 為upperTick ,cetus::tick_math::rice_sq. 60,863,087,478,126,617,965,993,239 。
函數get_delta_a 的具體實作如下:

Cetus 的核⼼問題出在checked_shlw 函數上,我們來看函數的具體程式碼:

此函數的邏輯很簡單,將⼀個數左移動⼀個word ,就是64位元。如果發⽣溢出,就回傳0,沒發⽣溢出就回傳左移後的數。所以,偵測左移後是否溢出需要判斷input > 2 ^ (256 - 64) - 1 ,然⽽,代碼中卻判斷input > 0xffffffffffffffff <<192 ,由於(0xffffffffffffffff << 192) > 2 ^ (256 - 64ffffffffffffff <(0xffffffffffffffff << 192) 的數字被截斷返回,且溢出檢測失效。此時,攻擊者建構的資料為:

所以, 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 。

隨後,攻擊者透過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

第⼀次修復沒有完全修復該漏洞, mask = 1 << 192 = 2 ^ 256 ,所以只有n >= mask 或n > mask - 1 ,才能完全修復,避免左移64位元後發⽣溢出截斷:
https://github.com/CetusProtocol/integer-mate/pull/7/files

總結
本次漏洞的成因是函數在針對左移溢位偵測時, mask 的值選擇錯誤,導致溢位偵測失敗,對應的值被截斷。最終,導致攻擊者以不符合代碼邏輯的極⼩的token 添加了極⼤的流動性。建議項⽬⽅在設計經濟模型及程式碼運⾏邏輯時要多⽅驗證,合約上線前審計時盡量選擇多個審計公司交叉審計。
