過去1週間(2026/07/20 - 2026/07/26)において、以下の8件の注目すべきセキュリティインシデントが発生し、合計損失額は約$39.5Mに上ります。
| 日付 | インシデント | 種別 | 推定損失 |
|---|---|---|---|
| 2026/07/19* | Allbridge | 入力バリデーションの欠陥 | ~$1.65M |
| 2026/07/20 | Zilliqa | ナンス生成の欠陥 | ~$400K |
| 2026/07/20 | Wanchain | メッセージエンコーディングの欠陥 | ~$500K |
| 2026/07/22 | 42DAO | 秘密鍵の漏洩 | ~$900K |
| 2026/07/22 | AFX Trade | 秘密鍵の漏洩 | ~$24.15M |
| 2026/07/22 | B² Network | 秘密鍵の漏洩 | ~$3.8M |
| 2026/07/23 | Verus | 秘密鍵の漏洩 | ~$7.6M |
| 2026/07/24 | Lien Finance | バリデーションロジックの欠陥 | ~$542K |
*Allbridgeのインシデントは7月19日(UTC 17:51)に発生しており、先週のレポートでは取り上げられませんでした。完全性のためにここに含めています。
- Zilliqa が選ばれた理由は、2019年以来レガシーネイティブZILアカウントのユーザーを秘密鍵漏洩のリスクにさらしていたオフチェーンウォレット実装における、長期間検出されなかった脆弱性が明らかになったためです。
- Allbridge が選ばれた理由は、Solanaの位置ベースのアカウントモデルによって引き起こされるアカウントエイリアシングの脆弱性を示しているためです。ソースコードが入手できなかったため、脆弱性はデプロイされたプログラムバイナリから再構築する必要がありました。
- Wanchain が選ばれた理由は、暗号技術を破らずにブリッジが流出可能であることを示しているためです。署名は有効であり正しく検証されていましたが、非単射かつ区切り文字なしのメッセージエンコーディングにより、3,097.56トークンの認可がCardano上で203,001,692.164714トークンとして再解釈されました。
- Lien Finance が選ばれた理由は、照合されたアイテムの同一性と多重度を確認せずに残高が数量と一致するかどうかを検証する処理が、いかに迂回されうるかを示しているためです。
Web3における最高のセキュリティ監査会社
ローンチ前に設計、コード、ビジネスロジックを検証する
今週のハイライト:Allbridge Core
このインシデントがハイライトされている理由は、アカウントエイリアシングのパターンが、同じミュータブルアカウントを2つのインストラクションロールで受け入れるあらゆるSolanaプログラムに適用されるためです。根本的なメカニズム、つまり同一ステートに対する2つのミュータブルなビューにおいて2番目の書き込みが最初の書き込みを暗黙的に上書きするというのは、古典的なERC-20セルフトランスファーバグ(from == toのtransferFrom)と同じ構造です。プロジェクトのポストモーテム [1] はハイレベルな概要を提供していますが、コードレベルのメカニズムは詳述されていません。以下の分析はそのギャップを埋めるために、デプロイされたプログラムバイナリから再構築されたものです。
2026年7月19日(UTC 17:51)、Solana上のクロスチェーンブリッジプロトコルであるAllbridge Coreが、約$1.65M(USDC約$1.12MとUSDT約$539K)の被害を受けました。根本原因は、スワップ命令が送信ロールと受信ロールの両方で同じPoolアカウントを受け入れる際に、その2つが異なることを強制していなかったことです。同じアカウントが2回渡されると、一方のロールからの内部アカウンティング更新が他方によって暗黙的に上書きされ、その一方で実際のトークン転送はすでに決済されていました。5回のセルフスワップにより、Poolの記録されたステートが十分に歪められ、攻撃者は少量のUSDTインプットを約$2.24MのUSDCに変換することができました。
背景
Allbridge Coreは、vUSDと呼ばれる内部アカウンティング単位を通じてスワップをルーティングするクロスチェーンブリッジです。スワップは2つのアカウンティングレッグで構成されます:
ソーストークン -- swap_to_v_usd(send_pool) --> vUSD
vUSD -- swap_from_v_usd(receive_pool) --> デスティネーショントークン
vUSDはSPLトークンではありません。各Poolのオンチェーンデータ内のフィールドとしてのみ存在します。各Poolはtoken_balance、v_usd_balance、およびreservesを追跡します。送信レッグはユーザーから送信ブリッジボールトへソーストークンを転送し、送信Poolのトークン側のアカウンティングを増やし、vUSDの出力を計算します。受信レッグはそのvUSDを受信Poolに追加し、そのトークン側のアカウンティングを減らし、受信ブリッジボールトからユーザーへデスティネーショントークンを転送します。
Solanaプログラムは、呼び出し元から位置配列としてアカウントを受け取ります。プログラムの命令ハンドラーはどの配列位置がどのロール(send_pool、receive_pool、send_mintなど)に対応するかを指定しますが、ランタイムは呼び出し元が2つの位置に同じアカウントアドレスを渡すことを防ぎません。プログラムが2つの異なる位置でアカウントをデシリアライズすると、各デシリアライズは別個のインメモリオブジェクトを生成します。一方のオブジェクトへのミューテーションは他方に影響しません。同じアカウントが2回渡された場合、両方のオブジェクトは同じオンチェーンデータから始まりますが、どちらかがミュートされるとすぐに乖離します。
ハンドラーが終了すると、プログラムの終了ロジック(例:AnchorのAccountsExit)は固定された順序で各アカウントオブジェクトをアカウントのデータにシリアライズし直します。2つのオブジェクトが同じアカウントにマップされている場合、2番目のシリアライゼーションが最初のものを完全に上書きします。
脆弱性分析
影響を受けたAllbridge Coreデプロイメントの正確なソースコードは入手できませんでした。分析はProgramDataに格納された1,770,736バイトのELF(SHA-256: 40f776...346bb6)から再構築されました。最後のデプロイメントスロット(204,727,029)は攻撃スロット(433,941,722)より前です。
バグのあるプログラムはBrdgN2...ceWBです。攻撃トランザクションのSwap命令(命令3から7)において、4つの送信および受信アカウントペアは同一でした:
| ロール | アカウント |
|---|---|
send_mint / receive_mint |
Es9vMF...wNYB |
send_pool / receive_pool |
DW4a2E...wCX |
send_bridge_token / receive_bridge_token |
2xY9TD...vohV |
send_user_token / receive_user_token |
817UdW...CVct |
パーサーには、各ロールをその期待されるミント、ボールト、オーナー、PDA、オーソリティ、またはTokenプログラムにバインドする16の復元された32バイト比較が含まれています。しかし、send_pool.key() != receive_pool.key()を強制する比較はありません。2つのPoolロールはハンドラーが実行される前に独立してデシリアライズされます。
デプロイされたELFは、2つのPool構築、送受信の計算、および固定された順序での2つのPool終了を示しています:
L54249-L54252 function_9881(accounts[5]) -> send_pool ローカル
L54294-L54297 function_9881(accounts[6]) -> receive_pool ローカル
L74177-L74195 function_12623(send_pool) -> swap_to_v_usd
L74368-L74389 function_12940(receive_pool) -> swap_from_v_usd
L55932-L55936 function_8300(send_pool) -> 完全なPoolシリアライゼーション
L55940-L55944 function_8300(receive_pool) -> 完全なPoolシリアライゼーション
各function_9881呼び出しは別個の176バイトのローカルPoolオブジェクトを割り当て、デコードされたフィールドをそこにコピーします。アカウントキーが等しい場合、両方のオブジェクトは同じデータから始まりますが、送信オブジェクトのミューテーションは受信オブジェクトを変更しません。
再構築された疑似Rust(デプロイされた動作であり、復元されたソースではない) [2] [3]:
let mut send_pool = Account::<Pool>::try_from(&accounts[5])?;
let mut receive_pool = Account::<Pool>::try_from(&accounts[6])?;
require_keys_eq!(send_pool.mint, send_mint.key());
require_keys_eq!(receive_pool.mint, receive_mint.key());
// ボールト、オーナー、PDA、オーソリティ、Tokenプログラムのバインディングはチェックされます。
// 欠如: require_keys_neq!(send_pool.key(), receive_pool.key());
token::transfer(send_user_token, send_bridge_token, amount)?;
let v_usd = swap_to_v_usd(&mut send_pool, amount)?;
let receive_amount =
swap_from_v_usd(&mut receive_pool, v_usd, receive_amount_min)?;
token::transfer(receive_bridge_token, receive_user_token, receive_amount)?;
// ハンドラーが返った後のAccountsExit:
send_pool.exit(program_id)?; // 最初の書き込み
receive_pool.exit(program_id)?; // 2番目の書き込み、最初を上書き
function_8300はライターを位置0から開始し、定義されたPool全131バイトをシリアライズします。最初の終了がsend_poolを書き込み、2番目の終了がreceive_poolを同じバイトに上書きします。最終的なアカウントステートは、2つのローカル更新のマージではなく、古い受信側の値になります。
SPLトークン転送は別個のCPIです。それらはPoolの終了前にSPL Tokenプログラムが所有するボールトアカウントを更新するため、2番目のPoolシリアライゼーションは入力転送を逆にすることができません。したがって、各エイリアスされたスワップは転送されたトークンをボールトに残しながら、対応する送信側のPool更新を破棄します。受信側の更新は残り、記録されたPoolステートをv_usd_balanceが高く、トークン側のアカウンティングが低い方向に移動させます。
根本的な欠陥は、プログラムがsend_poolとreceive_poolが異なるアカウントであることをチェックしないことです。他のすべてのバリデーション(ミントバインディング、ボールトオーナーシップ、PDA導出)は、両方のロールが正当に同じPoolに属しているため、通過します。
攻撃分析
以下の分析はトランザクション3LNLaG...Y39Qに基づいています。
エクスプロイトは、Kaminoフラッシュ借り入れ、7つのAllbridge Swap命令、Kaminoの返済を含む1つのトランザクションで完了しました。
- ステップ1:攻撃者はKaminoから約1.12Mの
USDCを借り入れ、Allbridgeを通じて約949KのUSDTにスワップしました(命令1-2)。この最初のスワップは、後続のセルフスワップに必要なUSDTを供給し、USDCとUSDTの両方のPoolステートをシフトさせました。

