2025年5月22日、Sui上で最大の集中流動性DEXであるCetus Protocolは、複数のプールにわたる流動性を枯渇させる大規模なエクスプロイトに見舞われ、推定2億2300万ドルの損失が発生しました。
このエクスプロイトは、カスタムのオーバーフロー防止関数(checked_shlw())の欠陥に起因していました。不正確な定数と条件比較により、固定小数点u256演算で使用される左シフトを実行する前に、関数が安全でない状況を検出できませんでした。これにより、重要なデルタ計算(例えば、特定の流動性に必要なトークン入力量を計算するロジック)中に、最上位ビットがサイレントに切り捨てられました。攻撃者は、流動性サイズとティック/価格範囲の設定といったパラメータを慎重に選択することで、プロトコルに実質的に「1単位のトークン」として必要な入金量を計算させることができましたが、その一方でポジションには「莫大な量の流動性」が credit されました。このインフレしたポジションがオンチェーンに記録された後、攻撃者は流動性を解除し、実際の準備金を引き出し、プールを枯渇させました。
背景
Cetusは、流動性プロバイダー(LP)が選択した価格範囲(ティック間隔)内でのみ流動性を提供する、集中流動性マーケットメーカー(CLMM)設計を採用しています。LPは、すべての価格に均一に資金を預け入れるのではなく、下限と上限の間に資本を集中させます。これにより、資本効率は向上しますが、プロトコルは以下間の変換において、正確な固定小数点演算に大きく依存します。
- ポジションに credit される流動性量と、
- 預け入れられる(または引き出せる)実際のトークン量
大まかに言うと、ユーザーが流動性を追加すると、プロトコルは現在の価格と選択された範囲に基づいてトークンデルタ(必要な基盤トークン量)を計算します。ユーザーが流動性を解除すると、プロトコルは逆の計算を実行して、ポジションがいくらを請求できるかを決定します。
「預け入れなければならない量」の計算が非常に少なくなる一方で、ポジションに大量の流動性が credit された場合、攻撃者は後でその credit された流動性を解除し、プールから実際の準備金を不正に引き出すことができます。
脆弱性分析
根本原因は、固定小数点u256算術で使用される左シフトを安全に実行するために設計されたヘルパー関数のバグ(通常、2^64のスケール係数を適用するために<< 64)でした。Moveベースのシステムでは、オーバーフローチェックはすべての演算で一律に強制されるわけではなく、ビットシフトは開発者が手動のセーフガードを追加することが多い分野です。
Cetusは、u256値を64ビット左シフトした場合に256ビット境界を超えるかどうかを判断するために、オーバーフロー防止ヘルパー(checked_shlw())を実装しました。しかし、不正確な定数と条件比較により、本来拒否されるべき特定の大きな入力に対して、チェックがバイパスされる可能性がありました。
具体的には、get_delta_aは、2つの価格(sqrt_price_0とsqrt_price_1)間で特定の流動性を提供するのに必要な基盤トークン(トークンA)の量を計算するために使用されます。get_delta_aの内部では、トークンAの計算で使用される分子がシフト時にオーバーフローしないことを保証するために、checked_shlw()が呼び出されます。
しかし、checked_shlw()のオーバーフローチェックは間違っています。0xffffffffffffffff << 192(すなわち2^256 - 2^192)というマスクを使用しており、これは正しい閾値よりもはるかに大きいです。その結果、2^192より大きく、このマスクより小さい入力値は、左シフトするとu256範囲を超えるにもかかわらず、チェックを通過してしまう可能性があります。シフトされた結果は切り捨てられ、不正確な(より小さな)値が生成されます。
以下の図は、脆弱な実装とパッチ適用後の実装を比較しています。正しい境界は1 << 192であるべきですが、0xffffffffffffffff << 192になっています。攻撃者はこの欠陥のあるチェックを悪用して、トークンAをわずか1 weiしか預け入れていないにもかかわらず、異常に大量の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)「ほぼ無料」の預け入れで流動性を追加
次に、攻撃者は非常に狭いティック範囲(例:300000–300200)を選択し、目標流動性を慎重に調整しました。CLMMシステムでは、トークンデルタの計算は範囲境界における平方根価格に依存し、狭い範囲は特定の内部値が非常に敏感になる可能性があります。
これらのパラメータを調整することで、攻撃者は内部乗算によってu256の内部値が生成されるようにしました。この値は左シフト時にオーバーフローするはずでしたが、欠陥のあるchecked-shiftガードを通過しました。その結果、安全でないシフトによる切り捨ての後、プロトコルは必要なトークンAの量を実質的に1単位と計算しましたが、ポジションには**莫大な流動性(すなわち、10,365,647,984,364,446,732,462,244,378,333,008)**がミント/記録されました。
3)流動性を解除して実際の準備金を抽出
オンチェーンでインフレした流動性を持つように見えるポジションができた後、攻撃者は流動性を解除し、ポジションが適切に資金提供されていたかのように資産を引き出しました。これがプールの実際の準備金が枯渇したステップです。
4)複数のプールで繰り返す
エクスプロイトの基本構造を検証した後、攻撃者は同じワークフローを複数のプールで再現し、総損失を急速に積み増しました。
まとめ
根本原因は、固定小数点演算パスにおけるu256左シフト周りの不正確なオーバーフローチェックであり、オーバーフローしたシフトが上位ビットをサイレントに切り捨てることを可能にし、必要な預け入れ額がほぼゼロに見える一方でLPポジションには莫大な流動性を credit する結果となり、準備金の抽出を可能にしました。
教訓
- 固定小数点演算におけるシフト、スケールファクター、丸め、境界条件には、特に厳格に注意してください。
- アドホックなヘルパーよりも、実績のあるセーフマスプリミティブ(または形式化された不変条件)を優先し、定数/閾値を慎重に検証してください。
- 最大値、境界ティック、敵対的なパラメータの組み合わせに対して、エッジケース+プロパティベースのテストを追加してください。
参考文献
- Cetus Protocolの公式発表
- 当社の短い分析
BlockSecについて
BlockSecは、フルスタックのブロックチェーンセキュリティおよび暗号コンプライアンスプロバイダーです。私たちは、顧客がプロトコルとプラットフォームのライフサイクル全体にわたって、コード監査(スマートコントラクト、ブロックチェーン、ウォレットを含む)、リアルタイムでの攻撃傍受、インシデント分析、不正資金の追跡、AML/CFT義務の履行を支援する製品とサービスを構築しています。
BlockSecは、著名なカンファレンスで複数のブロックチェーンセキュリティ論文を発表し、DeFiアプリケーションのゼロデイ攻撃を複数報告し、多数のハッキングを阻止して2000万ドル以上を救済し、数十億ドルの暗号資産を確保してきました。
-
公式ウェブサイト:https://blocksec.com/
-
公式Twitterアカウント:https://twitter.com/BlockSecTeam



