背景紹介
2025年7月28日、イーサリアム上のRareStakingへの攻撃を検出しました: https://etherscan.io/tx/0xd813751bfb98a51912b8394b5856ae4515be6a9c6e5583e06b41d9255ba6e3c1.
この攻撃により、合計25,800米ドルの損失が発生しました。
攻撃とインシデントの分析
まず、攻撃者は攻撃コントラクトを作成し、そこからRareStakingに対応するステーキングトークンを取得しました。

その後、攻撃者は updateMerkleRoot を呼び出して Merkle Tree のルートを更新しました。

updateMerkleRoot のコードを見てみましょう。
function updateMerkleRoot(bytes32 newRoot) external override {{
require((msg.sender != owner() || msg.sender !=
address(0xc2F394a45e994bc81EfF678bDE9172e10f7c8ddc)), "Merkle Root を更新する権限がありません);
if (newRoot == bytes32(0)) revert EmptyMerkleRoot();
currentClaimRoot = newRoot;
currentRound++;
emitting NewClaimRootAdded(newRoot, currentRound, block.timestamp);
一般的に、Merkle Tree Root の更新は機密性の高い操作であり、通常は管理者またはオペレーターによる変更が必要です。しかし、プログラムコードのロジックでは、所有者ではなくプロジェクトのアドレスを持たないユーザーのみが変更できるとされています。明らかに、開発者は require の機能を誤解していました。攻撃者は MerkleRoot を更新した後、クレームを発行し、プロジェクト内のすべてのトークンを抽出しました。

攻撃者によって MerkleRoot が改変されているため、この主張は検証可能です。攻撃後、プロジェクトはトランザクション
https://etherscan.io/tx/0x65f37e4b1ec995adadd3f264a77f67b6bcbb52f16f29cf3302c9bf90396aa67e
の実装コントラクトをアップグレードし、updateMerkleRoot コードを以下のように変更しました。function updateMerkleRoot(bytes32 newRoot) external override {{
if (
(msg.sender != owner() &&
msg.sender !=
address(0xc2F394a45e994bc81EfF678bDE9172e10f7c8ddc))
) revert NotAuthorized();
if (newRoot == bytes32(0)) revert EmptyMerkleRoot();
currentClaimRoot = newRoot;
currentRound++;
emitting NewClaimRootAdded(newRoot, currentRound, block.timestamp);
>
ご覧のとおり、修正したロジックにより、呼び出し元の権限を正しく検証できます。
概要
この脆弱性は、updateMerkleRoot 関数が呼び出し元の権限検証時に require を誤って解釈し、無効な権限検証を引き起こしたことが原因でした。最終的に、攻撃者は権限検証を通じて MerkleTreeRoot を改変し、プロジェクト資金をすべて引き出しました。プロジェクトでは、経済モデルとコード実行ロジックを設計する際に複数の検証手法を活用し、契約開始前に複数の監査法人による相互監査を受けることを推奨します。