- ステップ2:攻撃者は、送信と受信の両方に同一のミント、Pool、ボールト、ユーザートークンアカウントを使用して、5回の約100Kの
USDTセルフスワップを送信しました(命令3-7)。受信Poolが毎回最後にシリアライズされたため、持続されたアカウンティングは受信側の減少を記録しましたが、送信側の追加は記録しませんでした。観察された出力は、歪みが蓄積されるにつれて各呼び出しで減少しました:
| セルフスワップ | インプット | ログ記録されたvUSD |
価格(vUSD/USDT) | USDTアウトプット |
|---|---|---|---|---|
| 1 | ~100K | ~162K | ~5.24 | ~47.8K |
| 2 | ~100K | ~256K | ~16.6 | ~25.4K |
| 3 | ~100K | ~471K | ~57.2 | ~12.5K |
| 4 | ~100K | ~918K | ~190 | ~5.73K |
| 5 | ~100K | ~1.82M | ~563 | ~2.36K |
5回の呼び出しにより、約500KのUSDTがボールトに転送され、約93.8KのUSDTが返却されました。Poolの記録されたtoken_balanceは各呼び出しで低下しましたが、実際のボールト残高は増加し、次のステップが悪用するギャップが拡大しました。

- ステップ3:攻撃者は約3.99Kの
USDTのみを供給しました(命令8)。歪んだUSDTPoolは約2.24MのvUSDを生成し、USDCPoolはそれを約2.24MのUSDCに変換しました。膨張したvUSDアウトプットが可能だったのは、5ラウンドの破棄された送信側更新の後、Poolの記録されたトークン残高が実際のボールト残高をはるかに下回っていたためです。

