Core Contracts

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

Bypass of Pause Functionality in `BaseGauge` Contract

Summary

The BaseGauge contract implements the Pausable interface; however, its key functions (stake and withdraw) lack proper pause restrictions. As a result, these functions (and their associated updateReward logic) can be executed even when the contract is paused, potentially leading to unintended reward updates and fund mismanagement.

Vulnerability Details

In the BaseGauge.sol contract, the stake and withdraw functions are decorated with the updateReward modifier, which updates the user's reward state. Despite the contract inheriting from Pausable, neither function is guarded by a whenNotPaused modifier (or equivalent check). This oversight means that even if the contract is paused—presumably to mitigate an emergency or exploit—users can still call these functions.

Notably, the documentation explicitly states that "Emergency pause stops all operations", yet these critical functions remain active during a pause. This discrepancy can be exploited to manipulate reward calculations or withdraw funds during an emergency.

Codes:

contracts\core\governance\gauges\BaseGauge.sol

abstract contract BaseGauge is IGauge, ReentrancyGuard, AccessControl, Pausable {
modifier updateReward(address account) {
_updateReward(account);
_;
}
function _updateReward(address account) internal {
rewardPerTokenStored = getRewardPerToken();
lastUpdateTime = lastTimeRewardApplicable();
if (account != address(0)) {
UserState storage state = userStates[account];
state.rewards = earned(account);
state.rewardPerTokenPaid = rewardPerTokenStored;
state.lastUpdateTime = block.timestamp;
emit RewardUpdated(account, state.rewards);
}
}
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);
}
function withdraw(uint256 amount) external nonReentrant updateReward(msg.sender) {
if (amount == 0) revert InvalidAmount();
if (_balances[msg.sender] < amount) revert InsufficientBalance();
_totalSupply -= amount;
_balances[msg.sender] -= amount;
stakingToken.safeTransfer(msg.sender, amount);
emit Withdrawn(msg.sender, amount);
}

Impact

Exploiting this vulnerability could result in:

  • Unintended Reward Updates: Users' reward balances may be incorrectly updated during a pause, affecting the fairness and integrity of the reward distribution.

  • Fund Loss or Theft: Malicious actors might leverage the ability to call these functions during a pause to manipulate state changes, potentially leading to the misallocation or unauthorized withdrawal of funds.

  • Systemic Exploitation: In an emergency scenario where the contract is paused to prevent further damage, the continued execution of these functions undermines the pause mechanism and could expose the system to additional attack vectors.

Tools Used

Manual

Recommendations

  • Implement Pause Checks: Add the whenNotPaused modifier to both stake and withdraw functions to ensure they cannot be executed while the contract is paused.

  • Review State Update Logic: Reevaluate the reward update mechanism to ensure it behaves securely under all contract states, especially during a pause.

  • Align with Documentation: Ensure that the contract's behavior fully adheres to the documented promise that "Emergency pause stops all operations."

Updates

Lead Judging Commences

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

BaseGauge::withdraw, stake, and checkpoint functions lack whenNotPaused modifier, allowing critical state changes even during emergency pause

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

BaseGauge::withdraw, stake, and checkpoint functions lack whenNotPaused modifier, allowing critical state changes even during emergency pause

Support

FAQs

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

Give us feedback!