배경 소개
2025년 7월 28일, 이더리움의 RareStaking에 대한 공격을 감지했습니다: https://etherscan.io/tx/0xd813751bfb98a51912b8394b5856ae4515be6a9c6e5583e06b41d9255ba6e3c1.
이 공격으로 총 25.8k USD의 손실이 발생했습니다.
공격 및 사고 분석
먼저, 공격자는 공격 계약을 생성하고 이를 통해 RareStaking에 해당하는 스테이킹 토큰을 획득했습니다.

공격자는 updateMerkleRoot를 호출하여 머클 트리 루트를 업데이트했습니다.

updateMerkleRoot 코드를 살펴보겠습니다.
function updateMerkleRoot(bytes32 newRoot) external override {
require((msg.sender != owner() || msg.sender !=
address(0xc2F394a45e994bc81EfF678bDE9172e10f7c8ddc)), "머클 루트 업데이트 권한이 없음);
if (newRoot == bytes32(0)) revert EmptyMerkleRoot();
currentClaimRoot = newRoot;
currentRound++;
emit NewClaimRootAdded(newRoot, currentRound, block.timestamp);
일반적으로 머클 트리 루트를 업데이트하는 것은 민감한 작업으로, 관리자나 운영자가 수정해야 합니다. 그러나 프로그램 코드의 논리는 소유자가 아니고 프로젝트 주소를 가지고 있지 않은 사용자만 수정할 수 있다고 명시하고 있습니다. 개발자는 require 함수를 오해한 것이 분명합니다. 공격자는 머클 트리 루트를 업데이트한 후 클레임을 시작하고 프로젝트의 모든 토큰을 추출했습니다.

공격자가 머클루트를 수정했으므로 해당 주장을 검증할 수 있습니다. 공격 이후, 해당 프로젝트는 트랜잭션
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++;
emit NewClaimRootAdded(newRoot, currentRound, block.timestamp);
}
보시다시피, 수정된 로직은 호출자의 권한을 올바르게 확인할 수 있습니다.
요약
이 취약점은 updateMerkleRoot 함수가 호출자의 권한을 검증할 때 require를 잘못 해석하여 잘못된 권한 검증을 초래하여 발생했습니다. 결국 공격자는 권한 검증을 통해 MerkleTreeRoot를 수정한 후 모든 프로젝트 자금을 인출했습니다. 프로젝트에서는 경제 모델과 코드 실행 로직을 설계할 때 여러 검증 방법을 활용하고, 계약 체결 전에 여러 감사 기관의 교차 감사를 활용할 것을 권장합니다.