- ステップ4:攻撃者は約1.12Mの
USDCKaminoフラッシュローン元本と約11.2のUSDC手数料を返済しました(命令9)。攻撃者の最終残高は約1.12MのUSDCと約539KのUSDTでした。
結論
このインシデントの根本原因は、send_poolとreceive_poolが異なるアカウントを参照しているかどうかの検証が欠如していたことです。単一のミュータブルPoolが2つのローカルアカウンティングオブジェクトにデシリアライズされ、TokenプログラムがすでにボールトTransferを決済した後、2番目の完全なシリアライゼーションが最初のものを上書きしました。これにより、Poolの記録されたアカウンティングと実際のボールト残高が分離されました。アカウントエイリアス、ELF制御フロー、シリアライゼーション順序、トランザクションログ、ボールト残高はすべて同じメカニズムを支持しています [1]。
Solanaプログラムにとって、同じアカウントタイプを2つ以上のミュータブルロールで受け入れる命令は、一意性を強制するか(require_keys_neq!)、終了前にミューテーションをマージするべきです。Anchorフレームワークの#[account]制約システムはデフォルトでクロスロールの一意性を強制しないため、このチェックは明示的に追加する必要があります。一般的なパターン、つまり同一ステートに対する2つのミュータブルなビューで2番目の書き込みが最初のものを暗黙的に上書きするというパターンは、プログラムが独自のシリアライゼーション順序を管理する場所ならどこでも現れる可能性があります。
参考資料
- [1] Allbridge Core、技術的ポストモーテム:Solana Pool Swapエクスプロイト
- [2] Allbridge Core JS SDK、Solana Swapアカウントインターフェース(
bridge.json) - [3] Allbridge Core EVMコントラクト、Poolアカウンティングリファレンス(
Pool.sol)
今週のその他のインシデント
Zilliqa Ledgerウォレット
2026年7月20日、Zilliqaはレガシーネイティブ ZILアカウントの活発な悪用と一致するオンチェーン活動を観察し、既知の損失は約$400Kでした。根本原因は、Zilliqa LedgerアプリのEC-Schnorr署名パスにおけるバイアスがかかったナンスでした:ナンス生成コードは、剰余算による削減後に40バイトのバッファから誤った32バイトをコピーし、最上位64ビットをゼロに固定していました。同じアカウントからの複数の公開署名があれば、攻撃者は格子基底還元技術によって秘密鍵を復元し、アカウントを流出させることができました。
背景
Zilliqaのネイティブトランザクションはsecp256k1上のEC-Schnorr署名を使用します。各署名において、署名者は新鮮でフル幅の予測不可能なエフェメラルナンス (、secp256k1曲線の位数)をサンプリングする必要があります。署名フローはコミットメント 、チャレンジ 、およびレスポンス (は秘密鍵)を生成します。ブロードキャストされると、は公開されます。
線形関係 が重要なポイントです。正しい実装では、すべての が新鮮で一様分布しているため安全を保ちます。ナンスにバイアスがかかっていたり小さすぎたりすると、各公開署名が同じ秘密鍵に関する情報を漏洩します。
脆弱性分析
関連するナンス生成パスは、Zilliqa Ledgerアプリのコミット「Ledgerチームレビューに基づくバグ修正」で導入されました。意図されたフローは:40バイトのランダム性を生成し、曲線の位数 でモジュロ演算し、結果のスカラーを として使用するというものでした。広いランダム整数を生成して曲線の位数でモジュロ演算すること自体は問題ありません。欠陥はナンスバッファへのコピー中に現れました:
unsigned char nonce[size+8];
cx_rng(nonce, size+8);
cx_math_modm(nonce, size+8, (unsigned WIDE char *) PIC(domain->n), size);
os_memcpy(T->K, nonce, size);
cx_math_modmの後、32バイトのスカラー結果は40バイトのバッファ内に右詰めで配置されます。os_memcpy(T->K, nonce, size)は最初のsize(32)バイトをコピーし、先頭の8バイトのゼロパディングを保持し、エントロピーの最後の8バイトを破棄します。生成されたナンスは ではなく を満たします。
これは、すべてのナンスの64ビットがゼロに固定されることを意味します。Schnorrレスポンス方程式 から、各署名は結果が で有界な公開剰余線形関係を与えます。これは隠れ数問題(HNP)のインスタンスです。同じ鍵からの影響を受けた署名が約4つ以上あれば、標準的な格子基底還元アルゴリズムが商用ハードウェア上で数秒以内に秘密鍵を復元できます [1]。
この欠陥は、2019年からインシデントが2026年7月に特定されるまで、リリースされたすべてのバージョンのZilliqa Ledgerアプリに存在していました [2]。
このインシデントに対する攻撃分析は提供しません。悪用は完全にオフチェーンで行われました:攻撃者は格子基底還元を使用して公開されているオンチェーン署名から秘密鍵を復元し、標準的な転送トランザクションに署名しました。分析すべき複数ステップのオンチェーン攻撃シーケンスはありません。
結論
このインシデントは、オンチェーンスマートコントラクトのバグではなく、オフチェーン署名実装の欠陥によって引き起こされました。Zilliqa LedgerアプリはEC-Schnorr署名を に制約されたナンスで生成し、同じアカウントからのいくつかのネイティブトランザクション後に、秘密鍵の復元に十分な構造化された情報を漏洩しました。
主要な緩和策は、Ledgerアプリの更新だけでなく、鍵の廃棄です。修正されたアプリは新しい脆弱な署名を防ぎますが、すでにオンチェーンに記録されている公開署名を消去することはできません。十分な数の脆弱な署名を生成した影響を受けるアカウントはすべて、侵害されたものとして扱わなければなりません [2]。
ウォレットおよびプロトコルチームへ:ナンス生成とエンコーディングをセキュリティクリティカルなコードとして扱い、該当する場合は十分にレビューされた標準に従った決定論的ナンス生成を優先し、影響を受けるユーザーには、すでに同じ秘密鍵を保持している可能性のある攻撃者によるフロントランニングを考慮した、協調的な移行パスを提供してください。
参考資料
Wanchain Cardanoブリッジ
2026年7月20日、Wanchain CardanoブリッジはCardanoのTreasuryCheck Plutusバリデーターにおける非単射メッセージエンコーディングにより、約515.2MのNIGHT(~$500K)を失いました [1]。プロジェクトのポストモーテム [2] はハイレベルなメカニズムを説明していますが、バイトレベルのエンコーディングの詳細やコードは提供していません。以下の分析は完全な衝突を再構築したものです。ブリッジノードの署名は有効であり正しく検証されましたが、可変長フィールドの区切り文字なしの連結により、攻撃者は2つの隣接する数値フィールド間でバイト境界をシフトさせ、3,097.56 NIGHTの認可を203,001,692.164714 NIGHTの引き出しとして再解釈させることができました。
背景
影響を受けたコンポーネントはWanchainのCardanoクロスチェーントレジャリーシステムです。ソースチェーンのユーザーがトークンをバーンまたはロックし、ブリッジノードが解析されたリディーマーフィールドから構築された認可メッセージに署名し、署名済みプルーフがCardanoのTreasuryCheck Plutusバリデーターに提出されてトレジャリーからアセットをリリースします。
脆弱性分析
TreasuryCheckバリデーターは、14の解析されたリディーマーフィールドの生の連結に対するブリッジノードの署名を検証します:
hashRedeemer = sha3_256 $ mconcat
[ toPkhPay, toPkhStk, policy, assetName
, packInteger amount, packInteger adaAmount
, txHash, packInteger index, packInteger mode
, uniqueId, packInteger txType, packInteger ttl
, packInteger outputCount, userData
]
packIntegerが使用する整数エンコーディングは可変長であり、連結には長さプレフィックス、型セパレーター、またはドメイン分離された型付きシリアライゼーションがありません。したがって、異なるセマンティックタプルが同じ署名済みバイト文字列を生成することができます。
代表的な攻撃では、ブリッジノードは通常の引き出しに署名しました:
amount = 3097560000 -> packInteger = b8a103c0
adaAmount = 1206800 -> packInteger = 126a10
結合 = b8a103c0126a10
攻撃者は次のように解析されたCardanoリディーマーを提出しました:
amount = 203001692164714 -> packInteger = b8a103c0126a
adaAmount = 16 -> packInteger = 10
結合 = b8a103c0126a10
バイトシーケンスは同一です。署名チェックが通過し、Cardanoコントラクトはリディーマーを認可された額のおよそ65,000倍の引き出しとして解釈しました。
攻撃分析
以下の分析は、代表的なCardanoトランザクション0a4861...2ea1およびBSCソーストランザクション0xe90111...d5f26bを参照しています。
-
ステップ1:攻撃者は通常に見えるソースチェーンブリッジリクエストを作成しました。代表的なケースでは、BSCトランザクションで3,110の
NIGHTをバーンし、Wanchain APIはCardanoでの受信予定額として3,097.56のNIGHTを記録しました [3]。 -
ステップ2:ブリッジノードは生の連結フィールドから構築された認可ハッシュに署名しました。
-
ステップ3:攻撃者は署名済みバイトシーケンスを変更せずに、
amountとadaAmountの間のCardanoリディーマーの境界を変更しました。 -
ステップ4:Cardanoの
TreasuryCheckバリデーターはリディーマーを高額の引き出しとして解析し、再利用された署名の検証に成功しました。トランザクションは203,001,692.164714のNIGHTを攻撃者に支払いました。 -
ステップ5:同じパターンが複数のCardanoトランザクションにわたって繰り返され、合計で約515,206,545.426856の
NIGHT(~$500K)が流出しました。
結論
暗号技術は破られませんでした。署名者は有効な署名を生成し、TreasuryCheckバリデーターはそれらを正しく検証しました。欠陥はメッセージの構築にありました:hashRedeemerは14の可変長フィールドを区切り文字や長さプレフィックスなしで1つのバイト文字列に折りたたみ、エンコーディングを非単射にしました。異なるフィールドタプルが同じプレイメージ、同じハッシュ、そして同じ有効な署名を生成します。
修正策は、署名されたエンコーディングを単射にして、任意の与えられたバイト文字列を生成できるフィールドタプルが1つだけになるようにすることです。固定幅の整数エンコーディングまたは各フィールドへの長さプレフィックスの付与により、攻撃者が移動させた境界を固定します。標準的な構造化エンコーダーも同じ結果を達成し、誤りを犯しにくいです。一般的なルール:生の連結ではなく、正規化されたシリアライゼーションに署名してください。これは、Solidityのabi.encodePackedと可変長引数における同じクラスのバグです。異なる入力タプルが同一のバイトシーケンスを生成する可能性があります。これは可変長フィールドから組み立てられた署名済みメッセージに適用され、メッセージを構築・解析する当事者が異なるシステムで動作するブリッジに特に関連します。
参考資料
- [1] BlockSec PhalconのWanchainエクスプロイトに関する投稿
- [2] Wanchain Cardano-BNBチェーンブリッジインシデントポストモーテム
- [3] 代表的なBSCトランザクションのWanchain Status APIレコード
Lien Finance
2026年7月24日、Ethereum上の分散型ボンドOTCプロトコルであるLien Financeが、USDCで約$542Kの被害を受けました。根本原因はボンド交換関数のバリデーションの欠陥でした:入力グループと出力グループ間で共有ボンドの総数が一致しているかどうかをチェックしていましたが、どの特定のボンドが一致したかを追跡していませんでした。これにより攻撃者はすべての入力ボンドのバーンステップをバイパスしながら担保なしのボンドをミントし、それをプロトコルのOTCプールを通じて実際のUSDCで売却することができました。
背景
Lien FinanceはETHに対して担保付きボンドを発行するDeFiプロトコルです。そのBondMakerCollateralizedEthコントラクトはユーザーがETHを担保としてロックしてボンドトークンをミントすることを可能にします。ボンドのペイオフはfnMapによって定義されます。これは満期時の担保価格をそのボンドが支払う額にマッピングする区分線形関数です。
意味のある単位はボンドグループです。registerNewBondGroup()はグループ内のすべてのボンドのペイオフが、それらの結合したfnMapのすべてのブレークポイントでETH価格に合計されることを検証します。その条件を満たすグループは1単位の担保を正確に再構成します。これがグループが互換性を持つ理由です:その条件を満たす2つのグループは同じ価値があり、exchangeEquivalentBonds()変換パスはその保証に依存しています。
ボンドとグループの登録はどちらもパーミッションレスです:任意のアドレスが満期と任意のfnMapを提供することで新しいボンドを登録でき、任意のアドレスがペイオフ合計チェックのみを条件として、すでに登録されたボンドIDの任意のリストをグループとして登録できます。
脆弱性分析
バグのあるコントラクトは0xDA6F...BEf0と0x8432...7de0です。
ボンドが入力グループと出力グループの両方に存在する場合、それをバーンしてすぐに再ミントするのは無駄な作業です。exceptionBondsパラメーターは交換が両方のステップをスキップできるように、それらのボンドを指定します。関数はこれを単一カウンターexceptionCountで強制します:入力グループをスキャンする際に一致ごとに1回インクリメントし、出力グループをスキャンする際に一致ごとに1回デクリメントしますが、どのbondIDが一致したかは記録しません。

