Background Introduction
On July 28, 2025, we detected an attack on RareStaking on Ethereum: https://etherscan.io/tx/0xd813751bfb98a51912b8394b5856ae4515be6a9c6e5583e06b41d9255ba6e3c1.
The attack resulted in a total loss of 25.8k USD.
Attack and Incident Analysis
First, the attacker created an attack contract and obtained the staking tokens corresponding to RareStaking from it.

The attacker then updated the Merkle Tree Root by calling updateMerkleRoot.

Let's take a look at the updateMerkleRoot code:
function updateMerkleRoot(bytes32 newRoot) external override {
require((msg.sender != owner() || msg.sender !=
address(0xc2F394a45e994bc81EfF678bDE9172e10f7c8ddc)), "Not authorized to update merkle root);
if (newRoot == bytes32(0)) revert EmptyMerkleRoot();
currentClaimRoot = newRoot;
currentRound++;
emit NewClaimRootAdded(newRoot, currentRound, block.timestamp);
}
Generally speaking, updating the Merkle Tree Root is a sensitive operation, typically requiring administrators or operators to modify it. However, the logic in the program code states that only those who are not the owner and do not have the project's address can modify it. Clearly, the developer misunderstood the function of require. After the attacker updated the MerkleRoot, they initiated a claim and extracted all tokens in the project.

Since the MerkleRoot has been modified by the attacker, the claim can be verified. After the attack, the project upgraded the implementation contract in the transaction
https://etherscan.io/tx/0x65f37e4b1ec995adadd3f264a77f67b6bcbb52f16f29cf3302c9bf90396aa67e
, modifying the updateMerkleRoot code to the following:
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);
}
As you can see, the modified logic can correctly verify the caller's permissions.
Summary
This vulnerability was caused by the updateMerkleRoot function's misinterpretation of require when verifying the caller's permissions, resulting in invalid permission verification. Ultimately, the attacker modified the MerkleTreeRoot through permission verification and then withdrew all project funds. We recommend that projects utilize multiple verification methods when designing economic models and code execution logic, and that they utilize multiple audit firms for cross-audits before contract launch.
