Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: high
Valid

Any users who has not staked anything can steal rewards from the gauges simply by calling getReward

Summary

Any users who has not staked anything can steal rewards from the gauges simply by calling getReward

Vulnerability Details

The RAAC gauge contracts are governance contracts used to incentivize users to participate in governance activities. The only requirement is that users should stake their tokens in the gauges. This can be done via the BaseGauge::stake function:

/**
* @notice Stakes tokens in the gauge
* @param amount Amount to stake
*/
function stake(uint256 amount) external nonReentrant updateReward(msg.sender) {
if (amount == 0) revert InvalidAmount();
_totalSupply += amount;
_balances[msg.sender] += amount;
stakingToken.safeTransferFrom(msg.sender, address(this), amount);
emit Staked(msg.sender, amount);
}

Users who stake in gauges that perfom well receive rewards from the protocol. This is achieved via the getReward function:

/**
* @notice Claims accumulated rewards
* @dev Transfers earned rewards to caller
*/
function getReward() external virtual nonReentrant whenNotPaused updateReward(msg.sender) {
if (block.timestamp - lastClaimTime[msg.sender] < MIN_CLAIM_INTERVAL) {
revert ClaimTooFrequent();
}
lastClaimTime[msg.sender] = block.timestamp;//even if zero reward ???
UserState storage state = userStates[msg.sender];
uint256 reward = state.rewards;
if (reward > 0) {
state.rewards = 0;
uint256 balance = rewardToken.balanceOf(address(this));
if (reward > balance) {
revert InsufficientBalance();
}
rewardToken.safeTransfer(msg.sender, reward);
emit RewardPaid(msg.sender, reward);
}
}

The problem is that anybody can get rewards from the gauges even when they have staked none, making if no use the staking mechanism.

For a POC:

  1. Add a new user3 to the list of signers at https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/test/unit/core/governance/gauges/BaseGauge.test.js#L19:

    [owner, user1, user2, user3] = await ethers.getSigners();
  2. Add this test to

it("should send earned rewards to non-stakers", async () => {
const earned = await baseGauge.earned(user3.address);
expect(earned).to.be.gte(0);
await baseGauge.connect(user3).getReward();
const reward = await rewardToken.balanceOf(user3.address);
expect(reward).to.be.gte(0);
});

Impact

Non-stakers can steal rewards from the gauges by staking nothing and calling getReward

Tools Used

Manual review

Recommendations

Consider incorporating users staked amount when calculating earned rewards

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

BaseGauge::earned calculates rewards using getUserWeight instead of staked balances, potentially allowing users to claim rewards by gaining weight without proper reward checkpoint updates

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

BaseGauge::earned calculates rewards using getUserWeight instead of staked balances, potentially allowing users to claim rewards by gaining weight without proper reward checkpoint updates

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!