これは、入力グループ[BondA, BondB]と出力グループ[BondC, BondA, BondA]、exceptionBonds = [BondA, BondB]でバリデーションが通過することを意味します。カウンターは入力側で2に達し(BondAとBondBそれぞれが1つずつ一致)、出力側でゼロに戻りますが、両方のデクリメントはBondAの2つのエントリーから来ています。交換は何もバーンせず、BondCのみをミントします:入力を消費せずに作成された出力グループトークン。
攻撃分析
攻撃は2つのトランザクションに分かれています:ステップ1は0xe8689a...284d0fで発生し、ステップ2-5は0xb96d57...48e0e7で発生しました。
-
ステップ1:攻撃者は同じ満期を共有するBondA、BondB、BondCを定義するために、パーミッションレスな
registerNewBond()を3回呼び出しました。BondAとBondBは、_assertBondGroup()が要求するようにペイオフがETH価格に合計される$3,200以下でそれぞれ均等に担保を分割するレバレッジドコールです。BondCは$3,200で$1,870スポットに対してストライクされたディープ・アウト・オブ・ザ・マネーのLBTです:本質的価値はゼロですが、オプションとしての時間価値は約$7.64/IMTです。 -
ステップ2:攻撃者は2つのボンドグループを登録しました。
registerNewBondGroup([BondA, BondB])はグループ36(入力)を返し、registerNewBondGroup([BondC, BondA, BondA])はグループ37(出力)を返しました。BondCのフラットゼロペイオフはブレークポイント合計に何も追加しないため、その存在は等価条件を乱しません。BondAはグループ37に2回リストされており、これが交換のバリデーションを通過させるものです。 -
ステップ3:攻撃者は
exchangeEquivalentBonds(inputGroup = 36, outputGroup = 37, amount = 6968565865905, exceptionBonds = [BondA, BondB])を呼び出しました。入力グループのスキャン:BondAとBondBはそれぞれ例外に一致するため、両方がバーンをスキップしexceptionCountは2になります。出力グループのスキャン:BondCは何にも一致せずミントされ、次にBondAの2つのエントリーがそれぞれ例外に一致してカウンターを0にデクリメントします。 -
ステップ4:攻撃者はミントされたBondCをプロトコルのOTCプール(
GeneralizedDotc)を通じてUSDCで売却し、532,144のUSDCを取得しました。 -
ステップ5:攻撃者は2番目の
BondMakerCollateralizedEthコントラクトに対して同じパターンを繰り返し、合計利益は約542,144.63のUSDCになりました。

結論
共有ボンドの再ミントをスキップするためのメカニズムであるexceptionBondsは、出力グループでbondIDを複製することによって、すべての入力ボンドに一度に適用することができました。これにより、ボンドトークンがissueNewBonds()を通じた預けられた担保に対してのみ作成されるという不変条件が破られました。修正策は、どの特定のbondIDが一致したかを追跡し(例えばビットマップまたはセットを使用)、各例外が両方のグループで正確に1回消費されることを保証することです。
BlockSecについて
BlockSecはフルスタックのブロックチェーンセキュリティおよびクリプトコンプライアンスプロバイダーです。私たちは、プロトコルとプラットフォームのライフサイクル全体にわたって、顧客がコード監査(スマートコントラクト、ブロックチェーン、ウォレットを含む)を実施し、リアルタイムで攻撃を傍受し、インシデントを分析し、不正資金を追跡し、AML/CFT義務を果たすことを支援する製品とサービスを構築しています。
BlockSecは権威ある学術会議で複数のブロックチェーンセキュリティ論文を発表し、DeFiアプリケーションのゼロデイ攻撃を複数報告し、2,000万ドル以上を救済するために複数のハッキングをブロックし、数十億ドルの暗号通貨を保護してきました。
-
公式ウェブサイト:https://blocksec.com/
-
公式Twitterアカウント:https://twitter.com/BlockSecTeam



